diff --git a/.jshintrc b/.jshintrc index a8d17196ec..bc41ce0afe 100644 --- a/.jshintrc +++ b/.jshintrc @@ -49,6 +49,7 @@ "notEqual", "require", "requirejs", + "hasModule", "Blob", "File"], "node" : false, diff --git a/Gemfile b/Gemfile index cf663f22a9..1726d715cd 100644 --- a/Gemfile +++ b/Gemfile @@ -67,17 +67,19 @@ unless Bundler::Dependency::PLATFORM_MAP.include? :mri_21 end end -gem 'seed-fu', '~> 2.3.3' if rails_master? gem 'arel', git: 'https://github.com/rails/arel.git' gem 'rails', git: 'https://github.com/rails/rails.git' + gem 'rails-observers', git: 'https://github.com/SamSaffron/rails-observers.git' + gem 'seed-fu', git: 'https://github.com/SamSaffron/seed-fu.git', branch: 'discourse' else + gem 'seed-fu', '~> 2.3.3' gem 'rails' + gem 'rails-observers' end gem 'actionpack-action_caching' -gem 'rails-observers' # Rails 4.1.6+ will relax the mail gem version requirement to `~> 2.5, >= 2.5.4`. # However, mail gem 2.6.x currently does not work with discourse because of the @@ -102,9 +104,10 @@ end gem 'onebox' gem 'ember-rails' -gem 'ember-source', '1.6.0.beta.2' -gem 'handlebars-source', '1.3.0' +gem 'ember-source', '1.9.0.beta.4' +gem 'handlebars-source', '2.0.0' gem 'barber' +gem '6to5' gem 'message_bus' gem 'rails_multisite', path: 'vendor/gems/rails_multisite' @@ -114,15 +117,13 @@ gem 'eventmachine' gem 'fast_xs' gem 'fast_xor' -gem 'fastimage' -gem 'fog', '1.22.1', require: false + +# while we sort out https://github.com/sdsykes/fastimage/pull/46 +gem 'fastimage_discourse', require: 'fastimage' +gem 'fog', '1.26.0', require: false gem 'unf', require: false -# see: https://twitter.com/samsaffron/status/412360162297393152 -# Massive amount of changes made in branch we use, no PR upstreamed -# We need to get this sorted -# https://github.com/samsaffron/email_reply_parser -gem 'email_reply_parser-discourse', require: 'email_reply_parser' +gem 'email_reply_parser' # note: for image_optim to correctly work you need # sudo apt-get install -y advancecomp gifsicle jpegoptim libjpeg-progs optipng pngcrush @@ -144,8 +145,7 @@ gem 'omniauth-github-discourse', require: 'omniauth-github' gem 'omniauth-oauth2', require: false gem 'omniauth-google-oauth2' gem 'oj' -# while resolving https://groups.google.com/forum/#!topic/ruby-pg/5_ylGmog1S4 -gem 'pg', '0.15.1' +gem 'pg' gem 'pry-rails', require: false gem 'rake' @@ -225,7 +225,7 @@ gem 'lru_redux' gem 'htmlentities', require: false # IMPORTANT: mini profiler monkey patches, so it better be required last -# If you want to amend mini profiler to do the monkey patches in the railstie +# If you want to amend mini profiler to do the monkey patches in the railties # we are open to it. by deferring require to the initializer we can configure discourse installs without it gem 'flamegraph', require: false @@ -240,6 +240,9 @@ gem 'rbtrace', require: false, platform: :mri gem 'ruby-readability', require: false gem 'simple-rss', require: false + +# TODO mri_22 should be here, but bundler was real slow to pick it up +# not even in production bundler yet, monkey patching it in feels bad gem 'gctools', require: false, platform: :mri_21 gem 'stackprof', require: false, platform: :mri_21 gem 'memory_profiler', require: false, platform: :mri_21 diff --git a/Gemfile.lock b/Gemfile.lock index 0badd31502..d2d90ac99f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -6,6 +6,11 @@ PATH GEM remote: https://rubygems.org/ specs: + 6to5 (0.5.0) + 6to5-source (>= 1.14, < 4) + execjs (~> 2.0) + 6to5-source (3.3.7) + CFPropertyList (2.2.8) actionmailer (4.1.8) actionpack (= 4.1.8) actionview (= 4.1.8) @@ -36,27 +41,26 @@ GEM minitest (~> 5.1) thread_safe (~> 0.1) tzinfo (~> 1.1) - addressable (2.3.6) annotate (2.6.5) activerecord (>= 2.3.0) rake (>= 0.8.7) arel (5.0.1.20140414130214) - barber (0.4.2) + barber (0.5.0) ember-source execjs - handlebars-source - better_errors (2.0.0) + handlebars-source (>= 1.0.0.rc.4) + better_errors (2.1.1) coderay (>= 1.0.0) erubis (>= 2.6.6) rack (>= 0.9.0) binding_of_caller (0.7.2) debug_inspector (>= 0.0.1) builder (3.2.2) - celluloid (0.15.2) - timers (~> 1.1.0) + celluloid (0.16.0) + timers (~> 4.0.0) certified (1.0.0) coderay (1.1.0) - connection_pool (2.0.0) + connection_pool (2.1.1) crass (0.2.1) daemons (1.1.9) debug_inspector (0.0.2) @@ -65,7 +69,7 @@ GEM dotenv (0.11.1) dotenv-deployment (~> 0.0.2) dotenv-deployment (0.0.2) - email_reply_parser-discourse (0.6) + email_reply_parser (0.5.8) ember-data-source (0.14) ember-source ember-rails (0.14.1) @@ -77,12 +81,12 @@ GEM handlebars-source jquery-rails (>= 1.0.17) railties (>= 3.1) - ember-source (1.6.0.beta.2) - handlebars-source (~> 1.0) + ember-source (1.9.0.beta.4) + handlebars-source (~> 2.0) erubis (2.7.0) - eventmachine (1.0.3) - excon (0.39.6) - execjs (2.2.1) + eventmachine (1.0.7) + excon (0.44.2) + execjs (2.2.2) exifr (1.1.3) fabrication (2.9.8) fakeweb (1.3.0) @@ -96,30 +100,77 @@ GEM rake rake-compiler fast_xs (0.8.0) - fastimage (1.6.3) - addressable (~> 2.3, >= 2.3.5) - ffi (1.9.5) - flamegraph (0.0.8) + fastimage_discourse (1.6.6) + ffi (1.9.6) + fission (0.5.0) + CFPropertyList (~> 2.2) + flamegraph (0.1.0) fast_stack - fog (1.22.1) - fog-brightbox - fog-core (~> 1.22) + fog (1.26.0) + fog-atmos + fog-brightbox (~> 0.4) + fog-core (~> 1.27, >= 1.27.1) + fog-ecloud fog-json + fog-profitbricks + fog-radosgw (>= 0.0.2) + fog-sakuracloud (>= 0.0.4) + fog-softlayer + fog-storm_on_demand + fog-terremark + fog-vmfusion + fog-voxel + fog-xml (~> 0.1.1) ipaddress (~> 0.5) nokogiri (~> 1.5, >= 1.5.11) - fog-brightbox (0.5.1) + fog-atmos (0.1.0) + fog-core + fog-xml + fog-brightbox (0.7.1) fog-core (~> 1.22) fog-json - inflecto - fog-core (1.24.0) + inflecto (~> 0.0.2) + fog-core (1.27.2) builder excon (~> 0.38) formatador (~> 0.2) mime-types net-scp (~> 1.1) net-ssh (>= 2.1.3) + fog-ecloud (0.0.2) + fog-core + fog-xml fog-json (1.0.0) multi_json (~> 1.0) + fog-profitbricks (0.0.1) + fog-core + fog-xml + nokogiri + fog-radosgw (0.0.3) + fog-core (>= 1.21.0) + fog-json + fog-xml (>= 0.0.1) + fog-sakuracloud (0.1.1) + fog-core + fog-json + fog-softlayer (0.3.26) + fog-core + fog-json + fog-storm_on_demand (0.1.0) + fog-core + fog-json + fog-terremark (0.0.3) + fog-core + fog-xml + fog-vmfusion (0.0.1) + fission + fog-core + fog-voxel (0.0.2) + fog-core + fog-xml + fog-xml (0.1.1) + fog-core + nokogiri (~> 1.5, >= 1.5.11) foreman (0.75.0) dotenv (~> 0.11.1) thor (~> 0.19.1) @@ -129,12 +180,13 @@ GEM given_core (3.5.4) sorcerer (>= 0.3.7) guess_html_encoding (0.0.9) - handlebars-source (1.3.0) - hashie (3.3.1) + handlebars-source (2.0.0) + hashie (3.3.2) highline (1.6.21) hike (1.2.3) - hiredis (0.5.2) - htmlentities (4.3.2) + hiredis (0.6.0) + hitimes (1.2.2) + htmlentities (4.3.3) i18n (0.6.11) image_optim (0.9.1) exifr (~> 1.1.3) @@ -149,7 +201,7 @@ GEM jquery-rails (3.1.2) railties (>= 3.0, < 5.0) thor (>= 0.14, < 2.0) - json (1.8.1) + json (1.8.2) jwt (1.0.0) kgio (2.9.2) librarian (0.1.2) @@ -163,20 +215,20 @@ GEM mime-types (~> 1.16) treetop (~> 1.4.8) memory_profiler (0.0.4) - message_bus (1.0.5) + message_bus (1.0.6) eventmachine rack (>= 1.1.3) redis metaclass (0.0.4) method_source (0.8.2) mime-types (1.25.1) - mini_portile (0.6.1) - minitest (5.4.2) + mini_portile (0.6.2) + minitest (5.5.1) mocha (1.1.0) metaclass (~> 0.0.1) - mock_redis (0.13.2) + mock_redis (0.14.0) moneta (0.8.0) - msgpack (0.5.8) + msgpack (0.5.11) multi_json (1.10.1) multi_xml (0.5.5) multipart-post (2.0.0) @@ -185,7 +237,7 @@ GEM net-ssh (>= 2.6.5) net-ssh (2.9.1) netrc (0.7.7) - nokogiri (1.6.4.1) + nokogiri (1.6.6.2) mini_portile (~> 0.6.0) nokogumbo (1.1.12) nokogiri @@ -196,7 +248,7 @@ GEM multi_json (~> 1.3) multi_xml (~> 0.5) rack (~> 1.2) - oj (2.10.2) + oj (2.11.4) omniauth (1.2.2) hashie (>= 1.2, < 4) rack (~> 1.0) @@ -222,7 +274,7 @@ GEM omniauth-twitter (1.0.1) multi_json (~> 1.3) omniauth-oauth (~> 1.0) - onebox (1.5.7) + onebox (1.5.12) moneta (~> 0.7) multi_json (~> 1.7) mustache (~> 0.99) @@ -230,7 +282,7 @@ GEM openid-redis-store (0.0.2) redis ruby-openid - pg (0.15.1) + pg (0.18.1) polyglot (0.3.5) progress (3.0.1) pry (0.10.1) @@ -239,9 +291,9 @@ GEM slop (~> 3.4) pry-nav (0.2.4) pry (>= 0.9.10, < 0.11.0) - pry-rails (0.3.2) + pry-rails (0.3.3) pry (>= 0.9.10) - puma (2.9.1) + puma (2.11.1) rack (>= 1.1, < 2.0) qunit-rails (0.0.7) railties @@ -273,18 +325,18 @@ GEM rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) raindrops (0.13.0) - rake (10.3.2) - rake-compiler (0.9.3) + rake (10.4.2) + rake-compiler (0.9.4) rake rb-fsevent (0.9.4) rb-inotify (0.9.5) ffi (>= 0.5.0) - rbtrace (0.4.5) + rbtrace (0.4.7) ffi (>= 1.0.6) msgpack (>= 0.4.3) trollop (>= 1.16.2) - redcarpet (3.1.2) - redis (3.1.0) + redcarpet (3.2.2) + redis (3.2.1) redis-namespace (1.5.1) redis (~> 3.0, >= 3.0.4) ref (1.0.5) @@ -339,9 +391,9 @@ GEM shoulda-context (1.2.1) shoulda-matchers (2.7.0) activesupport (>= 3.0.0) - sidekiq (3.2.5) - celluloid (= 0.15.2) - connection_pool (>= 2.0.0) + sidekiq (3.3.1) + celluloid (>= 0.16.0) + connection_pool (>= 2.1.1) json redis (>= 3.0.6) redis-namespace (>= 1.3.1) @@ -375,22 +427,23 @@ GEM therubyracer (0.12.1) libv8 (~> 3.16.14.0) ref - thin (1.6.2) - daemons (>= 1.0.9) - eventmachine (>= 1.0.0) - rack (>= 1.0.0) + thin (1.6.3) + daemons (~> 1.0, >= 1.0.9) + eventmachine (~> 1.0) + rack (~> 1.0) thor (0.19.1) thread_safe (0.3.4) tilt (1.4.1) timecop (0.7.1) - timers (1.1.0) + timers (4.0.1) + hitimes treetop (1.4.15) polyglot polyglot (>= 0.3.1) - trollop (2.0) + trollop (2.1.1) tzinfo (1.2.2) thread_safe (~> 0.1) - uglifier (2.5.3) + uglifier (2.6.0) execjs (>= 0.3.0) json (>= 1.8.0) unf (0.1.4) @@ -405,6 +458,7 @@ PLATFORMS ruby DEPENDENCIES + 6to5 actionpack-action_caching active_model_serializers (~> 0.8.0) annotate @@ -412,21 +466,21 @@ DEPENDENCIES better_errors binding_of_caller certified - email_reply_parser-discourse + email_reply_parser ember-rails - ember-source (= 1.6.0.beta.2) + ember-source (= 1.9.0.beta.4) eventmachine fabrication (= 2.9.8) fakeweb (~> 1.3.0) fast_blank fast_xor fast_xs - fastimage + fastimage_discourse flamegraph - fog (= 1.22.1) + fog (= 1.26.0) foreman gctools - handlebars-source (= 1.3.0) + handlebars-source (= 2.0.0) highline hiredis htmlentities @@ -454,7 +508,7 @@ DEPENDENCIES omniauth-twitter onebox openid-redis-store - pg (= 0.15.1) + pg pry-nav pry-rails puma diff --git a/Gemfile_master.lock b/Gemfile_master.lock index c638188101..c3f6d3a39b 100644 --- a/Gemfile_master.lock +++ b/Gemfile_master.lock @@ -7,66 +7,26 @@ GIT activemodel (>= 3.0) GIT - remote: https://github.com/rails/arel.git - revision: 590c784a30b13153667f8db7915998d7731e24e5 + remote: https://github.com/SamSaffron/rails-observers.git + revision: 7d2222d758603a004f6599f82a7068ffeb2d7ebf specs: - arel (6.0.0.beta2) + rails-observers (0.1.2) + activemodel (> 4.0) GIT - remote: https://github.com/rails/rails.git - revision: eb26f24bde62cbbcd8ef0e7ee9c64060b098baff + remote: https://github.com/SamSaffron/seed-fu.git + revision: d93df3b6364ea938d87c5629bf950b0d1ffe037e + branch: discourse specs: - actionmailer (4.2.0.beta4) - actionpack (= 4.2.0.beta4) - actionview (= 4.2.0.beta4) - activejob (= 4.2.0.beta4) - mail (~> 2.5, >= 2.5.4) - rails-dom-testing (~> 1.0, >= 1.0.4) - actionpack (4.2.0.beta4) - actionview (= 4.2.0.beta4) - activesupport (= 4.2.0.beta4) - rack (~> 1.6.0.beta) - rack-test (~> 0.6.2) - rails-dom-testing (~> 1.0, >= 1.0.4) - rails-html-sanitizer (~> 1.0, >= 1.0.1) - actionview (4.2.0.beta4) - activesupport (= 4.2.0.beta4) - builder (~> 3.1) - erubis (~> 2.7.0) - rails-dom-testing (~> 1.0, >= 1.0.4) - rails-html-sanitizer (~> 1.0, >= 1.0.1) - activejob (4.2.0.beta4) - activesupport (= 4.2.0.beta4) - globalid (>= 0.3.0) - activemodel (4.2.0.beta4) - activesupport (= 4.2.0.beta4) - builder (~> 3.1) - activerecord (4.2.0.beta4) - activemodel (= 4.2.0.beta4) - activesupport (= 4.2.0.beta4) - arel (>= 6.0.0.beta2, < 6.1) - activesupport (4.2.0.beta4) - i18n (>= 0.7.0.beta1, < 0.8) - json (~> 1.7, >= 1.7.7) - minitest (~> 5.1) - thread_safe (~> 0.1) - tzinfo (~> 1.1) - rails (4.2.0.beta4) - actionmailer (= 4.2.0.beta4) - actionpack (= 4.2.0.beta4) - actionview (= 4.2.0.beta4) - activejob (= 4.2.0.beta4) - activemodel (= 4.2.0.beta4) - activerecord (= 4.2.0.beta4) - activesupport (= 4.2.0.beta4) - bundler (>= 1.3.0, < 2.0) - railties (= 4.2.0.beta4) - sprockets-rails (~> 3.0.0.beta1) - railties (4.2.0.beta4) - actionpack (= 4.2.0.beta4) - activesupport (= 4.2.0.beta4) - rake (>= 0.8.7) - thor (>= 0.18.1, < 2.0) + seed-fu (2.3.3) + activerecord (>= 3.1) + activesupport (>= 3.1) + +GIT + remote: https://github.com/rails/arel.git + revision: 98fc25991137ee09b6800578117f8c1c322680f2 + specs: + arel (6.0.0) GIT remote: https://github.com/rails/sass-rails.git @@ -78,6 +38,61 @@ GIT sprockets (~> 2.12) sprockets-rails (>= 2.0, < 4.0) +PATH + remote: ../rails + specs: + actionmailer (5.0.0.alpha) + actionpack (= 5.0.0.alpha) + actionview (= 5.0.0.alpha) + activejob (= 5.0.0.alpha) + mail (~> 2.5, >= 2.5.4) + rails-dom-testing (~> 1.0, >= 1.0.5) + actionpack (5.0.0.alpha) + actionview (= 5.0.0.alpha) + activesupport (= 5.0.0.alpha) + rack (~> 1.6.0.beta2) + rack-test (~> 0.6.2) + rails-dom-testing (~> 1.0, >= 1.0.5) + rails-html-sanitizer (~> 1.0, >= 1.0.1) + actionview (5.0.0.alpha) + activesupport (= 5.0.0.alpha) + builder (~> 3.1) + erubis (~> 2.7.0) + rails-dom-testing (~> 1.0, >= 1.0.5) + rails-html-sanitizer (~> 1.0, >= 1.0.1) + activejob (5.0.0.alpha) + activesupport (= 5.0.0.alpha) + globalid (>= 0.3.0) + activemodel (5.0.0.alpha) + activesupport (= 5.0.0.alpha) + builder (~> 3.1) + activerecord (5.0.0.alpha) + activemodel (= 5.0.0.alpha) + activesupport (= 5.0.0.alpha) + arel (~> 6.0) + activesupport (5.0.0.alpha) + i18n (>= 0.7.0.beta1, < 0.8) + json (~> 1.7, >= 1.7.7) + minitest (~> 5.1) + thread_safe (~> 0.1) + tzinfo (~> 1.1) + rails (5.0.0.alpha) + actionmailer (= 5.0.0.alpha) + actionpack (= 5.0.0.alpha) + actionview (= 5.0.0.alpha) + activejob (= 5.0.0.alpha) + activemodel (= 5.0.0.alpha) + activerecord (= 5.0.0.alpha) + activesupport (= 5.0.0.alpha) + bundler (>= 1.3.0, < 2.0) + railties (= 5.0.0.alpha) + sprockets-rails + railties (5.0.0.alpha) + actionpack (= 5.0.0.alpha) + activesupport (= 5.0.0.alpha) + rake (>= 0.8.7) + thor (>= 0.18.1, < 2.0) + PATH remote: vendor/gems/rails_multisite specs: @@ -150,7 +165,7 @@ GEM fastimage (1.6.3) addressable (~> 2.3, >= 2.3.5) ffi (1.9.5) - flamegraph (0.0.8) + flamegraph (0.0.9) fast_stack fog (1.22.1) fog-brightbox @@ -285,7 +300,7 @@ GEM openid-redis-store (0.0.2) redis ruby-openid - pg (0.15.1) + pg (0.18.0.pre20141117110243) polyglot (0.3.5) progress (3.0.1) pry (0.10.1) @@ -300,7 +315,7 @@ GEM rack (>= 1.1, < 2.0) qunit-rails (0.0.7) railties - rack (1.6.0.beta) + rack (1.6.0.beta2) rack-mini-profiler (0.9.2) rack (>= 1.1.3) rack-openid (1.3.1) @@ -312,16 +327,14 @@ GEM rack (>= 1.0) rails-deprecated_sanitizer (1.0.3) activesupport (>= 4.2.0.alpha) - rails-dom-testing (1.0.4) + rails-dom-testing (1.0.5) activesupport (>= 4.2.0.beta, < 5.0) nokogiri (~> 1.6.0) rails-deprecated_sanitizer (>= 1.0.1) rails-html-sanitizer (1.0.1) loofah (~> 2.0) - rails-observers (0.1.2) - activemodel (~> 4.0) raindrops (0.13.0) - rake (10.3.2) + rake (10.4.0) rake-compiler (0.9.3) rake rb-fsevent (0.9.4) @@ -373,9 +386,6 @@ GEM nokogiri (>= 1.4.4) nokogumbo (= 1.1.12) sass (3.2.19) - seed-fu (2.3.3) - activerecord (>= 3.1, < 4.2) - activesupport (>= 3.1, < 4.2) shoulda (3.5.0) shoulda-context (~> 1.0, >= 1.0.1) shoulda-matchers (>= 1.4.1, < 3.0) @@ -498,7 +508,7 @@ DEPENDENCIES omniauth-twitter onebox openid-redis-store - pg (= 0.15.1) + pg (= 0.18.0.pre20141117110243) pry-nav pry-rails puma @@ -506,7 +516,7 @@ DEPENDENCIES rack-mini-profiler rack-protection rails! - rails-observers + rails-observers! rails_multisite! rake rb-fsevent @@ -525,7 +535,7 @@ DEPENDENCIES sanitize sass sass-rails! - seed-fu (~> 2.3.3) + seed-fu! shoulda sidekiq simple-rss diff --git a/README.md b/README.md index ae82fb76fc..1afd54b02d 100644 --- a/README.md +++ b/README.md @@ -10,13 +10,14 @@ To learn more about the philosophy and goals of the project, [visit **discourse. ## Screenshots -[![](https://raw2.github.com/discourse/discourse-docimages/master/readme/boing-boing-latest-small2.png)](http://bbs.boingboing.net) -[![](https://raw2.github.com/discourse/discourse-docimages/master/readme/how-to-geek-profile-small2.png?breakcache=1)](http://discuss.howtogeek.com) -[![](https://raw2.github.com/discourse/discourse-docimages/master/readme/new-relic-categories-small2.png)](http://discuss.newrelic.com) -[![](https://raw2.github.com/discourse/discourse-docimages/master/readme/turtle-rock-topic-small2.jpg)](https://talk.turtlerockstudios.com/) -[![](https://raw.github.com/discourse/discourse-docimages/master/readme/nexus-7-mobile-discourse-small3.png)](http://discuss.atom.io) -[![](https://raw.github.com/discourse/discourse-docimages/master/readme/iphone-5s-mobile-discourse-small4.png)](http://discourse.soylent.me) +[![](https://raw.githubusercontent.com/discourse/discourse-docimages/master/readme/boing-boing-latest-small2.png)](http://bbs.boingboing.net) +[![](https://raw.githubusercontent.com/discourse/discourse-docimages/master/readme/how-to-geek-profile-small2.png)](http://discuss.howtogeek.com) +[![](https://raw.githubusercontent.com/discourse/discourse-docimages/master/readme/new-relic-categories-small2.png)](http://discuss.newrelic.com) +[![](https://raw.githubusercontent.com/discourse/discourse-docimages/master/readme/turtle-rock-topic-small2.jpg)](https://talk.turtlerockstudios.com/) +[![](https://raw.githubusercontent.com/discourse/discourse-docimages/master/readme/nexus-7-mobile-discourse-small3.png)](http://discuss.atom.io) +[![](https://raw.githubusercontent.com/discourse/discourse-docimages/master/readme/iphone-5s-mobile-discourse-small4.png)](http://discourse.soylent.me) +Browse [lots more notable Discourse instances](http://www.discourse.org/faq/customers/). ## Development @@ -56,8 +57,8 @@ Plus *lots* of Ruby Gems, a complete list of which is at [/master/Gemfile](https ## Contributing -[![Build Status](https://travis-ci.org/discourse/discourse.png)](https://travis-ci.org/discourse/discourse) -[![Code Climate](https://codeclimate.com/github/discourse/discourse.png)](https://codeclimate.com/github/discourse/discourse) +[![Build Status](https://travis-ci.org/discourse/discourse.svg)](https://travis-ci.org/discourse/discourse) +[![Code Climate](https://codeclimate.com/github/discourse/discourse.svg)](https://codeclimate.com/github/discourse/discourse) Discourse is **100% free** and **open-source**. We encourage and support an active, healthy community that accepts contributions from the public – including you! diff --git a/Vagrantfile b/Vagrantfile index acbd512684..54955eaa06 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -3,8 +3,8 @@ # See https://github.com/discourse/discourse/blob/master/docs/VAGRANT.md # Vagrant.configure("2") do |config| - config.vm.box = 'discourse-0.9.9.13' - config.vm.box_url = "https://d3fvb7b7auiut8.cloudfront.net/discourse-0.9.9.13.box" + config.vm.box= "discourse/discourse-0.9.9.15.box" + config.vm.box_url = "https://vagrantcloud.com/discourse/discourse-0.9.9.15.box" # Make this VM reachable on the host network as well, so that other # VM's running other browsers can access our dev server. diff --git a/app/assets/javascripts/admin.js.erb b/app/assets/javascripts/admin.js.erb index 3c536f4ca6..ce47046a21 100644 --- a/app/assets/javascripts/admin.js.erb +++ b/app/assets/javascripts/admin.js.erb @@ -1,12 +1,5 @@ <% -if Rails.env.development? - require_asset ("development/list-view.js") -else - require_asset ("production/list-view.js") -end - require_asset("main_include_admin.js") DiscoursePluginRegistry.admin_javascripts.each { |js| require_asset(js) } - %> diff --git a/app/assets/javascripts/admin/components/admin-group-selector.js.es6 b/app/assets/javascripts/admin/components/admin-group-selector.js.es6 index 4189beed3f..f838830b08 100644 --- a/app/assets/javascripts/admin/components/admin-group-selector.js.es6 +++ b/app/assets/javascripts/admin/components/admin-group-selector.js.es6 @@ -31,4 +31,4 @@ export default Ember.Component.extend({ _refreshOnReset: function() { this.$("input").select2("data", this.get("selected").map(this._format)); }.observes("selected") -}); \ No newline at end of file +}); diff --git a/app/assets/javascripts/admin/components/admin-nav-item.js.es6 b/app/assets/javascripts/admin/components/admin-nav-item.js.es6 new file mode 100644 index 0000000000..f5acc94a13 --- /dev/null +++ b/app/assets/javascripts/admin/components/admin-nav-item.js.es6 @@ -0,0 +1,18 @@ +export default Ember.Component.extend({ + tagName: 'li', + classNameBindings: ['active'], + + router: function() { + return this.container.lookup('router:main'); + }.property(), + + active: function() { + const route = this.get('route'); + if (!route) { return; } + + const routeParam = this.get('routeParam'), + router = this.get('router'); + + return routeParam ? router.isActive(route, routeParam) : router.isActive(route); + }.property('router.url', 'route') +}); diff --git a/app/assets/javascripts/admin/components/color-input.js.es6 b/app/assets/javascripts/admin/components/color-input.js.es6 new file mode 100644 index 0000000000..98d5f6e6bb --- /dev/null +++ b/app/assets/javascripts/admin/components/color-input.js.es6 @@ -0,0 +1,24 @@ +/** + An input field for a color. + + @param hexValue is a reference to the color's hex value. + @param brightnessValue is a number from 0 to 255 representing the brightness of the color. See ColorSchemeColor. + @params valid is a boolean indicating if the input field is a valid color. +**/ +export default Ember.Component.extend({ + hexValueChanged: function() { + var hex = this.get('hexValue'); + if (this.get('valid')) { + this.$('input').attr('style', 'color: ' + (this.get('brightnessValue') > 125 ? 'black' : 'white') + '; background-color: #' + hex + ';'); + } else { + this.$('input').attr('style', ''); + } + }.observes('hexValue', 'brightnessValue', 'valid'), + + _triggerHexChanged: function() { + var self = this; + Em.run.schedule('afterRender', function() { + self.hexValueChanged(); + }); + }.on('didInsertElement') +}); diff --git a/app/assets/javascripts/admin/components/color_input_component.js b/app/assets/javascripts/admin/components/color_input_component.js deleted file mode 100644 index 099d4ab1eb..0000000000 --- a/app/assets/javascripts/admin/components/color_input_component.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - An input field for a color. - - @param hexValue is a reference to the color's hex value. - @param brightnessValue is a number from 0 to 255 representing the brightness of the color. See ColorSchemeColor. - @params valid is a boolean indicating if the input field is a valid color. - - @class Discourse.ColorInputComponent - @extends Ember.Component - @namespace Discourse - @module Discourse - **/ -Discourse.ColorInputComponent = Ember.Component.extend({ - layoutName: 'components/color-input', - - hexValueChanged: function() { - var hex = this.get('hexValue'); - if (this.get('valid')) { - this.$('input').attr('style', 'color: ' + (this.get('brightnessValue') > 125 ? 'black' : 'white') + '; background-color: #' + hex + ';'); - } else { - this.$('input').attr('style', ''); - } - }.observes('hexValue', 'brightnessValue', 'valid'), - - didInsertElement: function() { - var self = this; - this._super(); - Em.run.schedule('afterRender', function() { - self.hexValueChanged(); - }); - } -}); diff --git a/app/assets/javascripts/admin/components/ip-lookup.js.es6 b/app/assets/javascripts/admin/components/ip-lookup.js.es6 index d3140eecf6..bf46c9a2da 100644 --- a/app/assets/javascripts/admin/components/ip-lookup.js.es6 +++ b/app/assets/javascripts/admin/components/ip-lookup.js.es6 @@ -9,6 +9,13 @@ export default Ember.Component.extend({ ].filter(Boolean).join(", "); }.property("location.{city,region,country}"), + otherAccountsToDelete: function() { + // can only delete up to 50 accounts at a time + var total = Math.min(50, this.get("totalOthersWithSameIP") || 0); + var visible = Math.min(50, this.get("other_accounts.length") || 0); + return Math.max(visible, total); + }.property("other_accounts", "totalOthersWithSameIP"), + actions: { lookup: function () { var self = this; @@ -24,10 +31,18 @@ export default Ember.Component.extend({ if (!this.get("other_accounts")) { this.set("otherAccountsLoading", true); - Discourse.AdminUser.findAll("active", { + + var data = { "ip": this.get("ip"), - "exclude": this.get("user_id") - }).then(function (users) { + "exclude": this.get("userId"), + "order": "trust_level DESC" + }; + + Discourse.ajax("/admin/users/total-others-with-same-ip.json", { data: data }).then(function (result) { + self.set("totalOthersWithSameIP", result.total); + }); + + Discourse.AdminUser.findAll("active", data).then(function (users) { self.setProperties({ other_accounts: users, otherAccountsLoading: false, @@ -38,6 +53,30 @@ export default Ember.Component.extend({ hide: function () { this.set("show", false); + }, + + deleteOtherAccounts: function() { + var self = this; + bootbox.confirm(I18n.t("ip_lookup.confirm_delete_other_accounts"), I18n.t("no_value"), I18n.t("yes_value"), function (confirmed) { + if (confirmed) { + self.setProperties({ + other_accounts: null, + otherAccountsLoading: true, + totalOthersWithSameIP: null + }); + + Discourse.ajax("/admin/users/delete-others-with-same-ip.json", { + type: "DELETE", + data: { + "ip": self.get("ip"), + "exclude": self.get("userId"), + "order": "trust_level DESC" + } + }).then(function() { + self.send("lookup"); + }); + } + }); } } }); diff --git a/app/assets/javascripts/admin/components/list-setting.js.es6 b/app/assets/javascripts/admin/components/list-setting.js.es6 new file mode 100644 index 0000000000..55ff799fb7 --- /dev/null +++ b/app/assets/javascripts/admin/components/list-setting.js.es6 @@ -0,0 +1,53 @@ +/** + Provide a nice GUI for a pipe-delimited list in the site settings. + + @param settingValue is a reference to SiteSetting.value. + @param choices is a reference to SiteSetting.choices +**/ +export default Ember.Component.extend({ + + _select2FormatSelection: function(selectedObject, jqueryWrapper, htmlEscaper) { + var text = selectedObject.text; + if (text.length <= 6) { + jqueryWrapper.closest('li.select2-search-choice').css({"border-bottom": '7px solid #'+text}); + } + return htmlEscaper(text); + }, + + _initializeSelect2: function(){ + var options = { + multiple: false, + separator: "|", + tokenSeparators: ["|"], + tags : this.get("choices") || [], + width: 'off', + dropdownCss: this.get("choices") ? {} : {display: 'none'} + }; + + var settingName = this.get('settingName'); + if (typeof settingName === 'string' && settingName.indexOf('colors') > -1) { + options.formatSelection = this._select2FormatSelection; + } + + var self = this; + this.$("input").select2(options).on("change", function(obj) { + self.set("settingValue", obj.val.join("|")); + self.refreshSortables(); + }); + + this.refreshSortables(); + }.on('didInsertElement'), + + refreshOnReset: function() { + this.$("input").select2("val", this.get("settingValue").split("|")); + }.observes("settingValue"), + + refreshSortables: function() { + var self = this; + this.$("ul.select2-choices").sortable().on('sortupdate', function() { + self.$("input").select2("onSortEnd"); + }); + } +}); + + diff --git a/app/assets/javascripts/admin/components/list_setting_component.js b/app/assets/javascripts/admin/components/list_setting_component.js deleted file mode 100644 index ecbe97c950..0000000000 --- a/app/assets/javascripts/admin/components/list_setting_component.js +++ /dev/null @@ -1,59 +0,0 @@ -/** - Provide a nice GUI for a pipe-delimited list in the site settings. - - @param settingValue is a reference to SiteSetting.value. - @param choices is a reference to SiteSetting.choices - - @class Discourse.ListSettingComponent - @extends Ember.Component - @namespace Discourse - @module Discourse - **/ - -Discourse.ListSettingComponent = Ember.Component.extend({ - tagName: 'div', - - - _select2FormatSelection: function(selectedObject, jqueryWrapper, htmlEscaper) { - var text = selectedObject.text; - if (text.length <= 6) { - jqueryWrapper.closest('li.select2-search-choice').css({"border-bottom": '7px solid #'+text}); - } - return htmlEscaper(text); - }, - - didInsertElement: function(){ - - var select2_options = { - multiple: false, - separator: "|", - tokenSeparators: ["|"], - tags : this.get("choices") || [], - width: 'off', - dropdownCss: this.get("choices") ? {} : {display: 'none'} - }; - - var settingName = this.get('settingName'); - if (typeof settingName === 'string' && settingName.indexOf('colors') > -1) { - select2_options.formatSelection = this._select2FormatSelection; - } - this.$("input").select2(select2_options).on("change", function(obj) { - this.set("settingValue", obj.val.join("|")); - this.refreshSortables(); - }.bind(this)); - - this.refreshSortables(); - }, - - refreshOnReset: function() { - this.$("input").select2("val", this.get("settingValue").split("|")); - }.observes("settingValue"), - - refreshSortables: function() { - this.$("ul.select2-choices").sortable().on('sortupdate', function() { - this.$("input").select2("onSortEnd"); - }.bind(this)); - } -}); - - diff --git a/app/assets/javascripts/admin/components/resumable_upload_component.js b/app/assets/javascripts/admin/components/resumable_upload_component.js index 3e3c1ad6fc..a5ef4af921 100644 --- a/app/assets/javascripts/admin/components/resumable_upload_component.js +++ b/app/assets/javascripts/admin/components/resumable_upload_component.js @@ -9,13 +9,8 @@ error="errorAction" uploadText="UPLOAD" }} - - @class ResumableUploadComponent - @extends Ember.Component - @namespace Discourse - @module Discourse **/ -Discourse.ResumableUploadComponent = Ember.Component.extend({ +Discourse.ResumableUploadComponent = Ember.Component.extend(Discourse.StringBuffer, { tagName: "button", classNames: ["btn", "ru"], classNameBindings: ["isUploading"], @@ -25,7 +20,7 @@ Discourse.ResumableUploadComponent = Ember.Component.extend({ isUploading: false, progress: 0, - shouldRerender: Discourse.View.renderIfChanged("isUploading", "progress"), + rerenderTriggers: ['isUploading', 'progress'], text: function() { if (this.get("isUploading")) { @@ -35,7 +30,7 @@ Discourse.ResumableUploadComponent = Ember.Component.extend({ } }.property("isUploading", "progress"), - render: function(buffer) { + renderString: function(buffer) { var icon = this.get("isUploading") ? "times" : "upload"; buffer.push(""); buffer.push("" + this.get("text") + ""); diff --git a/app/assets/javascripts/admin/controllers/admin-dashboard.js.es6 b/app/assets/javascripts/admin/controllers/admin-dashboard.js.es6 index 65e7f3f012..ad572337e0 100644 --- a/app/assets/javascripts/admin/controllers/admin-dashboard.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-dashboard.js.es6 @@ -53,6 +53,9 @@ export default Ember.Controller.extend({ actions: { refreshProblems: function() { this.loadProblems(); + }, + showTrafficReport: function() { + this.set("showTrafficReport", true); } } diff --git a/app/assets/javascripts/admin/controllers/admin-email-index.js.es6 b/app/assets/javascripts/admin/controllers/admin-email-index.js.es6 index 58eff83cd2..341ca4f652 100644 --- a/app/assets/javascripts/admin/controllers/admin-email-index.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-email-index.js.es6 @@ -36,8 +36,12 @@ export default DiscourseController.extend({ data: { email_address: this.get('testEmailAddress') } }).then(function () { self.set('sentTestEmail', true); - }).catch(function () { - bootbox.alert(I18n.t('admin.email.test_error')); + }, function(e) { + if (e.responseJSON && e.responseJSON.errors) { + bootbox.alert(I18n.t('admin.email.error', { server_error: e.responseJSON.errors[0] })); + } else { + bootbox.alert(I18n.t('admin.email.test_error')); + } }).finally(function() { self.set('sendingEmail', false); }); diff --git a/app/assets/javascripts/admin/controllers/admin-emojis.js.es6 b/app/assets/javascripts/admin/controllers/admin-emojis.js.es6 new file mode 100644 index 0000000000..5921c4f21d --- /dev/null +++ b/app/assets/javascripts/admin/controllers/admin-emojis.js.es6 @@ -0,0 +1,20 @@ +export default Ember.ArrayController.extend({ + sortProperties: ["name"], + + actions: { + emojiUploaded: function (emoji) { + this.pushObject(emoji); + }, + + destroy: function(emoji) { + var self = this; + return bootbox.confirm(I18n.t("admin.emoji.delete_confirm", { name: emoji.name }), I18n.t("no_value"), I18n.t("yes_value"), function (destroy) { + if (destroy) { + return Discourse.ajax("/admin/customize/emojis/" + emoji.name, { type: "DELETE" }).then(function() { + self.removeObject(emoji); + }); + } + }); + } + } +}); diff --git a/app/assets/javascripts/admin/controllers/admin-flags-list.js.es6 b/app/assets/javascripts/admin/controllers/admin-flags-list.js.es6 index da81f2e38d..9ef1a43272 100644 --- a/app/assets/javascripts/admin/controllers/admin-flags-list.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-flags-list.js.es6 @@ -5,7 +5,7 @@ export default Ember.ArrayController.extend({ adminActiveFlagsView: Em.computed.equal("query", "active"), actions: { - disagreeFlags: function (flaggedPost) { + disagreeFlags(flaggedPost) { var self = this; flaggedPost.disagreeFlags().then(function () { self.removeObject(flaggedPost); @@ -14,7 +14,7 @@ export default Ember.ArrayController.extend({ }); }, - deferFlags: function (flaggedPost) { + deferFlags(flaggedPost) { var self = this; flaggedPost.deferFlags().then(function () { self.removeObject(flaggedPost); @@ -23,12 +23,12 @@ export default Ember.ArrayController.extend({ }); }, - doneTopicFlags: function(item) { + doneTopicFlags(item) { this.send("disagreeFlags", item); }, }, - loadMore: function(){ + loadMore(){ var flags = this.get("model"); return Discourse.FlaggedPost.findAll(this.get("query"),flags.length+1).then(function(data){ if(data.length===0){ diff --git a/app/assets/javascripts/admin/controllers/admin-group.js.es6 b/app/assets/javascripts/admin/controllers/admin-group.js.es6 index 10cee82d99..012f2da9e8 100644 --- a/app/assets/javascripts/admin/controllers/admin-group.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-group.js.es6 @@ -1,51 +1,86 @@ export default Em.ObjectController.extend({ - needs: ['adminGroups'], - members: null, + needs: ['adminGroupsType'], disableSave: false, + currentPage: function() { + if (this.get("user_count") === 0) { return 0; } + return Math.floor(this.get("offset") / this.get("limit")) + 1; + }.property("limit", "offset", "user_count"), + + totalPages: function() { + if (this.get("user_count") === 0) { return 0; } + return Math.floor(this.get("user_count") / this.get("limit")) + 1; + }.property("limit", "user_count"), + + showingFirst: Em.computed.lte("currentPage", 1), + showingLast: Discourse.computed.propertyEqual("currentPage", "totalPages"), + aliasLevelOptions: function() { return [ - { name: I18n.t("groups.alias_levels.nobody"), value: 0}, - { name: I18n.t("groups.alias_levels.mods_and_admins"), value: 2}, - { name: I18n.t("groups.alias_levels.members_mods_and_admins"), value: 3}, - { name: I18n.t("groups.alias_levels.everyone"), value: 99} + { name: I18n.t("groups.alias_levels.nobody"), value: 0 }, + { name: I18n.t("groups.alias_levels.mods_and_admins"), value: 2 }, + { name: I18n.t("groups.alias_levels.members_mods_and_admins"), value: 3 }, + { name: I18n.t("groups.alias_levels.everyone"), value: 99 } ]; }.property(), - usernames: function(key, value) { - var members = this.get('members'); - if (arguments.length > 1) { - this.set('_usernames', value); - } else { - var usernames; - if(members) { - usernames = members.map(function(user) { - return user.get('username'); - }).join(','); - } - this.set('_usernames', usernames); - } - return this.get('_usernames'); - }.property('members.@each.username'), - actions: { + next: function() { + if (this.get("showingLast")) { return; } + + var group = this.get("model"), + offset = Math.min(group.get("offset") + group.get("limit"), group.get("user_count")); + + group.set("offset", offset); + + return group.findMembers(); + }, + + previous: function() { + if (this.get("showingFirst")) { return; } + + var group = this.get("model"), + offset = Math.max(group.get("offset") - group.get("limit"), 0); + + group.set("offset", offset); + + return group.findMembers(); + }, + + removeMember: function(member) { + var self = this, + message = I18n.t("admin.groups.delete_member_confirm", { username: member.get("username"), group: this.get("name") }); + return bootbox.confirm(message, I18n.t("no_value"), I18n.t("yes_value"), function(confirm) { + if (confirm) { + self.get("model").removeMember(member); + } + }); + }, + + addMembers: function() { + if (Em.isEmpty(this.get("usernames"))) { return; } + this.get("model").addMembers(this.get("usernames")); + // clear the user selector + this.set("usernames", null); + }, + save: function() { var self = this, - group = this.get('model'); + group = this.get('model'), + groupsController = this.get("controllers.adminGroupsType"); - self.set('disableSave', true); + this.set('disableSave', true); var promise; - if (group.get('id')) { - promise = group.saveWithUsernames(this.get('usernames')); + if (group.get("id")) { + promise = group.save(); } else { - promise = group.createWithUsernames(this.get('usernames')).then(function() { - var groupsController = self.get('controllers.adminGroups'); + promise = group.create().then(function() { groupsController.addObject(group); }); } promise.then(function() { - self.send('showGroup', group); + self.transitionToRoute("adminGroup", group); }, function(e) { var message = $.parseJSON(e.responseText).errors; bootbox.alert(message); @@ -56,12 +91,13 @@ export default Em.ObjectController.extend({ destroy: function() { var group = this.get('model'), - groupsController = this.get('controllers.adminGroups'), + groupsController = this.get('controllers.adminGroupsType'), self = this; - bootbox.confirm(I18n.t("admin.groups.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), function(result) { - if (result) { - self.set('disableSave', true); + this.set('disableSave', true); + + bootbox.confirm(I18n.t("admin.groups.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), function(confirmed) { + if (confirmed) { group.destroy().then(function() { groupsController.get('model').removeObject(group); self.transitionToRoute('adminGroups.index'); diff --git a/app/assets/javascripts/admin/controllers/admin-groups-type.js.es6 b/app/assets/javascripts/admin/controllers/admin-groups-type.js.es6 new file mode 100644 index 0000000000..2d75d1911b --- /dev/null +++ b/app/assets/javascripts/admin/controllers/admin-groups-type.js.es6 @@ -0,0 +1,16 @@ +export default Ember.ArrayController.extend({ + sortProperties: ['name'], + refreshingAutoGroups: false, + + actions: { + refreshAutoGroups: function(){ + var self = this; + this.set('refreshingAutoGroups', true); + Discourse.ajax('/admin/groups/refresh_automatic_groups', {type: 'POST'}).then(function() { + self.transitionToRoute("adminGroupsType", "automatic").then(function() { + self.set('refreshingAutoGroups', false); + }); + }); + } + } +}); diff --git a/app/assets/javascripts/admin/controllers/admin-groups.js.es6 b/app/assets/javascripts/admin/controllers/admin-groups.js.es6 deleted file mode 100644 index 03de7cfe98..0000000000 --- a/app/assets/javascripts/admin/controllers/admin-groups.js.es6 +++ /dev/null @@ -1,24 +0,0 @@ -export default Ember.ArrayController.extend({ - sortProperties: ['name'], - - refreshingAutoGroups: false, - - actions: { - refreshAutoGroups: function(){ - var self = this, - groups = this.get('model'); - - self.set('refreshingAutoGroups', true); - this.transitionToRoute('adminGroups.index').then(function() { - Discourse.ajax('/admin/groups/refresh_automatic_groups', {type: 'POST'}).then(function() { - return Discourse.Group.findAll().then(function(newGroups) { - groups.clear(); - groups.addObjects(newGroups); - }).finally(function() { - self.set('refreshingAutoGroups', false); - }); - }); - }); - } - } -}); diff --git a/app/assets/javascripts/admin/controllers/admin-log-screened-ip-address.js.es6 b/app/assets/javascripts/admin/controllers/admin-log-screened-ip-address.js.es6 index edd2e09148..786797f665 100644 --- a/app/assets/javascripts/admin/controllers/admin-log-screened-ip-address.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-log-screened-ip-address.js.es6 @@ -2,6 +2,10 @@ export default Ember.ObjectController.extend({ editing: false, savedIpAddress: null, + isRange: function() { + return this.get("ip_address").indexOf("/") > 0; + }.property("ip_address"), + actions: { allow: function(record) { record.set('action_name', 'do_nothing'); diff --git a/app/assets/javascripts/admin/controllers/admin-logs-screened-emails.js.es6 b/app/assets/javascripts/admin/controllers/admin-logs-screened-emails.js.es6 index 2b4bd0e29c..e467be5b17 100644 --- a/app/assets/javascripts/admin/controllers/admin-logs-screened-emails.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-logs-screened-emails.js.es6 @@ -1,18 +1,24 @@ +import { outputExportResult } from 'discourse/lib/export-result'; + export default Ember.ArrayController.extend(Discourse.Presence, { loading: false, actions: { - clearBlock: function(row){ + clearBlock(row){ row.clearBlock().then(function(){ // feeling lazy window.location.reload(); }); + }, + + exportScreenedEmailList() { + Discourse.ExportCsv.exportScreenedEmailList().then(outputExportResult); } }, - show: function() { + show() { var self = this; - this.set('loading', true); + self.set('loading', true); Discourse.ScreenedEmail.findAll().then(function(result) { self.set('model', result); self.set('loading', false); diff --git a/app/assets/javascripts/admin/controllers/admin-logs-screened-ip-addresses.js.es6 b/app/assets/javascripts/admin/controllers/admin-logs-screened-ip-addresses.js.es6 index aeaaffeaf3..dcbd6505e1 100644 --- a/app/assets/javascripts/admin/controllers/admin-logs-screened-ip-addresses.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-logs-screened-ip-addresses.js.es6 @@ -1,19 +1,46 @@ +import { outputExportResult } from 'discourse/lib/export-result'; + export default Ember.ArrayController.extend(Discourse.Presence, { loading: false, itemController: 'admin-log-screened-ip-address', + filter: null, - show: function() { + show: Discourse.debounce(function() { var self = this; - this.set('loading', true); - Discourse.ScreenedIpAddress.findAll().then(function(result) { + self.set('loading', true); + Discourse.ScreenedIpAddress.findAll(this.get("filter")).then(function(result) { self.set('model', result); self.set('loading', false); }); - }, + }, 250).observes("filter"), actions: { - recordAdded: function(arg) { + recordAdded(arg) { this.get("model").unshiftObject(arg); + }, + + rollUp() { + const self = this; + return bootbox.confirm(I18n.t("admin.logs.screened_ips.roll_up_confirm"), I18n.t("no_value"), I18n.t("yes_value"), function (confirmed) { + if (confirmed) { + self.set("loading", true); + return Discourse.ScreenedIpAddress.rollUp().then(function(results) { + if (results && results.subnets) { + if (results.subnets.length > 0) { + self.send("show"); + bootbox.alert(I18n.t("admin.logs.screened_ips.rolled_up_some_subnets", { subnets: results.subnets.join(", ") })); + } else { + self.set("loading", false); + bootbox.alert(I18n.t("admin.logs.screened_ips.rolled_up_no_subnet")); + } + } + }); + } + }); + }, + + exportScreenedIpList() { + Discourse.ExportCsv.exportScreenedIpList().then(outputExportResult); } } }); diff --git a/app/assets/javascripts/admin/controllers/admin-logs-screened-urls.js.es6 b/app/assets/javascripts/admin/controllers/admin-logs-screened-urls.js.es6 index 92cb40e496..003dedca08 100644 --- a/app/assets/javascripts/admin/controllers/admin-logs-screened-urls.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-logs-screened-urls.js.es6 @@ -1,12 +1,20 @@ +import { outputExportResult } from 'discourse/lib/export-result'; + export default Ember.ArrayController.extend(Discourse.Presence, { loading: false, - show: function() { - var self = this; - this.set('loading', true); + show() { + const self = this; + self.set('loading', true); Discourse.ScreenedUrl.findAll().then(function(result) { self.set('model', result); self.set('loading', false); }); + }, + + actions: { + exportScreenedUrlList() { + Discourse.ExportCsv.exportScreenedUrlList().then(outputExportResult); + } } }); diff --git a/app/assets/javascripts/admin/controllers/admin-logs-staff-action-logs.js.es6 b/app/assets/javascripts/admin/controllers/admin-logs-staff-action-logs.js.es6 index 76df4a3b60..6784fa1d8f 100644 --- a/app/assets/javascripts/admin/controllers/admin-logs-staff-action-logs.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-logs-staff-action-logs.js.es6 @@ -1,65 +1,98 @@ -/** - This controller supports the interface for listing staff action logs in the admin section. +import { outputExportResult } from 'discourse/lib/export-result'; - @class AdminLogsStaffActionLogsController - @extends Ember.ArrayController - @namespace Discourse - @module Discourse -**/ export default Ember.ArrayController.extend(Discourse.Presence, { loading: false, - filters: {}, + filters: null, - show: function() { - var self = this; - this.set('loading', true); - Discourse.URL.set('queryParams', this.get('filters')); // TODO: doesn't work - Discourse.StaffActionLog.findAll(this.get('filters')).then(function(result) { - self.set('model', result); - self.set('loading', false); - }); - }.observes('filters.action_name', 'filters.acting_user', 'filters.target_user', 'filters.subject'), - - filtersExists: function() { - return (_.size(this.get('filters')) > 0); - }.property('filters.action_name', 'filters.acting_user', 'filters.target_user', 'filters.subject'), + filtersExists: Ember.computed.gt('filterCount', 0), actionFilter: function() { - if (this.get('filters.action_name')) { - return I18n.t("admin.logs.staff_actions.actions." + this.get('filters.action_name')); + var name = this.get('filters.action_name'); + if (name) { + return I18n.t("admin.logs.staff_actions.actions." + name); } else { return null; } }.property('filters.action_name'), - showInstructions: function() { - return this.get('model.length') > 0; - }.property('loading', 'model.length'), + showInstructions: Ember.computed.gt('model.length', 0), + + refresh: function() { + var self = this; + this.set('loading', true); + + var filters = this.get('filters'), + params = {}, + count = 0; + + // Don't send null values + Object.keys(filters).forEach(function(k) { + var val = filters.get(k); + if (val) { + params[k] = val; + count += 1; + } + }); + this.set('filterCount', count); + + Discourse.StaffActionLog.findAll(params).then(function(result) { + self.set('model', result); + }).finally(function() { + self.set('loading', false); + }); + }, + + resetFilters: function() { + this.set('filters', Ember.Object.create()); + this.refresh(); + }.on('init'), + + _changeFilters: function(props) { + this.get('filters').setProperties(props); + this.refresh(); + }, actions: { clearFilter: function(key) { - delete this.get('filters')[key]; - this.notifyPropertyChange('filters'); + var changed = {}; + + // Special case, clear all action related stuff + if (key === 'actionFilter') { + changed.action_name = null; + changed.action_id = null; + changed.custom_type = null; + } else { + changed[key] = null; + } + this._changeFilters(changed); }, clearAllFilters: function() { - this.set('filters', {}); + this.resetFilters(); }, - filterByAction: function(action) { - this.set('filters.action_name', action); + filterByAction: function(logItem) { + this._changeFilters({ + action_name: logItem.get('action_name'), + action_id: logItem.get('action'), + custom_type: logItem.get('custom_type') + }); }, filterByStaffUser: function(acting_user) { - this.set('filters.acting_user', acting_user.username); + this._changeFilters({ acting_user: acting_user.username }); }, filterByTargetUser: function(target_user) { - this.set('filters.target_user', target_user.username); + this._changeFilters({ target_user: target_user.username }); }, filterBySubject: function(subject) { - this.set('filters.subject', subject); + this._changeFilters({ subject: subject }); + }, + + exportStaffActionLogs: function() { + Discourse.ExportCsv.exportStaffActionLogs().then(outputExportResult); } } }); diff --git a/app/assets/javascripts/admin/controllers/admin-plugins.js.es6 b/app/assets/javascripts/admin/controllers/admin-plugins.js.es6 new file mode 100644 index 0000000000..52b7cb6d8b --- /dev/null +++ b/app/assets/javascripts/admin/controllers/admin-plugins.js.es6 @@ -0,0 +1,6 @@ +export default Ember.ArrayController.extend({ + + adminRoutes: function() { + return this.get('model').map(p => p.admin_route).compact(); + }.property() +}); diff --git a/app/assets/javascripts/admin/controllers/admin-site-settings-category.js.es6 b/app/assets/javascripts/admin/controllers/admin-site-settings-category.js.es6 index f9f477ac9a..4f5fed4539 100644 --- a/app/assets/javascripts/admin/controllers/admin-site-settings-category.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-site-settings-category.js.es6 @@ -3,29 +3,16 @@ export default Ember.ObjectController.extend({ needs: ['adminSiteSettings'], filteredContent: function() { - if (!this.get('categoryNameKey')) { return Em.A(); } - - var category = this.get('controllers.adminSiteSettings.content').find(function(siteSettingCategory) { - return siteSettingCategory.nameKey === this.get('categoryNameKey'); - }, this); + if (!this.get('categoryNameKey')) { return []; } + var category = this.get('controllers.adminSiteSettings.content').findProperty('nameKey', this.get('categoryNameKey')); if (category) { return category.siteSettings; } else { - return Em.A(); + return []; } }.property('controllers.adminSiteSettings.content', 'categoryNameKey'), - emptyContentHandler: function() { - if (this.get('filteredContent').length < 1) { - if ( this.get('controllers.adminSiteSettings.filtered') ) { - this.transitionToRoute('adminSiteSettingsCategory', 'all_results'); - } else { - this.transitionToRoute('adminSiteSettings'); - } - } - }.observes('filteredContent'), - actions: { /** diff --git a/app/assets/javascripts/admin/controllers/admin-site-settings.js.es6 b/app/assets/javascripts/admin/controllers/admin-site-settings.js.es6 index 3c5c608838..a518c91679 100644 --- a/app/assets/javascripts/admin/controllers/admin-site-settings.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-site-settings.js.es6 @@ -1,11 +1,3 @@ -/** - This controller supports the interface for SiteSettings. - - @class AdminSiteSettingsController - @extends Ember.ArrayController - @namespace Discourse - @module Discourse -**/ export default Ember.ArrayController.extend(Discourse.Presence, { filter: null, onlyOverridden: false, @@ -28,6 +20,7 @@ export default Ember.ArrayController.extend(Discourse.Presence, { if ((filter === undefined || filter.length < 1) && !this.get('onlyOverridden')) { this.set('model', this.get('allSiteSettings')); + this.transitionToRoute("adminSiteSettings"); return; } @@ -50,20 +43,19 @@ export default Ember.ArrayController.extend(Discourse.Presence, { }); if (matches.length > 0) { matchesGroupedByCategory[0].siteSettings.pushObjects(matches); - matchesGroupedByCategory.pushObject({ - nameKey: settingsCategory.nameKey, - name: settingsCategory.name, - siteSettings: matches}); } }); this.set('model', matchesGroupedByCategory); + this.transitionToRoute("adminSiteSettingsCategory", "all_results"); }, 250).observes('filter', 'onlyOverridden'), actions: { clearFilter: function() { - this.set('filter', ''); - this.set('onlyOverridden', false); + this.setProperties({ + filter: '', + onlyOverridden: false + }); } } diff --git a/app/assets/javascripts/admin/controllers/admin-site-text-edit.js.es6 b/app/assets/javascripts/admin/controllers/admin-site-text-edit.js.es6 index 140e553766..c95db1e8b8 100644 --- a/app/assets/javascripts/admin/controllers/admin-site-text-edit.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-site-text-edit.js.es6 @@ -4,7 +4,7 @@ export default Ember.ObjectController.extend({ saveDisabled: function() { if (this.get('saving')) { return true; } - if ((!this.get('allow_blank')) && Ember.empty(this.get('value'))) { return true; } + if ((!this.get('allow_blank')) && Ember.isEmpty(this.get('value'))) { return true; } return false; }.property('saving', 'value'), diff --git a/app/assets/javascripts/admin/controllers/admin-user-badges.js.es6 b/app/assets/javascripts/admin/controllers/admin-user-badges.js.es6 index 91340b2bb4..5bb53c4b81 100644 --- a/app/assets/javascripts/admin/controllers/admin-user-badges.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-user-badges.js.es6 @@ -32,7 +32,7 @@ export default Ember.ArrayController.extend({ } }); - return badges; + return _.sortBy(badges, "name"); }.property('badges.@each', 'model.@each'), /** diff --git a/app/assets/javascripts/admin/controllers/admin-user-field-item.js.es6 b/app/assets/javascripts/admin/controllers/admin-user-field-item.js.es6 index 0e0ac50be4..f4b9ba2a79 100644 --- a/app/assets/javascripts/admin/controllers/admin-user-field-item.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-user-field-item.js.es6 @@ -17,15 +17,18 @@ export default Ember.ObjectController.extend(BufferedContent, { if (this.get('required')) { ret.push(I18n.t('admin.user_fields.required.enabled')); } + if (this.get('show_on_profile')) { + ret.push(I18n.t('admin.user_fields.show_on_profile.enabled')); + } return ret.join(', '); - }.property('editable', 'required'), + }.property('editable', 'required', 'show_on_profile'), actions: { save: function() { var self = this; - var attrs = this.get('buffered').getProperties('name', 'description', 'field_type', 'editable', 'required'); + var attrs = this.get('buffered').getProperties('name', 'description', 'field_type', 'editable', 'required', 'show_on_profile'); this.get('model').save(attrs).then(function(res) { self.set('model.id', res.user_field.id); @@ -50,7 +53,7 @@ export default Ember.ObjectController.extend(BufferedContent, { cancel: function() { var id = this.get('id'); - if (Ember.empty(id)) { + if (Ember.isEmpty(id)) { this.get('controllers.admin-user-fields').send('destroy', this.get('model')); } else { this.rollbackBuffer(); diff --git a/app/assets/javascripts/admin/controllers/admin-user-index.js.es6 b/app/assets/javascripts/admin/controllers/admin-user-index.js.es6 index 7c9efafa03..d218bbe104 100644 --- a/app/assets/javascripts/admin/controllers/admin-user-index.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-user-index.js.es6 @@ -19,7 +19,7 @@ export default ObjectController.extend(CanCheckEmails, { var siteUserFields = this.site.get('user_fields'), userFields = this.get('user_fields'); - if (!Ember.empty(siteUserFields)) { + if (!Ember.isEmpty(siteUserFields)) { return siteUserFields.map(function(uf) { var value = userFields ? userFields[uf.get('id').toString()] : null; return {name: uf.get('name'), value: value}; diff --git a/app/assets/javascripts/admin/controllers/admin-users-list-show.js.es6 b/app/assets/javascripts/admin/controllers/admin-users-list-show.js.es6 new file mode 100644 index 0000000000..708207f84a --- /dev/null +++ b/app/assets/javascripts/admin/controllers/admin-users-list-show.js.es6 @@ -0,0 +1,75 @@ +export default Ember.ArrayController.extend({ + query: null, + showEmails: false, + refreshing: false, + listFilter: null, + selectAll: false, + + queryNew: Em.computed.equal('query', 'new'), + queryPending: Em.computed.equal('query', 'pending'), + queryHasApproval: Em.computed.or('queryNew', 'queryPending'), + showApproval: Em.computed.and('siteSettings.must_approve_users', 'queryHasApproval'), + searchHint: Discourse.computed.i18n('search_hint'), + hasSelection: Em.computed.gt('selectedCount', 0), + + selectedCount: function() { + var model = this.get('model'); + if (!model || !model.length) return 0; + return model.filterProperty('selected').length; + }.property('model.@each.selected'), + + selectAllChanged: function() { + var val = this.get('selectAll'); + this.get('model').forEach(function(user) { + if (user.get('can_approve')) { + user.set('selected', val); + } + }); + }.observes('selectAll'), + + title: function() { + return I18n.t('admin.users.titles.' + this.get('query')); + }.property('query'), + + _filterUsers: Discourse.debounce(function() { + this._refreshUsers(); + }, 250).observes('listFilter'), + + _refreshUsers: function() { + var self = this; + this.set('refreshing', true); + + Discourse.AdminUser.findAll(this.get('query'), { filter: this.get('listFilter'), show_emails: this.get('showEmails') }).then(function (result) { + self.set('model', result); + }).finally(function() { + self.set('refreshing', false); + }); + }, + + actions: { + approveUsers: function() { + Discourse.AdminUser.bulkApprove(this.get('model').filterProperty('selected')); + this._refreshUsers(); + }, + + rejectUsers: function() { + var maxPostAge = this.siteSettings.delete_user_max_post_age; + var controller = this; + Discourse.AdminUser.bulkReject(this.get('model').filterProperty('selected')).then(function(result){ + var message = I18n.t("admin.users.reject_successful", {count: result.success}); + if (result.failed > 0) { + message += ' ' + I18n.t("admin.users.reject_failures", {count: result.failed}); + message += ' ' + I18n.t("admin.user.delete_forbidden", {count: maxPostAge}); + } + bootbox.alert(message); + controller._refreshUsers(); + }); + }, + + showEmails: function() { + this.set('showEmails', true); + this._refreshUsers(true); + } + } + +}); diff --git a/app/assets/javascripts/admin/controllers/admin-users-list.js.es6 b/app/assets/javascripts/admin/controllers/admin-users-list.js.es6 deleted file mode 100644 index e3c237d9d2..0000000000 --- a/app/assets/javascripts/admin/controllers/admin-users-list.js.es6 +++ /dev/null @@ -1,140 +0,0 @@ -/** - This controller supports the interface for listing users in the admin section. - - @class AdminUsersListController - @extends Ember.ArrayController - @namespace Discourse - @module Discourse -**/ -export default Ember.ArrayController.extend(Discourse.Presence, { - username: null, - query: null, - selectAll: false, - loading: false, - - mustApproveUsers: Discourse.computed.setting('must_approve_users'), - queryNew: Em.computed.equal('query', 'new'), - queryPending: Em.computed.equal('query', 'pending'), - queryHasApproval: Em.computed.or('queryNew', 'queryPending'), - - searchHint: function() { return I18n.t("search_hint"); }.property(), - - /** - Triggered when the selectAll property is changed - - @event selectAll - **/ - selectAllChanged: function() { - var _this = this; - _.each(this.get('model'),function(user) { - user.set('selected', _this.get('selectAll')); - }); - }.observes('selectAll'), - - /** - Triggered when the username filter is changed - - @event filterUsers - **/ - filterUsers: Discourse.debounce(function() { - this.refreshUsers(); - }, 250).observes('username'), - - /** - Triggered when the order of the users list is changed - - @event orderChanged - **/ - orderChanged: function() { - this.refreshUsers(); - }.observes('query'), - - /** - The title of the user list, based on which query was performed. - - @property title - **/ - title: function() { - return I18n.t('admin.users.titles.' + this.get('query')); - }.property('query'), - - /** - Do we want to show the approval controls? - - @property showApproval - **/ - showApproval: function() { - return Discourse.SiteSettings.must_approve_users && this.get('queryHasApproval'); - }.property('queryPending'), - - /** - How many users are currently selected - - @property selectedCount - **/ - selectedCount: function() { - if (this.blank('model')) return 0; - return this.get('model').filterProperty('selected').length; - }.property('model.@each.selected'), - - /** - Do we have any selected users? - - @property hasSelection - **/ - hasSelection: Em.computed.gt('selectedCount', 0), - - /** - Refresh the current list of users. - - @method refreshUsers - **/ - refreshUsers: function(showEmails) { - var adminUsersListController = this; - adminUsersListController.set('loading', true); - - Discourse.AdminUser.findAll(this.get('query'), { filter: this.get('username'), show_emails: showEmails }).then(function (result) { - adminUsersListController.set('model', result); - adminUsersListController.set('loading', false); - }); - }, - - - /** - Show the list of users. - - @method show - **/ - show: function(term) { - if (this.get('query') === term) { - this.refreshUsers(); - return; - } - this.set('query', term); - }, - - actions: { - approveUsers: function() { - Discourse.AdminUser.bulkApprove(this.get('model').filterProperty('selected')); - this.refreshUsers(); - }, - - rejectUsers: function() { - var controller = this; - Discourse.AdminUser.bulkReject(this.get('model').filterProperty('selected')).then(function(result){ - var message = I18n.t("admin.users.reject_successful", {count: result.success}); - if (result.failed > 0) { - message += ' ' + I18n.t("admin.users.reject_failures", {count: result.failed}); - message += ' ' + I18n.t("admin.user.delete_forbidden", {count: Discourse.SiteSettings.delete_user_max_post_age}); - } - bootbox.alert(message); - controller.refreshUsers(); - }); - }, - - showEmails: function() { - this.refreshUsers(true); - } - } - -}); diff --git a/app/assets/javascripts/admin/controllers/admin.js.es6 b/app/assets/javascripts/admin/controllers/admin.js.es6 index b255781ffd..ae2b81f087 100644 --- a/app/assets/javascripts/admin/controllers/admin.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin.js.es6 @@ -2,6 +2,6 @@ import DiscourseController from 'discourse/controllers/controller'; export default DiscourseController.extend({ showBadges: function() { - return this.get('currentUser.admin') && Discourse.SiteSettings.enable_badges; + return this.get('currentUser.admin') && this.siteSettings.enable_badges; }.property() }); diff --git a/app/assets/javascripts/admin/helpers/report_helpers.js b/app/assets/javascripts/admin/helpers/report_helpers.js deleted file mode 100644 index 5d0ee1ac45..0000000000 --- a/app/assets/javascripts/admin/helpers/report_helpers.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - Return the count of users at the given trust level. - - @method valueAtTrustLevel - @for Handlebars -**/ -Handlebars.registerHelper('valueAtTrustLevel', function(property, trustLevel) { - var data = Ember.Handlebars.get(this, property); - if( data ) { - var item = data.find( function(d) { return parseInt(d.x,10) === parseInt(trustLevel,10); } ); - if( item ) { - return item.y; - } else { - return 0; - } - } -}); diff --git a/app/assets/javascripts/admin/helpers/value-at-tl.js.es6 b/app/assets/javascripts/admin/helpers/value-at-tl.js.es6 new file mode 100644 index 0000000000..8fdc4e5d2b --- /dev/null +++ b/app/assets/javascripts/admin/helpers/value-at-tl.js.es6 @@ -0,0 +1,13 @@ +import registerUnbound from 'discourse/helpers/register-unbound'; + +registerUnbound('value-at-tl', function(data, params) { + var tl = parseInt(params.level, 10); + if (data) { + var item = data.find( function(d) { return parseInt(d.x, 10) === tl; } ); + if (item) { + return item.y; + } else { + return 0; + } + } +}); diff --git a/app/assets/javascripts/admin/models/admin_user.js b/app/assets/javascripts/admin/models/admin_user.js index c89b540973..95643db778 100644 --- a/app/assets/javascripts/admin/models/admin_user.js +++ b/app/assets/javascripts/admin/models/admin_user.js @@ -98,7 +98,21 @@ Discourse.AdminUser = Discourse.User.extend({ this.set('admin', true); this.set('can_grant_admin', false); this.set('can_revoke_admin', true); - Discourse.ajax("/admin/users/" + (this.get('id')) + "/grant_admin", {type: 'PUT'}); + var self = this; + + Discourse.ajax("/admin/users/" + (this.get('id')) + "/grant_admin", {type: 'PUT'}) + .then(null, function(e) { + self.set('admin', false); + self.set('can_grant_admin', true); + self.set('can_revoke_admin', false); + + var error; + if (e.responseJSON && e.responseJSON.error) { + error = e.responseJSON.error; + } + error = error || I18n.t('admin.user.grant_admin_failed', { error: "http: " + e.status + " - " + e.body }); + bootbox.alert(error); + }); }, // Revoke the user's moderation access @@ -113,7 +127,20 @@ Discourse.AdminUser = Discourse.User.extend({ this.set('moderator', true); this.set('can_grant_moderation', false); this.set('can_revoke_moderation', true); - Discourse.ajax("/admin/users/" + (this.get('id')) + "/grant_moderation", {type: 'PUT'}); + var self = this; + Discourse.ajax("/admin/users/" + (this.get('id')) + "/grant_moderation", {type: 'PUT'}) + .then(null, function(e) { + self.set('moderator', false); + self.set('can_grant_moderation', true); + self.set('can_revoke_moderation', false); + + var error; + if (e.responseJSON && e.responseJSON.error) { + error = e.responseJSON.error; + } + error = error || I18n.t('admin.user.grant_moderation_failed', { error: "http: " + e.status + " - " + e.body }); + bootbox.alert(error); + }); }, refreshBrowsers: function() { @@ -298,9 +325,7 @@ Discourse.AdminUser = Discourse.User.extend({ }); }, - deleteForbidden: function() { - return (!this.get('can_be_deleted') || this.get('post_count') > 0); - }.property('post_count'), + deleteForbidden: Em.computed.not("canBeDeleted"), deleteExplanation: function() { if (this.get('deleteForbidden')) { @@ -316,9 +341,10 @@ Discourse.AdminUser = Discourse.User.extend({ destroy: function(opts) { var user = this; + var location = document.location.pathname; var performDestroy = function(block) { - var formData = { context: window.location.pathname }; + var formData = { context: location }; if (block) { formData["block_email"] = true; formData["block_urls"] = true; @@ -332,7 +358,11 @@ Discourse.AdminUser = Discourse.User.extend({ data: formData }).then(function(data) { if (data.deleted) { - document.location = "/admin/users/list/active"; + if (/^\/admin\/users\/list\//.test(location)) { + document.location = location; + } else { + document.location = "/admin/users/list/active"; + } } else { bootbox.alert(I18n.t("admin.user.delete_failed")); if (data.user) { diff --git a/app/assets/javascripts/admin/models/export_csv.js b/app/assets/javascripts/admin/models/export_csv.js deleted file mode 100644 index 3afe84a1ce..0000000000 --- a/app/assets/javascripts/admin/models/export_csv.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - Data model for representing an export - - @class ExportCsv - @extends Discourse.Model - @namespace Discourse - @module Discourse -**/ -Discourse.ExportCsv = Discourse.Model.extend({}); - -Discourse.ExportCsv.reopenClass({ - /** - Exports user list - - @method export_user_list - **/ - exportUserList: function() { - return Discourse.ajax("/admin/export_csv/users.json").then(function(result) { - if (result.success) { - bootbox.alert(I18n.t("admin.export_csv.success")); - } else { - bootbox.alert(I18n.t("admin.export_csv.failed")); - } - }); - } -}); diff --git a/app/assets/javascripts/admin/models/screened_ip_address.js b/app/assets/javascripts/admin/models/screened_ip_address.js index 985ee81c39..4aafe4df9c 100644 --- a/app/assets/javascripts/admin/models/screened_ip_address.js +++ b/app/assets/javascripts/admin/models/screened_ip_address.js @@ -45,11 +45,15 @@ Discourse.ScreenedIpAddress = Discourse.Model.extend({ }); Discourse.ScreenedIpAddress.reopenClass({ - findAll: function() { - return Discourse.ajax("/admin/logs/screened_ip_addresses.json").then(function(screened_ips) { + findAll: function(filter) { + return Discourse.ajax("/admin/logs/screened_ip_addresses.json", { data: { filter: filter } }).then(function(screened_ips) { return screened_ips.map(function(b) { return Discourse.ScreenedIpAddress.create(b); }); }); + }, + + rollUp: function() { + return Discourse.ajax("/admin/logs/screened_ip_addresses/roll_up", { type: "POST" }); } }); diff --git a/app/assets/javascripts/admin/models/site_customization.js b/app/assets/javascripts/admin/models/site_customization.js index 0e678185cc..27b6ddbdd9 100644 --- a/app/assets/javascripts/admin/models/site_customization.js +++ b/app/assets/javascripts/admin/models/site_customization.js @@ -7,11 +7,16 @@ @module Discourse **/ Discourse.SiteCustomization = Discourse.Model.extend({ - trackedProperties: ['enabled', 'name', 'stylesheet', 'header', 'footer', 'mobile_stylesheet', 'mobile_header', 'mobile_footer', 'override_default_style'], + trackedProperties: [ + 'enabled', 'name', + 'stylesheet', 'header', 'top', 'footer', + 'mobile_stylesheet', 'mobile_header', 'mobile_top', 'mobile_footer', + 'head_tag', 'body_tag' + ], description: function() { return "" + this.name + (this.enabled ? ' (*)' : ''); - }.property('selected', 'name'), + }.property('selected', 'name', 'enabled'), changed: function() { var self = this; @@ -25,8 +30,10 @@ Discourse.SiteCustomization = Discourse.Model.extend({ if (changed) { this.set('savingStatus', ''); } return changed; - - }.property('override_default_style', 'enabled', 'name', 'stylesheet', 'header', 'footer', 'mobile_stylesheet', 'mobile_header', 'mobile_footer', 'originals'), + }.property('enabled', 'name', 'originals', + 'stylesheet', 'header', 'top', 'footer', + 'mobile_stylesheet', 'mobile_header', 'mobile_top', 'mobile_footer', + 'head_tag', 'body_tag'), startTrackingChanges: function() { var self = this; @@ -43,16 +50,20 @@ Discourse.SiteCustomization = Discourse.Model.extend({ save: function() { this.set('savingStatus', I18n.t('saving')); this.set('saving',true); + var data = { name: this.name, enabled: this.enabled, stylesheet: this.stylesheet, header: this.header, + top: this.top, footer: this.footer, mobile_stylesheet: this.mobile_stylesheet, mobile_header: this.mobile_header, + mobile_top: this.mobile_top, mobile_footer: this.mobile_footer, - override_default_style: this.override_default_style + head_tag: this.head_tag, + body_tag: this.body_tag }; var siteCustomization = this; diff --git a/app/assets/javascripts/admin/models/site_setting.js b/app/assets/javascripts/admin/models/site_setting.js index 2990d908cc..0c32fcbabd 100644 --- a/app/assets/javascripts/admin/models/site_setting.js +++ b/app/assets/javascripts/admin/models/site_setting.js @@ -1,11 +1,3 @@ -/** - Our data model for interacting with site settings. - - @class SiteSetting - @extends Discourse.Model - @namespace Discourse - @module Discourse -**/ Discourse.SiteSetting = Discourse.Model.extend({ validationMessage: null, @@ -17,16 +9,13 @@ Discourse.SiteSetting = Discourse.Model.extend({ **/ enabled: function(key, value) { - if (arguments.length === 1) { - // get the boolean value of the setting - if (this.blank('value')) return false; - return this.get('value') === 'true'; - - } else { - // set the boolean value of the setting + if (arguments.length > 1) { this.set('value', value ? 'true' : 'false'); } + if (this.blank('value')) return false; + return this.get('value') === 'true'; + }.property('value'), /** diff --git a/app/assets/javascripts/admin/models/staff_action_log.js b/app/assets/javascripts/admin/models/staff_action_log.js index 75c1e3174f..dc52a7b170 100644 --- a/app/assets/javascripts/admin/models/staff_action_log.js +++ b/app/assets/javascripts/admin/models/staff_action_log.js @@ -1,11 +1,3 @@ -/** - Represents an action taken by a staff member that has been logged. - - @class StaffActionLog - @extends Discourse.Model - @namespace Discourse - @module Discourse -**/ Discourse.StaffActionLog = Discourse.Model.extend({ showFullDetails: false, diff --git a/app/assets/javascripts/admin/models/user-field.js.es6 b/app/assets/javascripts/admin/models/user-field.js.es6 index 56218d8d80..e5c3348efd 100644 --- a/app/assets/javascripts/admin/models/user-field.js.es6 +++ b/app/assets/javascripts/admin/models/user-field.js.es6 @@ -1,8 +1,3 @@ -var _fieldTypes = [ - Ember.Object.create({id: 'text', name: I18n.t('admin.user_fields.field_types.text') }), - Ember.Object.create({id: 'confirm', name: I18n.t('admin.user_fields.field_types.confirm') }) - ]; - var UserField = Ember.Object.extend({ destroy: function() { var self = this; @@ -43,11 +38,18 @@ UserField.reopenClass({ }, fieldTypes: function() { - return _fieldTypes; + if (!this._fieldTypes) { + this._fieldTypes = [ + Ember.Object.create({id: 'text', name: I18n.t('admin.user_fields.field_types.text') }), + Ember.Object.create({id: 'confirm', name: I18n.t('admin.user_fields.field_types.confirm') }) + ]; + } + + return this._fieldTypes; }, fieldTypeById: function(id) { - return _fieldTypes.findBy('id', id); + return this.fieldTypes().findBy('id', id); } }); diff --git a/app/assets/javascripts/admin/routes/admin-dashboard.js.es6 b/app/assets/javascripts/admin/routes/admin-dashboard.js.es6 index acf3b504bb..bbe4c79845 100644 --- a/app/assets/javascripts/admin/routes/admin-dashboard.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-dashboard.js.es6 @@ -1,12 +1,3 @@ -/** - Handles the default admin route - - @class AdminDashboardRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ - export default Discourse.Route.extend({ setupController: function(c) { @@ -16,8 +7,9 @@ export default Discourse.Route.extend({ fetchDashboardData: function(c) { if( !c.get('dashboardFetchedAt') || moment().subtract(30, 'minutes').toDate() > c.get('dashboardFetchedAt') ) { c.set('dashboardFetchedAt', new Date()); + var versionChecks = this.siteSettings.version_checks; Discourse.AdminDashboard.find().then(function(d) { - if( Discourse.SiteSettings.version_checks ){ + if (versionChecks) { c.set('versionCheck', Discourse.VersionCheck.create(d.version_check)); } _.each(d.reports,function(report){ @@ -32,7 +24,8 @@ export default Discourse.Route.extend({ c.set('top_referrers', topReferrers); } - ['admins', 'moderators', 'blocked', 'suspended', 'top_traffic_sources', 'top_referred_topics', 'updated_at'].forEach(function(x) { + [ 'disk_space','admins', 'moderators', 'blocked', 'suspended', + 'top_traffic_sources', 'top_referred_topics', 'updated_at'].forEach(function(x) { c.set(x, d[x]); }); diff --git a/app/assets/javascripts/admin/routes/admin-emojis.js.es6 b/app/assets/javascripts/admin/routes/admin-emojis.js.es6 new file mode 100644 index 0000000000..a33ade0111 --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-emojis.js.es6 @@ -0,0 +1,7 @@ +export default Discourse.Route.extend({ + model: function() { + return Discourse.ajax("/admin/customize/emojis.json").then(function(emojis) { + return emojis.map(function (emoji) { return Ember.Object.create(emoji); }); + }); + } +}); diff --git a/app/assets/javascripts/admin/routes/admin-group.js.es6 b/app/assets/javascripts/admin/routes/admin-group.js.es6 new file mode 100644 index 0000000000..f397297767 --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-group.js.es6 @@ -0,0 +1,20 @@ +export default Discourse.Route.extend({ + + model: function(params) { + var groups = this.modelFor('adminGroupsType'), + group = groups.findProperty('name', params.name); + + if (!group) { return this.transitionTo('adminGroups.index'); } + + return group; + }, + + setupController: function(controller, model) { + controller.set("model", model); + // clear the user selector + controller.set("usernames", null); + // load the members of the group + model.findMembers(); + } + +}); diff --git a/app/assets/javascripts/admin/routes/admin-groups-index.js.es6 b/app/assets/javascripts/admin/routes/admin-groups-index.js.es6 new file mode 100644 index 0000000000..e650d150fb --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-groups-index.js.es6 @@ -0,0 +1,5 @@ +export default Discourse.Route.extend({ + redirect: function() { + this.transitionTo("adminGroupsType", "custom"); + } +}); diff --git a/app/assets/javascripts/admin/routes/admin-groups-type.js.es6 b/app/assets/javascripts/admin/routes/admin-groups-type.js.es6 new file mode 100644 index 0000000000..4f944f9dbc --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-groups-type.js.es6 @@ -0,0 +1,17 @@ +export default Discourse.Route.extend({ + model(params) { + return Discourse.Group.findAll().then(function(groups) { + return groups.filterBy("type", params.type); + }); + }, + + actions: { + newGroup() { + const self = this; + this.transitionTo("adminGroupsType", "custom").then(function() { + var group = Discourse.Group.create({ automatic: false, visible: true }); + self.transitionTo("adminGroup", group); + }); + } + } +}); diff --git a/app/assets/javascripts/admin/routes/admin-logs-staff-action-logs.js.es6 b/app/assets/javascripts/admin/routes/admin-logs-staff-action-logs.js.es6 new file mode 100644 index 0000000000..bcc6a5fc17 --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-logs-staff-action-logs.js.es6 @@ -0,0 +1,23 @@ +export default Discourse.Route.extend({ + // TODO: make this automatic using an `{{outlet}}` + renderTemplate: function() { + this.render('admin/templates/logs/staff_action_logs', {into: 'adminLogs'}); + }, + + setupController: function(controller) { + controller.resetFilters(); + controller.refresh(); + }, + + actions: { + showDetailsModal: function(logRecord) { + Discourse.Route.showModal(this, 'admin_staff_action_log_details', logRecord); + this.controllerFor('modal').set('modalClass', 'log-details-modal'); + }, + + showCustomDetailsModal: function(logRecord) { + Discourse.Route.showModal(this, logRecord.action_name + '_details', logRecord); + this.controllerFor('modal').set('modalClass', 'tabbed-modal log-details-modal'); + } + } +}); diff --git a/app/assets/javascripts/admin/routes/admin-plugins.js.es6 b/app/assets/javascripts/admin/routes/admin-plugins.js.es6 new file mode 100644 index 0000000000..ae7f961fd1 --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-plugins.js.es6 @@ -0,0 +1,12 @@ +export default Ember.Route.extend({ + model() { + return this.store.findAll('plugin'); + }, + + actions: { + showSettings() { + this.transitionTo('adminSiteSettingsCategory', 'plugins'); + } + } +}); + diff --git a/app/assets/javascripts/admin/routes/admin-route-map.js.es6 b/app/assets/javascripts/admin/routes/admin-route-map.js.es6 new file mode 100644 index 0000000000..ab5f2e16c9 --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-route-map.js.es6 @@ -0,0 +1,66 @@ +export default { + resource: 'admin', + + map() { + this.route('dashboard', { path: '/' }); + this.resource('adminSiteSettings', { path: '/site_settings' }, function() { + this.resource('adminSiteSettingsCategory', { path: 'category/:category_id'} ); + }); + + this.resource('adminEmail', { path: '/email'}, function() { + this.route('all'); + this.route('sent'); + this.route('skipped'); + this.route('previewDigest', { path: '/preview-digest' }); + }); + + this.resource('adminCustomize', { path: '/customize' } ,function() { + this.route('colors'); + this.route('css_html'); + this.resource('adminSiteText', { path: '/site_text' }, function() { + this.route('edit', {path: '/:text_type'}); + }); + this.resource('adminUserFields', { path: '/user_fields' }); + this.resource('adminEmojis', { path: '/emojis' }); + }); + this.route('api'); + + this.resource('admin.backups', { path: '/backups' }, function() { + this.route('logs'); + }); + + this.resource('adminReports', { path: '/reports/:type' }); + + this.resource('adminFlags', { path: '/flags' }, function() { + this.route('list', { path: '/:filter' }); + }); + + this.resource('adminLogs', { path: '/logs' }, function() { + this.route('staffActionLogs', { path: '/staff_action_logs' }); + this.route('screenedEmails', { path: '/screened_emails' }); + this.route('screenedIpAddresses', { path: '/screened_ip_addresses' }); + this.route('screenedUrls', { path: '/screened_urls' }); + }); + + this.resource('adminGroups', { path: '/groups' }, function() { + this.resource('adminGroupsType', { path: '/:type' }, function() { + this.resource('adminGroup', { path: '/:name' }); + }); + }); + + this.resource('adminUsers', { path: '/users' }, function() { + this.resource('adminUser', { path: '/:username' }, function() { + this.route('badges'); + this.route('tl3Requirements', { path: '/tl3_requirements' }); + }); + + this.resource('adminUsersList', { path: '/list' }, function() { + this.route('show', { path: '/:filter' }); + }); + }); + + this.resource('adminBadges', { path: '/badges' }, function() { + this.route('show', { path: '/:badge_id' }); + }); + } +}; diff --git a/app/assets/javascripts/admin/routes/admin-users-list-index.js.es6 b/app/assets/javascripts/admin/routes/admin-users-list-index.js.es6 new file mode 100644 index 0000000000..c989a677c1 --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-users-list-index.js.es6 @@ -0,0 +1,5 @@ +export default Discourse.Route.extend({ + redirect: function() { + this.replaceWith('adminUsersList.show', 'active'); + } +}); diff --git a/app/assets/javascripts/admin/routes/admin-users-list-show.js.es6 b/app/assets/javascripts/admin/routes/admin-users-list-show.js.es6 new file mode 100644 index 0000000000..87d5fce68c --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-users-list-show.js.es6 @@ -0,0 +1,15 @@ +export default Discourse.Route.extend({ + model: function(params) { + this.userFilter = params.filter; + return Discourse.AdminUser.findAll(params.filter); + }, + + setupController: function(controller, model) { + controller.setProperties({ + model: model, + query: this.userFilter, + showEmails: false, + refreshing: false, + }); + } +}); diff --git a/app/assets/javascripts/admin/routes/admin-users-list.js.es6 b/app/assets/javascripts/admin/routes/admin-users-list.js.es6 new file mode 100644 index 0000000000..5a028a9693 --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-users-list.js.es6 @@ -0,0 +1,19 @@ +import { outputExportResult } from 'discourse/lib/export-result'; + +export default Discourse.Route.extend({ + + actions: { + exportUsers: function() { + Discourse.ExportCsv.exportUserList().then(outputExportResult); + }, + + sendInvites: function() { + this.transitionTo('user.invited', Discourse.User.current()); + }, + + deleteUser: function(user) { + Discourse.AdminUser.create(user).destroy({ deletePosts: true }); + } + } + +}); diff --git a/app/assets/javascripts/admin/routes/admin_email_index_route.js b/app/assets/javascripts/admin/routes/admin_email_index_route.js index 718afa426d..479d591171 100644 --- a/app/assets/javascripts/admin/routes/admin_email_index_route.js +++ b/app/assets/javascripts/admin/routes/admin_email_index_route.js @@ -1,13 +1,4 @@ -/** - Handles email routes - - @class AdminEmailRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ Discourse.AdminEmailIndexRoute = Discourse.Route.extend({ - model: function() { return Discourse.EmailSettings.find(); }, diff --git a/app/assets/javascripts/admin/routes/admin_group_route.js b/app/assets/javascripts/admin/routes/admin_group_route.js deleted file mode 100644 index 3de37b38e4..0000000000 --- a/app/assets/javascripts/admin/routes/admin_group_route.js +++ /dev/null @@ -1,23 +0,0 @@ -Discourse.AdminGroupRoute = Discourse.Route.extend({ - - model: function(params) { - var groups = this.modelFor('adminGroups'), - group = groups.findProperty('name', params.name); - - if (!group) { return this.transitionTo('adminGroups.index'); } - return group; - }, - - afterModel: function(model) { - var self = this; - return model.findMembers().then(function(members) { - self.set('_members', members); - }); - }, - - setupController: function(controller, model) { - controller.set('model', model); - controller.set('members', this.get('_members')); - } -}); - diff --git a/app/assets/javascripts/admin/routes/admin_groups_route.js b/app/assets/javascripts/admin/routes/admin_groups_route.js deleted file mode 100644 index e66299a50f..0000000000 --- a/app/assets/javascripts/admin/routes/admin_groups_route.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - Handles routes for admin groups - - @class AdminGroupsRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminGroupsRoute = Discourse.Route.extend({ - model: function() { - return Discourse.Group.findAll(); - }, - - actions: { - showGroup: function(g) { - // This hack is needed because the autocomplete plugin does not - // refresh properly when the underlying data changes. TODO should - // be to update the plugin so it works properly and remove this hack. - var self = this; - this.transitionTo('adminGroups.index').then(function() { - self.transitionTo('adminGroup', g); - }); - }, - - newGroup: function(){ - var group = Discourse.Group.create({ visible: true }); - this.send('showGroup', group); - } - } -}); - diff --git a/app/assets/javascripts/admin/routes/admin_logs_routes.js b/app/assets/javascripts/admin/routes/admin_logs_routes.js index 2a7dc72300..ad54a895d4 100644 --- a/app/assets/javascripts/admin/routes/admin_logs_routes.js +++ b/app/assets/javascripts/admin/routes/admin_logs_routes.js @@ -12,47 +12,6 @@ Discourse.AdminLogsIndexRoute = Discourse.Route.extend({ } }); -/** - The route that lists staff actions that were logged. - - @class AdminLogsStaffActionLogsRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminLogsStaffActionLogsRoute = Discourse.Route.extend({ - renderTemplate: function() { - this.render('admin/templates/logs/staff_action_logs', {into: 'adminLogs'}); - }, - - setupController: function(controller) { - var queryParams = Discourse.URL.get('queryParams'); - if (queryParams) { - controller.set('filters', queryParams); - } - return controller.show(); - }, - - actions: { - showDetailsModal: function(logRecord) { - Discourse.Route.showModal(this, 'admin_staff_action_log_details', logRecord); - this.controllerFor('modal').set('modalClass', 'log-details-modal'); - }, - - showCustomDetailsModal: function(logRecord) { - Discourse.Route.showModal(this, logRecord.action_name + '_details', logRecord); - this.controllerFor('modal').set('modalClass', 'tabbed-modal log-details-modal'); - } - }, - - deactivate: function() { - this._super(); - - // Clear any filters when we leave the route - Discourse.URL.set('queryParams', null); - } -}); - /** The route that lists blocked email addresses. diff --git a/app/assets/javascripts/admin/routes/admin_routes.js b/app/assets/javascripts/admin/routes/admin_routes.js deleted file mode 100644 index 6640df9198..0000000000 --- a/app/assets/javascripts/admin/routes/admin_routes.js +++ /dev/null @@ -1,65 +0,0 @@ -Discourse.Route.buildRoutes(function() { - this.resource('admin', function() { - this.route('dashboard', { path: '/' }); - this.resource('adminSiteSettings', { path: '/site_settings' }, function() { - this.resource('adminSiteSettingsCategory', { path: 'category/:category_id'} ); - }); - - this.resource('adminEmail', { path: '/email'}, function() { - this.route('all'); - this.route('sent'); - this.route('skipped'); - this.route('previewDigest', { path: '/preview-digest' }); - }); - - this.resource('adminCustomize', { path: '/customize' } ,function() { - this.route('colors'); - this.route('css_html'); - this.resource('adminSiteText', { path: '/site_text' }, function() { - this.route('edit', {path: '/:text_type'}); - }); - this.resource('adminUserFields', { path: '/user_fields' }, function() { - }); - }); - this.route('api'); - - this.resource('admin.backups', { path: '/backups' }, function() { - this.route('logs'); - }); - - this.resource('adminReports', { path: '/reports/:type' }); - - this.resource('adminFlags', { path: '/flags' }, function() { - this.route('list', { path: '/:filter' }); - }); - - this.resource('adminLogs', { path: '/logs' }, function() { - this.route('staffActionLogs', { path: '/staff_action_logs' }); - this.route('screenedEmails', { path: '/screened_emails' }); - this.route('screenedIpAddresses', { path: '/screened_ip_addresses' }); - this.route('screenedUrls', { path: '/screened_urls' }); - }); - - this.resource('adminGroups', { path: '/groups'}, function() { - this.resource('adminGroup', { path: '/:name' }); - }); - - this.resource('adminUsers', { path: '/users' }, function() { - this.resource('adminUser', { path: '/:username' }, function() { - this.route('badges'); - this.route('tl3Requirements', { path: '/tl3_requirements' }); - }); - this.resource('adminUsersList', { path: '/list' }, function() { - _.each(['active', 'new', 'pending', 'admins', 'moderators', 'blocked', 'suspended', - 'newuser', 'basicuser', 'regular', 'leaders', 'elders'], function(x) { - this.route(x, { path: '/' + x }); - }, this); - }); - }); - - this.resource('adminBadges', { path: '/badges' }, function() { - this.route('show', { path: '/:badge_id' }); - }); - - }); -}); diff --git a/app/assets/javascripts/admin/routes/admin_users_list_routes.js b/app/assets/javascripts/admin/routes/admin_users_list_routes.js deleted file mode 100644 index bc1516c182..0000000000 --- a/app/assets/javascripts/admin/routes/admin_users_list_routes.js +++ /dev/null @@ -1,131 +0,0 @@ -/** - Handles the route that deals with listing users - - @class AdminUsersListRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminUsersListRoute = Discourse.Route.extend({ - renderTemplate: function() { - this.render('admin/templates/users_list', {into: 'admin/templates/admin'}); - }, - - actions: { - exportUsers: function() { - Discourse.ExportCsv.exportUserList(); - } - } -}); - -/** - Index should just redirect to active - - @class AdminUsersIndexRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminUsersListIndexRoute = Discourse.Route.extend({ - redirect: function() { - this.transitionTo('adminUsersList.active'); - } -}); - -/** - Handles the route that lists active users. - - @class AdminUsersListActiveRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminUsersListActiveRoute = Discourse.Route.extend({ - setupController: function() { - return this.controllerFor('adminUsersList').show('active'); - } -}); - -/** - Handles the route that lists new users. - - @class AdminUsersListNewRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminUsersListNewRoute = Discourse.Route.extend({ - setupController: function() { - return this.controllerFor('adminUsersList').show('new'); - } -}); - -/** - Handles the route that lists pending users. - - @class AdminUsersListPendingRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminUsersListPendingRoute = Discourse.Route.extend({ - setupController: function() { - return this.controllerFor('adminUsersList').show('pending'); - } -}); - -/** - Handles the route that lists admin users. - - @class AdminUsersListAdminsRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminUsersListAdminsRoute = Discourse.Route.extend({ - setupController: function() { - return this.controllerFor('adminUsersList').show('admins'); - } -}); - -/** - Handles the route that lists moderators. - - @class AdminUsersListModeratorsRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminUsersListModeratorsRoute = Discourse.Route.extend({ - setupController: function() { - return this.controllerFor('adminUsersList').show('moderators'); - } -}); - -/** - Handles the route that lists blocked users. - - @class AdminUsersListBlockedRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminUsersListBlockedRoute = Discourse.Route.extend({ - setupController: function() { - return this.controllerFor('adminUsersList').show('blocked'); - } -}); - -/** - Handles the route that lists suspended users. - - @class AdminUsersListSuspendedRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminUsersListSuspendedRoute = Discourse.Route.extend({ - setupController: function() { - return this.controllerFor('adminUsersList').show('suspended'); - } -}); diff --git a/app/assets/javascripts/admin/routes/admin_users_list_trust_level_routes.js b/app/assets/javascripts/admin/routes/admin_users_list_trust_level_routes.js deleted file mode 100644 index 4575299b2e..0000000000 --- a/app/assets/javascripts/admin/routes/admin_users_list_trust_level_routes.js +++ /dev/null @@ -1,69 +0,0 @@ -/** - Handles the route that lists users at trust level 0. - - @class AdminUsersListNewuserRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminUsersListNewuserRoute = Discourse.Route.extend({ - setupController: function() { - return this.controllerFor('adminUsersList').show('newuser'); - } -}); - -/** - Handles the route that lists users at trust level 1. - - @class AdminUsersListBasicuserRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminUsersListBasicuserRoute = Discourse.Route.extend({ - setupController: function() { - return this.controllerFor('adminUsersList').show('basic'); - } -}); - -/** - Handles the route that lists users at trust level 2. - - @class AdminUsersListRegularRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminUsersListRegularRoute = Discourse.Route.extend({ - setupController: function() { - return this.controllerFor('adminUsersList').show('regular'); - } -}); - -/** - Handles the route that lists users at trust level 3. - - @class AdminUsersListLeadersRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminUsersListLeadersRoute = Discourse.Route.extend({ - setupController: function() { - return this.controllerFor('adminUsersList').show('leader'); - } -}); - -/** - Handles the route that lists users at trust level 4. - - @class AdminUsersListEldersRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminUsersListEldersRoute = Discourse.Route.extend({ - setupController: function() { - return this.controllerFor('adminUsersList').show('elder'); - } -}); diff --git a/app/assets/javascripts/admin/templates/admin.hbs b/app/assets/javascripts/admin/templates/admin.hbs index d67291e9ad..a651826c86 100644 --- a/app/assets/javascripts/admin/templates/admin.hbs +++ b/app/assets/javascripts/admin/templates/admin.hbs @@ -4,25 +4,27 @@
@@ -34,4 +36,3 @@
- diff --git a/app/assets/javascripts/admin/templates/api.hbs b/app/assets/javascripts/admin/templates/api.hbs index 3807e1e681..c37c468db3 100644 --- a/app/assets/javascripts/admin/templates/api.hbs +++ b/app/assets/javascripts/admin/templates/api.hbs @@ -1,33 +1,33 @@ {{#if model}} - - + + - {{#each model}} + {{#each k in model}} - + {{/each}}
{{i18n admin.api.key}}{{i18n admin.api.user}}{{i18n 'admin.api.key'}}{{i18n 'admin.api.user'}}  
{{key}}{{k.key}} - {{#if user}} - {{#link-to 'adminUser' user}} - {{avatar user imageSize="small"}} + {{#if k.user}} + {{#link-to 'adminUser' k.user}} + {{avatar k.user imageSize="small"}} {{/link-to}} {{else}} - {{i18n admin.api.all_users}} + {{i18n 'admin.api.all_users'}} {{/if}} - - + +
{{else}} -

{{i18n admin.api.none}}

+

{{i18n 'admin.api.none'}}

{{/if}} {{#unless hasMasterKey}} - + {{/unless }} diff --git a/app/assets/javascripts/admin/templates/backups.hbs b/app/assets/javascripts/admin/templates/backups.hbs index 3ccbfce80b..bcd14fadd6 100644 --- a/app/assets/javascripts/admin/templates/backups.hbs +++ b/app/assets/javascripts/admin/templates/backups.hbs @@ -1,18 +1,31 @@
{{#if canRollback}} - + {{d-button action="rollback" + class="btn-rollback" + label="admin.backups.operations.rollback.text" + title="admin.backups.operations.rollback.title" + icon="ambulance" + disabled=rollbackDisabled}} {{/if}} {{#if isOperationRunning}} - + {{d-button action="cancelOperation" + class="btn-danger" + title="admin.backups.operations.cancel.title" + label="admin.backups.operations.cancel.text" + icon="times"}} {{else}} - + {{d-button action="startBackup" + class="btn-primary" + title="admin.backups.operations.backup.title" + label="admin.backups.operations.backup.text" + icon="rocket"}} {{/if}}
diff --git a/app/assets/javascripts/admin/templates/backups_index.hbs b/app/assets/javascripts/admin/templates/backups_index.hbs index 486bf3027c..bfbedd1be2 100644 --- a/app/assets/javascripts/admin/templates/backups_index.hbs +++ b/app/assets/javascripts/admin/templates/backups_index.hbs @@ -1,7 +1,7 @@ - - + + {{else}} - + diff --git a/app/assets/javascripts/admin/templates/badges-index.hbs b/app/assets/javascripts/admin/templates/badges-index.hbs index 5bb47c2170..fcb54478d8 100644 --- a/app/assets/javascripts/admin/templates/badges-index.hbs +++ b/app/assets/javascripts/admin/templates/badges-index.hbs @@ -1,9 +1,9 @@
-

{{i18n admin.badges.none_selected}}

+

{{i18n 'admin.badges.none_selected'}}

{{#link-to 'adminBadges.show' 'new' class="btn"}} - {{fa-icon "plus"}} {{i18n admin.badges.new}} + {{fa-icon "plus"}} {{i18n 'admin.badges.new'}} {{/link-to}}
diff --git a/app/assets/javascripts/admin/templates/badges-show.hbs b/app/assets/javascripts/admin/templates/badges-show.hbs index e12957320d..2a6b74f169 100644 --- a/app/assets/javascripts/admin/templates/badges-show.hbs +++ b/app/assets/javascripts/admin/templates/badges-show.hbs @@ -1,52 +1,52 @@
- + {{input type="text" name="name" value=buffered.name}}
{{#if showDisplayName}}
- {{i18n admin.badges.display_name}} + {{i18n 'admin.badges.display_name'}} {{buffered.displayName}}
{{/if}}
- + {{input type="text" name="name" value=buffered.icon}} -

{{i18n admin.badges.icon_help}}

+

{{i18n 'admin.badges.icon_help'}}

- + {{input type="text" name="name" value=buffered.image}} -

{{i18n admin.badges.icon_help}}

+

{{i18n 'admin.badges.icon_help'}}

- - {{view Ember.Select name="badge_type_id" - value=buffered.badge_type_id - content=badgeTypes - optionValuePath="content.id" - optionLabelPath="content.name" - disabled=readOnly}} + + {{view "select" name="badge_type_id" + value=buffered.badge_type_id + content=badgeTypes + optionValuePath="content.id" + optionLabelPath="content.name" + disabled=readOnly}}
- - {{view Ember.Select name="badge_grouping_id" - value=buffered.badge_grouping_id - content=badgeGroupings - optionValuePath="content.id" - optionLabelPath="content.name"}} + + {{view "select" name="badge_grouping_id" + value=buffered.badge_grouping_id + content=badgeGroupings + optionValuePath="content.id" + optionLabelPath="content.name"}}  
- + {{#if canEditDescription}} {{textarea name="description" value=buffered.description}} {{else}} @@ -55,83 +55,83 @@
- + {{textarea name="query" value=buffered.query disabled=readOnly}}
{{#if hasQuery}} - {{i18n admin.badges.preview.link_text}} + {{i18n 'admin.badges.preview.link_text'}} | - {{i18n admin.badges.preview.plan_text}} + {{i18n 'admin.badges.preview.plan_text'}} {{#if preview_loading}} - {{i18n loading}}... + {{i18n 'loading'}}... {{/if}}
- - {{view Ember.Select name="trigger" - value=buffered.trigger - content=badgeTriggers - optionValuePath="content.id" - optionLabelPath="content.name" - disabled=readOnly}} + + {{view "select" name="trigger" + value=buffered.trigger + content=badgeTriggers + optionValuePath="content.id" + optionLabelPath="content.name" + disabled=readOnly}}
{{/if}}
- + {{savingStatus}} {{#unless readOnly}} - {{i18n admin.badges.delete}} + {{i18n 'admin.badges.delete'}} {{/unless}}
@@ -140,7 +140,7 @@ {{#if grant_count}}
- {{#link-to 'badges.show' this}}{{i18n badges.granted count=grant_count}}{{/link-to}} + {{#link-to 'badges.show' this}}{{i18n 'badges.granted' count=grant_count}}{{/link-to}}
{{/if}} diff --git a/app/assets/javascripts/admin/templates/badges.hbs b/app/assets/javascripts/admin/templates/badges.hbs index fe2d7ce80a..c128c6fb93 100644 --- a/app/assets/javascripts/admin/templates/badges.hbs +++ b/app/assets/javascripts/admin/templates/badges.hbs @@ -1,21 +1,21 @@
-

{{i18n admin.badges.title}}

+

{{i18n 'admin.badges.title'}}

    - {{#each}} + {{#each badge in model}}
  • - {{#link-to 'adminBadges.show' id}} - {{badge-button badge=this}} - {{#if newBadge}} - {{i18n filters.new.lower_title}} + {{#link-to 'adminBadges.show' badge.id}} + {{badge-button badge=badge}} + {{#if badge.newBadge}} + {{i18n 'filters.new.lower_title'}} {{/if}} {{/link-to}}
  • {{/each}}
{{#link-to 'adminBadges.show' 'new' class="btn"}} - {{fa-icon "plus"}} {{i18n admin.badges.new}} + {{fa-icon "plus"}} {{i18n 'admin.badges.new'}} {{/link-to}}

diff --git a/app/assets/javascripts/admin/templates/components/admin-nav-item.hbs b/app/assets/javascripts/admin/templates/components/admin-nav-item.hbs new file mode 100644 index 0000000000..d6341fdce8 --- /dev/null +++ b/app/assets/javascripts/admin/templates/components/admin-nav-item.hbs @@ -0,0 +1,9 @@ +{{#if routeParam}} + {{#link-to route routeParam}}{{i18n label}}{{/link-to}} +{{else}} + {{#if route}} + {{#link-to route}}{{i18n label}}{{/link-to}} + {{else}} + {{i18n label}} + {{/if}} +{{/if}} diff --git a/app/assets/javascripts/admin/templates/components/admin-nav.hbs b/app/assets/javascripts/admin/templates/components/admin-nav.hbs new file mode 100644 index 0000000000..c5b1d3b58a --- /dev/null +++ b/app/assets/javascripts/admin/templates/components/admin-nav.hbs @@ -0,0 +1,7 @@ +
+
+ +
+
diff --git a/app/assets/javascripts/admin/templates/customize.hbs b/app/assets/javascripts/admin/templates/customize.hbs index 8ac29110e1..75dcdbab9f 100644 --- a/app/assets/javascripts/admin/templates/customize.hbs +++ b/app/assets/javascripts/admin/templates/customize.hbs @@ -1,13 +1,10 @@ -
-
- -
-
+{{#admin-nav}} + {{admin-nav-item route='adminCustomize.colors' label='admin.customize.colors.title'}} + {{admin-nav-item route='adminCustomize.css_html' label='admin.customize.css_html.title'}} + {{admin-nav-item route='adminSiteText' label='admin.site_text.title'}} + {{admin-nav-item route='adminUserFields' label='admin.user_fields.title'}} + {{admin-nav-item route='adminEmojis' label='admin.emoji.title'}} +{{/admin-nav}}
{{outlet}} diff --git a/app/assets/javascripts/admin/templates/customize_colors.hbs b/app/assets/javascripts/admin/templates/customize_colors.hbs index 2f35d97e37..85fc27abb8 100644 --- a/app/assets/javascripts/admin/templates/customize_colors.hbs +++ b/app/assets/javascripts/admin/templates/customize_colors.hbs @@ -1,35 +1,33 @@
-

{{i18n admin.customize.colors.long_title}}

+

{{i18n 'admin.customize.colors.long_title'}}

- +
{{#if selectedItem}}
- {{#with selectedItem}} -

{{text-field class="style-name" value=name}}

+

{{text-field class="style-name" value=selectedItem.name}}

-
- - - - - {{savingStatus}} -
- {{/with}} +
+ + + + + {{selectedItem.savingStatus}} +

@@ -37,7 +35,7 @@
@@ -47,34 +45,34 @@
- + - {{#each colors}} - - + - + {{/each}}
{{i18n admin.backups.columns.filename}}{{i18n admin.backups.columns.size}}{{i18n 'admin.backups.columns.filename'}}{{i18n 'admin.backups.columns.size'}}
{{resumable-upload target="/admin/backups/upload" success="uploadSuccess" error="uploadError" uploadText=uploadText}} @@ -15,15 +15,15 @@
{{human-size backup.size}}
- {{i18n admin.backups.operations.download.text}} + {{i18n 'admin.backups.operations.download.text'}} - +
{{i18n admin.backups.none}}{{i18n 'admin.backups.none'}}
{{i18n admin.customize.color}}{{i18n 'admin.customize.color'}}
- {{translatedName}} + {{#each c in colors}} +
+ {{c.translatedName}}
- {{description}} + {{c.description}}
{{color-input hexValue=hex brightnessValue=brightness valid=valid}}{{color-input hexValue=c.hex brightnessValue=c.brightness valid=c.valid}} - - + +
{{else}} -

{{i18n search.no_results}}

+

{{i18n 'search.no_results'}}

{{/if}} {{else}} -

{{i18n admin.customize.colors.about}}

+

{{i18n 'admin.customize.colors.about'}}

{{/if}}
diff --git a/app/assets/javascripts/admin/templates/customize_css_html.hbs b/app/assets/javascripts/admin/templates/customize_css_html.hbs index 57bccda499..acff1b73e3 100644 --- a/app/assets/javascripts/admin/templates/customize_css_html.hbs +++ b/app/assets/javascripts/admin/templates/customize_css_html.hbs @@ -1,59 +1,80 @@
-

{{i18n admin.customize.css_html.long_title}}

+

{{i18n 'admin.customize.css_html.long_title'}}

{{#if selectedItem}} -
- {{#with selectedItem}} - {{text-field class="style-name" value=name}} +
+
+ {{text-field class="style-name" value=selectedItem.name}}
- {{#if view.stylesheetActive}}{{aceEditor content=stylesheet mode="scss"}}{{/if}} - {{#if view.headerActive}}{{aceEditor content=header mode="html"}}{{/if}} - {{#if view.footerActive}}{{aceEditor content=footer mode="html"}}{{/if}} - {{#if view.mobileStylesheetActive}}{{aceEditor content=mobile_stylesheet mode="scss"}}{{/if}} - {{#if view.mobileHeaderActive}}{{aceEditor content=mobile_header mode="html"}}{{/if}} - {{#if view.mobileFooterActive}}{{aceEditor content=mobile_footer mode="html"}}{{/if}} + {{#if view.stylesheetActive}}{{aceEditor content=selectedItem.stylesheet mode="scss"}}{{/if}} + {{#if view.headerActive}}{{aceEditor content=selectedItem.header mode="html"}}{{/if}} + {{#if view.topActive}}{{aceEditor content=selectedItem.top mode="html"}}{{/if}} + {{#if view.footerActive}}{{aceEditor content=selectedItem.footer mode="html"}}{{/if}} + {{#if view.headTagActive}}{{aceEditor content=selectedItem.head_tag mode="html"}}{{/if}} + {{#if view.bodyTagActive}}{{aceEditor content=selectedItem.body_tag mode="html"}}{{/if}} + {{#if view.mobileStylesheetActive}}{{aceEditor content=selectedItem.mobile_stylesheet mode="scss"}}{{/if}} + {{#if view.mobileHeaderActive}}{{aceEditor content=selectedItem.mobile_header mode="html"}}{{/if}} + {{#if view.mobileTopActive}}{{aceEditor content=selectedItem.mobile_top mode="html"}}{{/if}} + {{#if view.mobileFooterActive}}{{aceEditor content=selectedItem.mobile_footer mode="html"}}{{/if}}
- {{/with}} -
-
- {{i18n admin.customize.override_default}} {{view Ember.Checkbox checkedBinding="selectedItem.override_default_style"}} - {{i18n admin.customize.enabled}} {{view Ember.Checkbox checkedBinding="selectedItem.enabled"}} - {{#unless selectedItem.changed}} - {{i18n admin.customize.preview}} - | - {{i18n admin.customize.undo_preview}} - | - {{i18n admin.customize.rescue_preview}}
- {{/unless}} -
+
{{else}} -

{{i18n admin.customize.about}}

+

{{i18n 'admin.customize.about'}}

{{/if}} diff --git a/app/assets/javascripts/admin/templates/dashboard.hbs b/app/assets/javascripts/admin/templates/dashboard.hbs index cb597d1cdc..74a508fe60 100644 --- a/app/assets/javascripts/admin/templates/dashboard.hbs +++ b/app/assets/javascripts/admin/templates/dashboard.hbs @@ -24,16 +24,16 @@
- - - - + + + + - - - - + + + +
{{i18n admin.dashboard.admins}}{{#link-to 'adminUsersList.admins'}}{{admins}}{{/link-to}} {{i18n admin.dashboard.suspended}}{{#link-to 'adminUsersList.suspended'}}{{suspended}}{{/link-to}} {{i18n 'admin.dashboard.admins'}}{{#link-to 'adminUsersList.show' 'admins'}}{{admins}}{{/link-to}} {{i18n 'admin.dashboard.suspended'}}{{#link-to 'adminUsersList.show' 'suspended'}}{{suspended}}{{/link-to}}
{{i18n admin.dashboard.moderators}}{{#link-to 'adminUsersList.moderators'}}{{moderators}}{{/link-to}} {{i18n admin.dashboard.blocked}}{{#link-to 'adminUsersList.blocked'}}{{blocked}}{{/link-to}} {{i18n 'admin.dashboard.moderators'}}{{#link-to 'adminUsersList.show' 'moderators'}}{{moderators}}{{/link-to}} {{i18n 'admin.dashboard.blocked'}}{{#link-to 'adminUsersList.show' 'blocked'}}{{blocked}}{{/link-to}}
@@ -43,21 +43,21 @@   - {{i18n admin.dashboard.reports.today}} - {{i18n admin.dashboard.reports.yesterday}} - {{i18n admin.dashboard.reports.last_7_days}} - {{i18n admin.dashboard.reports.last_30_days}} - {{i18n admin.dashboard.reports.all}} + {{i18n 'admin.dashboard.reports.today'}} + {{i18n 'admin.dashboard.reports.yesterday'}} + {{i18n 'admin.dashboard.reports.last_7_days'}} + {{i18n 'admin.dashboard.reports.last_30_days'}} + {{i18n 'admin.dashboard.reports.all'}} {{#unless loading}} + {{ render 'admin/templates/reports/per_day_counts_report' visits tagName="tbody"}} {{ render 'admin_report_counts' signups }} {{ render 'admin_report_counts' topics }} {{ render 'admin_report_counts' posts }} {{ render 'admin_report_counts' likes }} {{ render 'admin_report_counts' flags }} {{ render 'admin_report_counts' bookmarks }} - {{ render 'admin_report_counts' starred }} {{ render 'admin_report_counts' emails }} {{/unless}} @@ -67,12 +67,34 @@ - - - - - - + + + + + + + + + {{#unless loading}} + {{ render 'admin_report_counts' page_view_anon_reqs }} + {{ render 'admin_report_counts' page_view_logged_in_reqs }} + {{ render 'admin_report_counts' page_view_crawler_reqs }} + {{ render 'admin_report_counts' page_view_total_reqs }} + {{/unless}} +
{{i18n admin.dashboard.private_messages_short}}{{i18n admin.dashboard.reports.today}}{{i18n admin.dashboard.reports.yesterday}}{{i18n admin.dashboard.reports.last_7_days}}{{i18n admin.dashboard.reports.last_30_days}}{{i18n admin.dashboard.reports.all}}{{i18n 'admin.dashboard.page_views_short'}}{{i18n 'admin.dashboard.reports.today'}}{{i18n 'admin.dashboard.reports.yesterday'}}{{i18n 'admin.dashboard.reports.last_7_days'}}{{i18n 'admin.dashboard.reports.last_30_days'}}{{i18n 'admin.dashboard.reports.all'}}
+
+ + +
+ + + + + + + + + {{#unless loading}} @@ -90,17 +112,54 @@ - - - - + + + - {{#unless loading}} - {{ render 'admin/templates/reports/per_day_counts_report' visits tagName="tbody"}} - {{/unless}} + + {{#unless loading}} + + + + + + + {{/unless}} +
{{i18n 'admin.dashboard.private_messages_short'}}{{i18n 'admin.dashboard.reports.today'}}{{i18n 'admin.dashboard.reports.yesterday'}}{{i18n 'admin.dashboard.reports.last_7_days'}}{{i18n 'admin.dashboard.reports.last_30_days'}}{{i18n 'admin.dashboard.reports.all'}}
 {{i18n admin.dashboard.reports.today}}{{i18n admin.dashboard.reports.yesterday}}{{i18n admin.dashboard.reports.7_days_ago}}{{i18n admin.dashboard.reports.30_days_ago}}
{{i18n 'admin.dashboard.uploads'}}{{disk_space.uploads_used}} ({{i18n 'admin.dashboard.space_free' size=disk_space.uploads_free}}){{i18n 'admin.dashboard.backups'}}{{disk_space.backups_used}} ({{i18n 'admin.dashboard.space_free' size=disk_space.backups_free}})
+ + {{#unless loading}} + {{#if showTrafficReport}} +
+ + + + + + + + + + + + {{#unless loading}} + {{ render 'admin_report_counts' http_2xx_reqs }} + {{ render 'admin_report_counts' http_3xx_reqs}} + {{ render 'admin_report_counts' http_4xx_reqs}} + {{ render 'admin_report_counts' http_5xx_reqs}} + {{ render 'admin_report_counts' http_background_reqs }} + {{ render 'admin_report_counts' http_total_reqs }} + {{/unless}} +
{{i18n 'admin.dashboard.traffic_short'}}{{i18n 'admin.dashboard.reports.today'}}{{i18n 'admin.dashboard.reports.yesterday'}}{{i18n 'admin.dashboard.reports.last_7_days'}}{{i18n 'admin.dashboard.reports.last_30_days'}}{{i18n 'admin.dashboard.reports.all'}}
+
+ {{else}} +
+ {{i18n 'admin.dashboard.show_traffic_report'}} +
+ {{/if}} + {{/unless}}
@@ -110,7 +169,7 @@

- {{i18n admin.dashboard.problems_found}} + {{i18n 'admin.dashboard.problems_found'}}

- {{i18n admin.dashboard.last_checked}}: {{problemsTimestamp}} - + {{i18n 'admin.dashboard.last_checked'}}: {{problemsTimestamp}} + {{d-button action="refreshProblems" class="btn-small" icon="refresh" label="admin.dashboard.refresh_problems"}}

@@ -130,8 +189,8 @@
 

- {{i18n admin.dashboard.no_problems}} - + {{i18n 'admin.dashboard.no_problems'}} + {{d-button action="refreshProblems" class="btn-small" icon="refresh" label="admin.dashboard.refresh_problems"}}

@@ -143,7 +202,7 @@ - + @@ -170,18 +229,18 @@
{{top_referred_topics.title}} ({{i18n admin.dashboard.reports.last_30_days}}){{top_referred_topics.title}} ({{i18n 'admin.dashboard.reports.last_30_days'}}) {{top_referred_topics.ytitles.num_clicks}}
- + {{#unless loading}} - {{#each top_traffic_sources.data}} + {{#each s in top_traffic_sources.data}} - - - + + + {{/each}} @@ -193,18 +252,18 @@
{{top_traffic_sources.title}} ({{i18n admin.dashboard.reports.last_30_days}}){{top_traffic_sources.title}} ({{i18n 'admin.dashboard.reports.last_30_days'}}) {{top_traffic_sources.ytitles.num_clicks}} {{top_traffic_sources.ytitles.num_topics}}
{{domain}}{{num_clicks}}{{num_topics}}{{s.domain}}{{s.num_clicks}}{{s.num_topics}}
- + {{#unless loading}} - {{#each top_referrers.data}} + {{#each r in top_referrers.data}} - - - + + + {{/each}} @@ -215,7 +274,7 @@
-
{{i18n admin.dashboard.last_updated}} {{updatedTimestamp}}
+
{{i18n 'admin.dashboard.last_updated'}} {{updatedTimestamp}}
diff --git a/app/assets/javascripts/admin/templates/email.hbs b/app/assets/javascripts/admin/templates/email.hbs index 14ee7296f1..0ec147207e 100644 --- a/app/assets/javascripts/admin/templates/email.hbs +++ b/app/assets/javascripts/admin/templates/email.hbs @@ -1,14 +1,10 @@ -
-
- -
-
+{{#admin-nav}} + {{admin-nav-item route='adminEmail.index' label='admin.email.settings'}} + {{admin-nav-item route='adminEmail.all' label='admin.email.all'}} + {{admin-nav-item route='adminEmail.sent' label='admin.email.sent'}} + {{admin-nav-item route='adminEmail.skipped' label='admin.email.skipped'}} + {{admin-nav-item route='adminEmail.previewDigest' label='admin.email.preview_digest'}} +{{/admin-nav}}
{{outlet}} diff --git a/app/assets/javascripts/admin/templates/email_all.hbs b/app/assets/javascripts/admin/templates/email_all.hbs index 702499c6b4..c2c6d261a7 100644 --- a/app/assets/javascripts/admin/templates/email_all.hbs +++ b/app/assets/javascripts/admin/templates/email_all.hbs @@ -1,39 +1,39 @@
{{top_referrers.title}} ({{i18n admin.dashboard.reports.last_30_days}}){{top_referrers.title}} ({{i18n 'admin.dashboard.reports.last_30_days'}}) {{top_referrers.ytitles.num_clicks}} {{top_referrers.ytitles.num_topics}}
{{#link-to 'adminUser' this}}{{unbound username}}{{/link-to}}{{num_clicks}}{{num_topics}}{{#link-to 'adminUser' r}}{{unbound r.username}}{{/link-to}}{{r.num_clicks}}{{r.num_topics}}
- - - - - + + + + + - + - {{#each model}} + {{#each l in model}} - + - - - + + + {{else}} - + {{/each}}
{{i18n admin.email.time}}{{i18n admin.email.user}}{{i18n admin.email.to_address}}{{i18n admin.email.email_type}}{{i18n admin.email.skipped_reason}}{{i18n 'admin.email.time'}}{{i18n 'admin.email.user'}}{{i18n 'admin.email.to_address'}}{{i18n 'admin.email.email_type'}}{{i18n 'admin.email.skipped_reason'}}
{{i18n admin.email.logs.filters.title}}{{i18n 'admin.email.logs.filters.title'}} {{text-field value=filter.user placeholderKey="admin.email.logs.filters.user_placeholder"}} {{text-field value=filter.address placeholderKey="admin.email.logs.filters.address_placeholder"}} {{text-field value=filter.type placeholderKey="admin.email.logs.filters.type_placeholder"}} {{text-field value=filter.skipped_reason placeholderKey="admin.email.logs.filters.skipped_reason_placeholder"}}
{{format-date created_at}}{{format-date l.created_at}} - {{#if user}} - {{#link-to 'adminUser' user}}{{avatar user imageSize="tiny"}}{{/link-to}} - {{#link-to 'adminUser' user}}{{user.username}}{{/link-to}} + {{#if l.user}} + {{#link-to 'adminUser' l.user}}{{avatar l.user imageSize="tiny"}}{{/link-to}} + {{#link-to 'adminUser' l.user}}{{l.user.username}}{{/link-to}} {{else}} — {{/if}} {{to_address}}{{email_type}}{{skipped_reason}}{{l.to_address}}{{l.email_type}}{{l.skipped_reason}}
{{i18n admin.email.logs.none}}
{{i18n 'admin.email.logs.none'}}
diff --git a/app/assets/javascripts/admin/templates/email_index.hbs b/app/assets/javascripts/admin/templates/email_index.hbs index 461435534c..8837ac619e 100644 --- a/app/assets/javascripts/admin/templates/email_index.hbs +++ b/app/assets/javascripts/admin/templates/email_index.hbs @@ -1,27 +1,27 @@ - - + + - {{#each model.settings}} + {{#each s in model.settings}} - - + + {{/each}}
{{i18n admin.email.delivery_method}}{{model.delivery_method}}{{i18n 'admin.email.delivery_method'}}{{delivery_method}}
{{name}}{{value}}{{s.name}}{{s.value}}
{{#if sendingEmail}} -
{{i18n admin.email.sending_test}}
+
{{i18n 'admin.email.sending_test'}}
{{else}}
{{text-field value=testEmailAddress placeholderKey="admin.email.test_email_address"}}
- - {{#if sentTestEmail}}{{i18n admin.email.sent_test}}{{/if}} + + {{#if sentTestEmail}}{{i18n 'admin.email.sent_test'}}{{/if}}
{{/if}}
diff --git a/app/assets/javascripts/admin/templates/email_preview_digest.hbs b/app/assets/javascripts/admin/templates/email_preview_digest.hbs index b9119e5ead..d5f3f932b2 100644 --- a/app/assets/javascripts/admin/templates/email_preview_digest.hbs +++ b/app/assets/javascripts/admin/templates/email_preview_digest.hbs @@ -1,19 +1,19 @@ -

{{i18n admin.email.preview_digest_desc}}

+

{{i18n 'admin.email.preview_digest_desc'}}

- + {{input type="date" value=lastSeen id="last-seen"}}
- +
- + {{#if showHtml}} - {{i18n admin.email.html}} | {{i18n admin.email.text}} + {{i18n 'admin.email.html'}} | {{i18n 'admin.email.text'}} {{else}} - {{i18n admin.email.html}} | {{i18n admin.email.text}} + {{i18n 'admin.email.html'}} | {{i18n 'admin.email.text'}} {{/if}}
diff --git a/app/assets/javascripts/admin/templates/email_sent.hbs b/app/assets/javascripts/admin/templates/email_sent.hbs index f3c2cc436b..0287492f17 100644 --- a/app/assets/javascripts/admin/templates/email_sent.hbs +++ b/app/assets/javascripts/admin/templates/email_sent.hbs @@ -1,39 +1,39 @@ - - - - - + + + + + - + - {{#each model}} + {{#each l in model}} - + - - - + + + {{else}} - + {{/each}}
{{i18n admin.email.sent_at}}{{i18n admin.email.user}}{{i18n admin.email.to_address}}{{i18n admin.email.email_type}}{{i18n admin.email.reply_key}}{{i18n 'admin.email.sent_at'}}{{i18n 'admin.email.user'}}{{i18n 'admin.email.to_address'}}{{i18n 'admin.email.email_type'}}{{i18n 'admin.email.reply_key'}}
{{i18n admin.email.logs.filters.title}}{{i18n 'admin.email.logs.filters.title'}} {{text-field value=filter.user placeholderKey="admin.email.logs.filters.user_placeholder"}} {{text-field value=filter.address placeholderKey="admin.email.logs.filters.address_placeholder"}} {{text-field value=filter.type placeholderKey="admin.email.logs.filters.type_placeholder"}} {{text-field value=filter.reply_key placeholderKey="admin.email.logs.filters.reply_key_placeholder"}}
{{format-date created_at}}{{format-date l.created_at}} - {{#if user}} - {{#link-to 'adminUser' user}}{{avatar user imageSize="tiny"}}{{/link-to}} - {{#link-to 'adminUser' user}}{{user.username}}{{/link-to}} + {{#if l.user}} + {{#link-to 'adminUser' l.user}}{{avatar l.user imageSize="tiny"}}{{/link-to}} + {{#link-to 'adminUser' l.user}}{{l.user.username}}{{/link-to}} {{else}} — {{/if}} {{to_address}}{{email_type}}{{reply_key}}{{l.to_address}}{{l.email_type}}{{l.reply_key}}
{{i18n admin.email.logs.none}}
{{i18n 'admin.email.logs.none'}}
diff --git a/app/assets/javascripts/admin/templates/email_skipped.hbs b/app/assets/javascripts/admin/templates/email_skipped.hbs index 702499c6b4..c2c6d261a7 100644 --- a/app/assets/javascripts/admin/templates/email_skipped.hbs +++ b/app/assets/javascripts/admin/templates/email_skipped.hbs @@ -1,39 +1,39 @@ - - - - - + + + + + - + - {{#each model}} + {{#each l in model}} - + - - - + + + {{else}} - + {{/each}}
{{i18n admin.email.time}}{{i18n admin.email.user}}{{i18n admin.email.to_address}}{{i18n admin.email.email_type}}{{i18n admin.email.skipped_reason}}{{i18n 'admin.email.time'}}{{i18n 'admin.email.user'}}{{i18n 'admin.email.to_address'}}{{i18n 'admin.email.email_type'}}{{i18n 'admin.email.skipped_reason'}}
{{i18n admin.email.logs.filters.title}}{{i18n 'admin.email.logs.filters.title'}} {{text-field value=filter.user placeholderKey="admin.email.logs.filters.user_placeholder"}} {{text-field value=filter.address placeholderKey="admin.email.logs.filters.address_placeholder"}} {{text-field value=filter.type placeholderKey="admin.email.logs.filters.type_placeholder"}} {{text-field value=filter.skipped_reason placeholderKey="admin.email.logs.filters.skipped_reason_placeholder"}}
{{format-date created_at}}{{format-date l.created_at}} - {{#if user}} - {{#link-to 'adminUser' user}}{{avatar user imageSize="tiny"}}{{/link-to}} - {{#link-to 'adminUser' user}}{{user.username}}{{/link-to}} + {{#if l.user}} + {{#link-to 'adminUser' l.user}}{{avatar l.user imageSize="tiny"}}{{/link-to}} + {{#link-to 'adminUser' l.user}}{{l.user.username}}{{/link-to}} {{else}} — {{/if}} {{to_address}}{{email_type}}{{skipped_reason}}{{l.to_address}}{{l.email_type}}{{l.skipped_reason}}
{{i18n admin.email.logs.none}}
{{i18n 'admin.email.logs.none'}}
diff --git a/app/assets/javascripts/admin/templates/emojis.hbs b/app/assets/javascripts/admin/templates/emojis.hbs new file mode 100644 index 0000000000..e89ec2af1e --- /dev/null +++ b/app/assets/javascripts/admin/templates/emojis.hbs @@ -0,0 +1,30 @@ +
+

{{i18n 'admin.emoji.title'}}

+ +

{{i18n 'admin.emoji.help'}}

+ +

{{emoji-uploader done="emojiUploaded"}}

+ + {{#if controller}} +
+ + + + + + + + + + {{#each e in controller}} + + + + + + {{/each}} + +
{{i18n "admin.emoji.image"}}{{i18n "admin.emoji.name"}}
:{{e.name}}:
+
+ {{/if}} +
diff --git a/app/assets/javascripts/admin/templates/flags-list.hbs b/app/assets/javascripts/admin/templates/flags-list.hbs index 3afa757d08..6285d9e938 100644 --- a/app/assets/javascripts/admin/templates/flags-list.hbs +++ b/app/assets/javascripts/admin/templates/flags-list.hbs @@ -4,8 +4,8 @@ - {{i18n admin.flags.flagged_by}} - {{#if adminOldFlagsView}}{{i18n admin.flags.resolved_by}}{{/if}} + {{i18n 'admin.flags.flagged_by'}} + {{#if adminOldFlagsView}}{{i18n 'admin.flags.resolved_by'}}{{/if}} @@ -16,12 +16,12 @@ {{#if flaggedPost.postAuthorFlagged}} {{#if flaggedPost.user}} {{#link-to 'adminUser' flaggedPost.user}}{{avatar flaggedPost.user imageSize="small"}}{{/link-to}} - {{#if flaggedPost.wasEdited}}{{/if}} + {{#if flaggedPost.wasEdited}}{{/if}} {{/if}} {{/if}} {{#if adminActiveFlagsView}} {{#if flaggedPost.previous_flags_count}} - {{flaggedPost.previous_flags_count}} + {{flaggedPost.previous_flags_count}} {{/if}} {{/if}} @@ -29,7 +29,7 @@

{{#if flaggedPost.topic.isPrivateMessage}} - {{fa-icon envelope}} + {{fa-icon "envelope"}} {{/if}} {{topic-status topic=flaggedPost.topic}} {{flaggedPost.topic.title}} @@ -42,20 +42,20 @@ - {{#each flaggedPost.flaggers}} + {{#each flagger in flaggedPost.flaggers}} {{/each}} @@ -67,18 +67,18 @@ {{#if adminOldFlagsView}}
- {{#link-to 'adminUser' user}} - {{avatar user imageSize="small"}} + {{#link-to 'adminUser' flagger.user}} + {{avatar flagger.user imageSize="small"}} {{/link-to}} - {{#link-to 'adminUser' user}} - {{user.username}} + {{#link-to 'adminUser' flagger.user}} + {{flagger.user.username}} {{/link-to}} - {{format-age flaggedAt}} + {{format-age flagger.flaggedAt}}
- {{flagType}} + {{flagger.flagType}}
- {{#each flaggedPost.flaggers}} + {{#each flagger in flaggedPost.flaggers}} @@ -95,31 +95,31 @@ {{/if}} - {{#each flaggedPost.conversations}} + {{#each c in flaggedPost.conversations}} @@ -149,5 +149,5 @@ {{loading-spinner condition=view.loading}} {{else}} -

{{i18n admin.flags.no_results}}

+

{{i18n 'admin.flags.no_results'}}

{{/if}} diff --git a/app/assets/javascripts/admin/templates/flags.hbs b/app/assets/javascripts/admin/templates/flags.hbs index 78e11492bf..a1ecf47875 100644 --- a/app/assets/javascripts/admin/templates/flags.hbs +++ b/app/assets/javascripts/admin/templates/flags.hbs @@ -1,11 +1,7 @@ -
-
- -
-
+{{#admin-nav}} + {{admin-nav-item route='adminFlags.list' routeParam='active' label='admin.flags.active'}} + {{admin-nav-item route='adminFlags.list' routeParam='old' label='admin.flags.old'}} +{{/admin-nav}}
{{outlet}} diff --git a/app/assets/javascripts/admin/templates/group.hbs b/app/assets/javascripts/admin/templates/group.hbs index 396e39b88c..2800ac2b9c 100644 --- a/app/assets/javascripts/admin/templates/group.hbs +++ b/app/assets/javascripts/admin/templates/group.hbs @@ -1,29 +1,64 @@ -{{#if automatic}} -

{{name}}

-{{else}} - {{text-field value=name placeholderKey="admin.groups.name_placeholder"}} -{{/if}} +
-
- -
- {{user-selector usernames=usernames id="group-users" placeholderKey="admin.groups.selector_placeholder" tabindex="1" disabled=automatic}} +
+ {{#if automatic}} +

{{name}}

+ {{else}} + + {{text-field name="name" value=name placeholderKey="admin.groups.name_placeholder"}} + {{/if}}
-
-
-
- {{input type="checkbox" checked=visible}} {{i18n groups.visible}} + + {{#if id}} +
+ +
+ {{fa-icon "fast-backward"}} + {{currentPage}}/{{totalPages}} + {{fa-icon "fast-forward"}} +
+
+ {{each member in members itemView="group-member"}} +
+
+ + {{#unless automatic}} +
+ + {{user-selector usernames=usernames placeholderKey="admin.groups.selector_placeholder" id="user-selector"}} + +
+ {{/unless}} + {{/if}} + +
+
-
-
- -
- {{combo-box valueAttribute="value" value=alias_level content=aliasLevelOptions}} + +
+ + {{combo-box name="alias" valueAttribute="value" value=alias_level content=aliasLevelOptions}}
-
-
- + {{#unless automatic}} - +
+ + {{list-setting name="automatic_membership" settingValue=emailDomains}} + +
{{/unless}} -
+ +
+ + {{#unless automatic}} + + {{/unless}} +
+ + diff --git a/app/assets/javascripts/admin/templates/group_member.hbs b/app/assets/javascripts/admin/templates/group_member.hbs new file mode 100644 index 0000000000..d94f2ebe85 --- /dev/null +++ b/app/assets/javascripts/admin/templates/group_member.hbs @@ -0,0 +1 @@ +{{avatar member imageSize="small"}} {{member.username}} {{#unless automatic}}{{fa-icon "times"}}{{/unless}} diff --git a/app/assets/javascripts/admin/templates/groups.hbs b/app/assets/javascripts/admin/templates/groups.hbs index aac3928e60..0de3fc5c79 100644 --- a/app/assets/javascripts/admin/templates/groups.hbs +++ b/app/assets/javascripts/admin/templates/groups.hbs @@ -1,20 +1,8 @@ -
-
-

{{i18n admin.groups.edit}}

- -
- - -
-
+{{#admin-nav}} + {{admin-nav-item route='adminGroupsType' routeParam='custom' label='admin.groups.custom'}} + {{admin-nav-item route='adminGroupsType' routeParam='automatic' label='admin.groups.automatic'}} +{{/admin-nav}} -
- {{outlet}} -
+
+ {{outlet}}
diff --git a/app/assets/javascripts/admin/templates/groups_index.hbs b/app/assets/javascripts/admin/templates/groups_index.hbs deleted file mode 100644 index 5c29aa24ae..0000000000 --- a/app/assets/javascripts/admin/templates/groups_index.hbs +++ /dev/null @@ -1 +0,0 @@ -{{i18n admin.groups.about}} diff --git a/app/assets/javascripts/admin/templates/groups_type.hbs b/app/assets/javascripts/admin/templates/groups_type.hbs new file mode 100644 index 0000000000..0aec861a66 --- /dev/null +++ b/app/assets/javascripts/admin/templates/groups_type.hbs @@ -0,0 +1,20 @@ +
+
+

{{i18n 'admin.groups.edit'}}

+
    + {{#each group in controller}} +
  • + {{#link-to "adminGroup" group.type group.name}}{{group.name}} {{group.userCountDisplay}}{{/link-to}} +
  • + {{/each}} +
+
+ {{d-button action="newGroup" icon="plus" label="admin.groups.new"}} + {{d-button action="refreshAutoGroups" icon="refresh" label="admin.groups.refresh" disabled=refreshingAutoGroups}} +
+
+ +
+ {{outlet}} +
+
diff --git a/app/assets/javascripts/admin/templates/logs.hbs b/app/assets/javascripts/admin/templates/logs.hbs index fc194af035..38654fefca 100644 --- a/app/assets/javascripts/admin/templates/logs.hbs +++ b/app/assets/javascripts/admin/templates/logs.hbs @@ -1,14 +1,12 @@ -
-
- -
-
+{{#admin-nav}} + {{admin-nav-item route='adminLogs.staffActionLogs' label='admin.logs.staff_actions.title'}} + {{admin-nav-item route='adminLogs.screenedEmails' label='admin.logs.screened_emails.title'}} + {{admin-nav-item route='adminLogs.screenedIpAddresses' label='admin.logs.screened_ips.title'}} + {{admin-nav-item route='adminLogs.screenedUrls' label='admin.logs.screened_urls.title'}} + {{#if currentUser.admin}} + {{admin-nav-item href='/logs' label='admin.logs.logster.title'}} + {{/if}} +{{/admin-nav}}
{{outlet}} diff --git a/app/assets/javascripts/admin/templates/logs/_site_customization_change_details.hbs b/app/assets/javascripts/admin/templates/logs/_site_customization_change_details.hbs index f3f5acf196..1c353f62ad 100644 --- a/app/assets/javascripts/admin/templates/logs/_site_customization_change_details.hbs +++ b/app/assets/javascripts/admin/templates/logs/_site_customization_change_details.hbs @@ -1,22 +1,19 @@
- {{i18n admin.customize.css}}: + {{i18n 'admin.customize.css'}}: {{#if stylesheet}} - ({{i18n character_count count=stylesheet.length}}) + ({{i18n 'character_count' count=stylesheet.length}}) {{/if}}
{{textarea value=stylesheet class="plain"}}
- {{i18n admin.customize.header}}: + {{i18n 'admin.customize.header'}}: {{#if header}} - ({{i18n character_count count=header.length}}) + ({{i18n 'character_count' count=header.length}}) {{/if}}
{{textarea value=header class="plain"}}
- {{i18n admin.customize.enabled}}: {{enabled}} -
-
- {{i18n admin.customize.override_default}}: {{override_default_style}} + {{i18n 'admin.customize.enabled'}}: {{enabled}}
diff --git a/app/assets/javascripts/admin/templates/logs/details_modal.hbs b/app/assets/javascripts/admin/templates/logs/details_modal.hbs index 232e993736..d789dd9cc8 100644 --- a/app/assets/javascripts/admin/templates/logs/details_modal.hbs +++ b/app/assets/javascripts/admin/templates/logs/details_modal.hbs @@ -2,5 +2,5 @@
{{details}}
diff --git a/app/assets/javascripts/admin/templates/logs/screened_emails.hbs b/app/assets/javascripts/admin/templates/logs/screened_emails.hbs index d61eb44574..f231c39194 100644 --- a/app/assets/javascripts/admin/templates/logs/screened_emails.hbs +++ b/app/assets/javascripts/admin/templates/logs/screened_emails.hbs @@ -1,16 +1,20 @@ -

{{i18n admin.logs.screened_emails.description}}

+

+ {{i18n 'admin.logs.screened_emails.description'}} + +

+
{{#loading-spinner condition=loading}} {{#if model.length}}
- -
{{i18n admin.logs.action}}
-
{{i18n admin.logs.match_count}}
-
{{i18n admin.logs.last_match_at}}
-
{{i18n admin.logs.created_at}}
-
{{i18n admin.logs.ip_address}}
+ +
{{i18n 'admin.logs.action'}}
+
{{i18n 'admin.logs.match_count'}}
+
{{i18n 'admin.logs.last_match_at'}}
+
{{i18n 'admin.logs.created_at'}}
+
{{i18n 'admin.logs.ip_address'}}
@@ -19,6 +23,6 @@
{{else}} - {{i18n search.no_results}} + {{i18n 'search.no_results'}} {{/if}} {{/loading-spinner}} diff --git a/app/assets/javascripts/admin/templates/logs/screened_emails_list_item.hbs b/app/assets/javascripts/admin/templates/logs/screened_emails_list_item.hbs index 9cbd963f87..7522d0af63 100644 --- a/app/assets/javascripts/admin/templates/logs/screened_emails_list_item.hbs +++ b/app/assets/javascripts/admin/templates/logs/screened_emails_list_item.hbs @@ -6,5 +6,5 @@
{{age-with-tooltip last_match_at}}
{{age-with-tooltip created_at}}
{{ip_address}}
-
+
diff --git a/app/assets/javascripts/admin/templates/logs/screened_ip_addresses.hbs b/app/assets/javascripts/admin/templates/logs/screened_ip_addresses.hbs index 438e51b395..27fa423711 100644 --- a/app/assets/javascripts/admin/templates/logs/screened_ip_addresses.hbs +++ b/app/assets/javascripts/admin/templates/logs/screened_ip_addresses.hbs @@ -1,5 +1,9 @@ -

{{i18n admin.logs.screened_ips.description}}

- +

{{i18n 'admin.logs.screened_ips.description'}}

+
+ {{text-field value=filter class="ip-address-input" placeholderKey="admin.logs.screened_ips.form.filter" autocorrect="off" autocapitalize="off"}} + + +
{{screened-ip-address-form action="recordAdded"}}
@@ -8,11 +12,11 @@
-
{{i18n admin.logs.ip_address}}
-
{{i18n admin.logs.action}}
-
{{i18n admin.logs.match_count}}
-
{{i18n admin.logs.last_match_at}}
-
{{i18n admin.logs.created_at}}
+
{{i18n 'admin.logs.ip_address'}}
+
{{i18n 'admin.logs.action'}}
+
{{i18n 'admin.logs.match_count'}}
+
{{i18n 'admin.logs.last_match_at'}}
+
{{i18n 'admin.logs.created_at'}}
@@ -21,6 +25,6 @@
{{else}} - {{i18n search.no_results}} + {{i18n 'search.no_results'}} {{/if}} {{/loading-spinner}} diff --git a/app/assets/javascripts/admin/templates/logs/screened_ip_addresses_list_item.hbs b/app/assets/javascripts/admin/templates/logs/screened_ip_addresses_list_item.hbs index 23a21becc6..94bc4155e9 100644 --- a/app/assets/javascripts/admin/templates/logs/screened_ip_addresses_list_item.hbs +++ b/app/assets/javascripts/admin/templates/logs/screened_ip_addresses_list_item.hbs @@ -2,7 +2,13 @@ {{#if editing}} {{text-field value=ip_address autofocus="autofocus"}} {{else}} - {{ip_address}} + + {{#if isRange}} + {{ip_address}} + {{else}} + {{ip_address}} + {{/if}} + {{/if}}
@@ -21,13 +27,13 @@ {{#if isBlocked}} - + {{else}} - + {{/if}} {{else}} - - {{i18n cancel}} + + {{i18n 'cancel'}} {{/unless}}
diff --git a/app/assets/javascripts/admin/templates/logs/screened_urls.hbs b/app/assets/javascripts/admin/templates/logs/screened_urls.hbs index 4a87eaf9ce..701909ff56 100644 --- a/app/assets/javascripts/admin/templates/logs/screened_urls.hbs +++ b/app/assets/javascripts/admin/templates/logs/screened_urls.hbs @@ -1,20 +1,24 @@ -

{{i18n admin.logs.screened_urls.description}}

+

+ {{i18n 'admin.logs.screened_urls.description'}} + +

+
{{#loading-spinner condition=loading}} {{#if model.length}}
-
{{i18n admin.logs.screened_urls.domain}}
-
{{i18n admin.logs.action}}
-
{{i18n admin.logs.match_count}}
-
{{i18n admin.logs.last_match_at}}
-
{{i18n admin.logs.created_at}}
+
{{i18n 'admin.logs.screened_urls.domain'}}
+
{{i18n 'admin.logs.action'}}
+
{{i18n 'admin.logs.match_count'}}
+
{{i18n 'admin.logs.last_match_at'}}
+
{{i18n 'admin.logs.created_at'}}
{{view 'screened-urls-list' content=controller}}
{{else}} - {{i18n search.no_results}} + {{i18n 'search.no_results'}} {{/if}} {{/loading-spinner}} diff --git a/app/assets/javascripts/admin/templates/logs/site_customization_change_modal.hbs b/app/assets/javascripts/admin/templates/logs/site_customization_change_modal.hbs index 301ebd9d5b..7fb1a8d253 100644 --- a/app/assets/javascripts/admin/templates/logs/site_customization_change_modal.hbs +++ b/app/assets/javascripts/admin/templates/logs/site_customization_change_modal.hbs @@ -1,10 +1,10 @@
@@ -23,11 +23,11 @@ {{partial "admin/templates/logs/site_customization_change_details"}} {{/with}} {{else}} - {{i18n admin.logs.staff_actions.no_previous}} + {{i18n 'admin.logs.staff_actions.no_previous'}} {{/if}}
diff --git a/app/assets/javascripts/admin/templates/logs/staff_action_logs.hbs b/app/assets/javascripts/admin/templates/logs/staff_action_logs.hbs index c12978703a..58a5ba72a1 100644 --- a/app/assets/javascripts/admin/templates/logs/staff_action_logs.hbs +++ b/app/assets/javascripts/admin/templates/logs/staff_action_logs.hbs @@ -1,45 +1,50 @@ +
+ {{d-button action="exportStaffActionLogs" label="admin.export_csv.button_text" icon="download"}} +
+
+
- {{i18n admin.logs.staff_actions.instructions}} + {{i18n 'admin.logs.staff_actions.instructions'}}
-
{{i18n admin.logs.staff_actions.staff_user}}
-
{{i18n admin.logs.action}}
-
{{i18n admin.logs.staff_actions.subject}}
-
{{i18n admin.logs.staff_actions.when}}
-
{{i18n admin.logs.staff_actions.details}}
-
{{i18n admin.logs.staff_actions.context}}
+
{{i18n 'admin.logs.staff_actions.staff_user'}}
+
{{i18n 'admin.logs.action'}}
+
{{i18n 'admin.logs.staff_actions.subject'}}
+
{{i18n 'admin.logs.staff_actions.when'}}
+
{{i18n 'admin.logs.staff_actions.details'}}
+
{{i18n 'admin.logs.staff_actions.context'}}
@@ -47,7 +52,7 @@ {{#if model.length}} {{view "staff-action-logs-list" content=controller}} {{else}} - {{i18n search.no_results}} + {{i18n 'search.no_results'}} {{/if}} {{/loading-spinner}}
diff --git a/app/assets/javascripts/admin/templates/logs/staff_action_logs_list_item.hbs b/app/assets/javascripts/admin/templates/logs/staff_action_logs_list_item.hbs index c0be5acc5a..07a9b1bf8d 100644 --- a/app/assets/javascripts/admin/templates/logs/staff_action_logs_list_item.hbs +++ b/app/assets/javascripts/admin/templates/logs/staff_action_logs_list_item.hbs @@ -3,7 +3,7 @@ {{acting_user.username}}
{{#if target_user}} @@ -18,10 +18,10 @@
{{{formattedDetails}}} {{#if useCustomModalForDetails}} - {{i18n admin.logs.staff_actions.show}} + {{i18n 'admin.logs.staff_actions.show'}} {{/if}} {{#if useModalForDetails}} - {{i18n admin.logs.staff_actions.show}} + {{i18n 'admin.logs.staff_actions.show'}} {{/if}}
{{context}}
diff --git a/app/assets/javascripts/admin/templates/modal/admin_agree_flag.hbs b/app/assets/javascripts/admin/templates/modal/admin_agree_flag.hbs index 324d6f7bc5..14e1ae9c51 100644 --- a/app/assets/javascripts/admin/templates/modal/admin_agree_flag.hbs +++ b/app/assets/javascripts/admin/templates/modal/admin_agree_flag.hbs @@ -1,11 +1,11 @@ {{#if user_deleted}} - + {{else}} {{#unless postHidden}} - + {{/unless}} {{/if}} - + {{#if canDeleteAsSpammer}} - + {{/if}} diff --git a/app/assets/javascripts/admin/templates/modal/admin_badge_preview.hbs b/app/assets/javascripts/admin/templates/modal/admin_badge_preview.hbs index d05abafdca..835a105cda 100644 --- a/app/assets/javascripts/admin/templates/modal/admin_badge_preview.hbs +++ b/app/assets/javascripts/admin/templates/modal/admin_badge_preview.hbs @@ -1,31 +1,31 @@
{{#if errors}} -

{{i18n admin.badges.preview.sql_error_header}}

+

{{i18n 'admin.badges.preview.sql_error_header'}}

{{errors}}
{{else}} -

{{{i18n admin.badges.preview.grant_count count=count}}}

+

{{{i18n 'admin.badges.preview.grant_count' count=count}}}

{{#if count_warning}}
-

{{i18n admin.badges.preview.bad_count_warning.header}}

-

{{i18n admin.badges.preview.bad_count_warning.text}}

+

{{i18n 'admin.badges.preview.bad_count_warning.header'}}

+

{{i18n 'admin.badges.preview.bad_count_warning.text'}}

{{/if}} {{#if sample}}

- {{i18n admin.badges.preview.sample}} + {{i18n 'admin.badges.preview.sample'}}

    {{#each html in processed_sample}} diff --git a/app/assets/javascripts/admin/templates/modal/admin_delete_flag.hbs b/app/assets/javascripts/admin/templates/modal/admin_delete_flag.hbs index 831ee53891..5762101a1f 100644 --- a/app/assets/javascripts/admin/templates/modal/admin_delete_flag.hbs +++ b/app/assets/javascripts/admin/templates/modal/admin_delete_flag.hbs @@ -1,5 +1,5 @@ - - + + {{#if canDeleteAsSpammer}} - + {{/if}} diff --git a/app/assets/javascripts/admin/templates/modal/admin_edit_badge_groupings.hbs b/app/assets/javascripts/admin/templates/modal/admin_edit_badge_groupings.hbs index 0cb9eb9ca8..1dd273e0fc 100644 --- a/app/assets/javascripts/admin/templates/modal/admin_edit_badge_groupings.hbs +++ b/app/assets/javascripts/admin/templates/modal/admin_edit_badge_groupings.hbs @@ -1,28 +1,27 @@ - diff --git a/app/assets/javascripts/admin/templates/modal/admin_start_backup.hbs b/app/assets/javascripts/admin/templates/modal/admin_start_backup.hbs index 4967f31e6b..93a3a96bc9 100644 --- a/app/assets/javascripts/admin/templates/modal/admin_start_backup.hbs +++ b/app/assets/javascripts/admin/templates/modal/admin_start_backup.hbs @@ -1,3 +1,3 @@ - - - + + + diff --git a/app/assets/javascripts/admin/templates/modal/admin_suspend_user.hbs b/app/assets/javascripts/admin/templates/modal/admin_suspend_user.hbs index 1f31982469..7a82cc0712 100644 --- a/app/assets/javascripts/admin/templates/modal/admin_suspend_user.hbs +++ b/app/assets/javascripts/admin/templates/modal/admin_suspend_user.hbs @@ -1,15 +1,15 @@ diff --git a/app/assets/javascripts/admin/templates/plugins-index.hbs b/app/assets/javascripts/admin/templates/plugins-index.hbs new file mode 100644 index 0000000000..1686e34b9f --- /dev/null +++ b/app/assets/javascripts/admin/templates/plugins-index.hbs @@ -0,0 +1,39 @@ +{{#if length}} + + {{d-button label="admin.plugins.change_settings" + icon="gear" + class='settings-button pull-right' + action="showSettings"}} + +

    {{i18n "admin.plugins.installed"}}

    + +
    + +
- {{#link-to 'adminUser' disposedBy}} - {{avatar disposedBy imageSize="small"}} + {{#link-to 'adminUser' flagger.disposedBy}} + {{avatar flagger.disposedBy imageSize="small"}} {{/link-to}} - {{format-age disposedAt}} - {{{dispositionIcon}}} - {{#if tookAction}} - + {{format-age flagger.disposedAt}} + {{{flagger.dispositionIcon}}} + {{#if flagger.tookAction}} + {{/if}}
- {{{i18n admin.flags.topic_flagged}}} {{i18n admin.flags.visit_topic}} + {{{i18n 'admin.flags.topic_flagged'}}} {{i18n 'admin.flags.visit_topic'}}
- {{#if response}} + {{#if c.response}}

- {{#link-to 'adminUser' response.user}}{{avatar response.user imageSize="small"}}{{/link-to}} {{{response.excerpt}}} + {{#link-to 'adminUser' c.response.user}}{{avatar c.response.user imageSize="small"}}{{/link-to}} {{{c.response.excerpt}}}

- {{#if reply}} + {{#if c.reply}}

- {{#link-to 'adminUser' reply.user}}{{avatar reply.user imageSize="small"}}{{/link-to}} {{{reply.excerpt}}} - {{#if hasMore}} - {{i18n admin.flags.more}} + {{#link-to 'adminUser' c.reply.user}}{{avatar c.reply.user imageSize="small"}}{{/link-to}} {{{c.reply.excerpt}}} + {{#if c.hasMore}} + {{i18n 'admin.flags.more'}} {{/if}}

{{/if}} - - + + {{/if}}
@@ -130,14 +130,14 @@
{{#if adminActiveFlagsView}} - + {{#if flaggedPost.postHidden}} - + {{else}} - + {{/if}} - - + + {{/if}}
+ + + + + + + + {{#each plugin in controller}} + + + + + {{/each}} + +
{{i18n "admin.plugins.name"}}{{i18n "admin.plugins.version"}}
+ {{#if plugin.url}} + {{plugin.name}} + {{else}} + {{plugin.name}} + {{/if}} + {{plugin.version}}
+{{else}} +

{{i18n "admin.plugins.none_installed"}}

+{{/if}} + +

{{i18n "admin.plugins.howto"}}

+ diff --git a/app/assets/javascripts/admin/templates/plugins.hbs b/app/assets/javascripts/admin/templates/plugins.hbs new file mode 100644 index 0000000000..7150e14cdb --- /dev/null +++ b/app/assets/javascripts/admin/templates/plugins.hbs @@ -0,0 +1,13 @@ +
+ +
+ +
+ {{outlet}} +
diff --git a/app/assets/javascripts/admin/templates/reports.hbs b/app/assets/javascripts/admin/templates/reports.hbs index e1f011b5a4..e4fdc87cbf 100644 --- a/app/assets/javascripts/admin/templates/reports.hbs +++ b/app/assets/javascripts/admin/templates/reports.hbs @@ -1,22 +1,22 @@

{{title}}

- {{i18n admin.dashboard.reports.start_date}} {{input type="date" value=startDate}} - {{i18n admin.dashboard.reports.end_date}} {{input type="date" value=endDate}} - + {{i18n 'admin.dashboard.reports.start_date'}} {{input type="date" value=startDate}} + {{i18n 'admin.dashboard.reports.end_date'}} {{input type="date" value=endDate}} + {{d-button action="refreshReport" class="btn-primary" label="admin.dashboard.reports.refresh_report" icon="refresh"}}
{{#if viewingTable}} - {{i18n admin.dashboard.reports.view_table}} + {{i18n 'admin.dashboard.reports.view_table'}} {{else}} - {{i18n admin.dashboard.reports.view_table}} + {{i18n 'admin.dashboard.reports.view_table'}} {{/if}} | {{#if viewingBarChart}} - {{i18n admin.dashboard.reports.view_chart}} + {{i18n 'admin.dashboard.reports.view_chart'}} {{else}} - {{i18n admin.dashboard.reports.view_chart}} + {{i18n 'admin.dashboard.reports.view_chart'}} {{/if}}
diff --git a/app/assets/javascripts/admin/templates/reports/per_day_counts_report.hbs b/app/assets/javascripts/admin/templates/reports/per_day_counts_report.hbs index abd92d27f5..bc025cdf8f 100644 --- a/app/assets/javascripts/admin/templates/reports/per_day_counts_report.hbs +++ b/app/assets/javascripts/admin/templates/reports/per_day_counts_report.hbs @@ -4,4 +4,5 @@ {{yesterdayCount}} {{sevenDaysAgoCount}} {{thirtyDaysAgoCount}} + diff --git a/app/assets/javascripts/admin/templates/reports/trust_levels_report.hbs b/app/assets/javascripts/admin/templates/reports/trust_levels_report.hbs index b483755fe1..cedde2f3ad 100644 --- a/app/assets/javascripts/admin/templates/reports/trust_levels_report.hbs +++ b/app/assets/javascripts/admin/templates/reports/trust_levels_report.hbs @@ -1,8 +1,8 @@ {{title}} - {{#link-to 'adminUsersList.newuser'}}{{valueAtTrustLevel data 0}}{{/link-to}} - {{#link-to 'adminUsersList.basicuser'}}{{valueAtTrustLevel data 1}}{{/link-to}} - {{#link-to 'adminUsersList.regular'}}{{valueAtTrustLevel data 2}}{{/link-to}} - {{#link-to 'adminUsersList.leaders'}}{{valueAtTrustLevel data 3}}{{/link-to}} - {{#link-to 'adminUsersList.elders'}}{{valueAtTrustLevel data 4}}{{/link-to}} + {{#link-to 'adminUsersList.show' 'newuser'}}{{value-at-tl data level="0"}}{{/link-to}} + {{#link-to 'adminUsersList.show' 'basic'}}{{value-at-tl data level="1"}}{{/link-to}} + {{#link-to 'adminUsersList.show' 'regular'}}{{value-at-tl data level="2"}}{{/link-to}} + {{#link-to 'adminUsersList.show' 'leader'}}{{value-at-tl data level="3"}}{{/link-to}} + {{#link-to 'adminUsersList.show' 'elder'}}{{value-at-tl data level="4"}}{{/link-to}} diff --git a/app/assets/javascripts/admin/templates/site_settings.hbs b/app/assets/javascripts/admin/templates/site_settings.hbs index 440c919d21..8587e6707e 100644 --- a/app/assets/javascripts/admin/templates/site_settings.hbs +++ b/app/assets/javascripts/admin/templates/site_settings.hbs @@ -2,16 +2,16 @@
{{text-field value=filter placeholderKey="type_to_filter"}} - +
-
+
-
+
{{outlet}}
diff --git a/app/assets/javascripts/admin/templates/site_settings/setting_bool.hbs b/app/assets/javascripts/admin/templates/site_settings/setting_bool.hbs index 66939c4da7..84f07ce4ce 100644 --- a/app/assets/javascripts/admin/templates/site_settings/setting_bool.hbs +++ b/app/assets/javascripts/admin/templates/site_settings/setting_bool.hbs @@ -3,7 +3,7 @@
@@ -14,6 +14,6 @@
{{else}} {{#if overridden}} - + {{/if}} {{/if}} diff --git a/app/assets/javascripts/admin/templates/site_settings/setting_enum.hbs b/app/assets/javascripts/admin/templates/site_settings/setting_enum.hbs index c53b1cf5d7..b0f06e5b14 100644 --- a/app/assets/javascripts/admin/templates/site_settings/setting_enum.hbs +++ b/app/assets/javascripts/admin/templates/site_settings/setting_enum.hbs @@ -3,6 +3,7 @@
{{combo-box valueAttribute="value" content=validValues value=value none=allowsNone}} + {{view.preview}}
{{unbound description}}
{{#if dirty}} @@ -12,6 +13,6 @@ {{else}} {{#if overridden}} - + {{/if}} {{/if}} diff --git a/app/assets/javascripts/admin/templates/site_settings/setting_list.hbs b/app/assets/javascripts/admin/templates/site_settings/setting_list.hbs index f50e4214ff..802a53324e 100644 --- a/app/assets/javascripts/admin/templates/site_settings/setting_list.hbs +++ b/app/assets/javascripts/admin/templates/site_settings/setting_list.hbs @@ -12,6 +12,6 @@ {{else}} {{#if overridden}} - + {{/if}} {{/if}} diff --git a/app/assets/javascripts/admin/templates/site_settings/setting_string.hbs b/app/assets/javascripts/admin/templates/site_settings/setting_string.hbs index a5369cd157..52279773b1 100644 --- a/app/assets/javascripts/admin/templates/site_settings/setting_string.hbs +++ b/app/assets/javascripts/admin/templates/site_settings/setting_string.hbs @@ -13,6 +13,6 @@ {{else}} {{#if overridden}} - + {{/if}} {{/if}} diff --git a/app/assets/javascripts/admin/templates/site_settings_category.hbs b/app/assets/javascripts/admin/templates/site_settings_category.hbs index c7a762e296..002baae2e9 100644 --- a/app/assets/javascripts/admin/templates/site_settings_category.hbs +++ b/app/assets/javascripts/admin/templates/site_settings_category.hbs @@ -2,5 +2,5 @@ {{collection contentBinding="filteredContent" classNames="form-horizontal settings" itemView="site-setting"}} {{else}}
- {{i18n admin.site_settings.no_results}} + {{i18n 'admin.site_settings.no_results'}} {{/if}} diff --git a/app/assets/javascripts/admin/templates/site_text.hbs b/app/assets/javascripts/admin/templates/site_text.hbs index de38984b2f..25924f99e9 100644 --- a/app/assets/javascripts/admin/templates/site_text.hbs +++ b/app/assets/javascripts/admin/templates/site_text.hbs @@ -1,9 +1,9 @@
diff --git a/app/assets/javascripts/admin/templates/site_text_edit.hbs b/app/assets/javascripts/admin/templates/site_text_edit.hbs index d87d17bd59..6db9dd716b 100644 --- a/app/assets/javascripts/admin/templates/site_text_edit.hbs +++ b/app/assets/javascripts/admin/templates/site_text_edit.hbs @@ -17,10 +17,10 @@
- {{#if saved}}{{i18n saved}}{{/if}} + {{#if saved}}{{i18n 'saved'}}{{/if}}
diff --git a/app/assets/javascripts/admin/templates/site_text_index.hbs b/app/assets/javascripts/admin/templates/site_text_index.hbs index 71b256c9c5..5a448def37 100644 --- a/app/assets/javascripts/admin/templates/site_text_index.hbs +++ b/app/assets/javascripts/admin/templates/site_text_index.hbs @@ -1 +1 @@ -

{{i18n admin.site_text.none}}

+

{{i18n 'admin.site_text.none'}}

diff --git a/app/assets/javascripts/admin/templates/user-fields.hbs b/app/assets/javascripts/admin/templates/user-fields.hbs index c829ef18c2..2412dfd1ad 100644 --- a/app/assets/javascripts/admin/templates/user-fields.hbs +++ b/app/assets/javascripts/admin/templates/user-fields.hbs @@ -1,7 +1,7 @@
-

{{i18n admin.user_fields.title}}

+

{{i18n 'admin.user_fields.title'}}

-

{{i18n admin.user_fields.help}}

+

{{i18n 'admin.user_fields.help'}}

{{#if model}} {{#each f in model itemController="admin-user-field-item" itemView="admin-user-field-item"}} @@ -17,19 +17,24 @@ {{combo-box content=fieldTypes valueAttribute="id" value=f.buffered.field_type}}
- - + +
+
+
+
@@ -38,11 +43,10 @@
{{f.name}}
{{{f.description}}}
{{f.fieldName}}
-
-
+
- - + +
{{f.flags}}
@@ -53,6 +57,6 @@ diff --git a/app/assets/javascripts/admin/templates/user_badges.hbs b/app/assets/javascripts/admin/templates/user_badges.hbs index c454e4a7db..a4bfc236e1 100644 --- a/app/assets/javascripts/admin/templates/user_badges.hbs +++ b/app/assets/javascripts/admin/templates/user_badges.hbs @@ -8,26 +8,26 @@ {{#loading-spinner condition=loading}}
-

{{i18n admin.badges.grant_badge}}

+

{{i18n 'admin.badges.grant_badge'}}

{{#if noBadges}} -

{{i18n admin.badges.no_badges}}

+

{{i18n 'admin.badges.no_badges'}}

{{else}}
{{combo-box valueAttribute="id" value=controller.selectedBadgeId content=controller.grantableBadges}} - + {{/if}}

-

{{i18n admin.badges.granted_badges}}

+

{{i18n 'admin.badges.granted_badges'}}


- - - + + + @@ -42,13 +42,13 @@ {{else}} {{/each}} diff --git a/app/assets/javascripts/admin/templates/user_index.hbs b/app/assets/javascripts/admin/templates/user_index.hbs index 2b5f9b8d36..6c85c515eb 100644 --- a/app/assets/javascripts/admin/templates/user_index.hbs +++ b/app/assets/javascripts/admin/templates/user_index.hbs @@ -4,68 +4,68 @@ {{#if active}} {{#link-to 'user' model class="btn"}} - {{i18n admin.user.show_public_profile}} + {{i18n 'admin.user.show_public_profile'}} {{/link-to}} {{#if can_impersonate}} - {{/if}} {{#if currentUser.admin}} {{/if}} {{/if}}
-
{{i18n user.username.title}}
+
{{i18n 'user.username.title'}}
{{username}}
{{#link-to 'preferences.username' model class="btn"}} - {{i18n user.change_username.title}} + {{i18n 'user.change_username.title'}} {{/link-to}}
{{#if canCheckEmails}}
-
{{i18n user.email.title}}
+
{{i18n 'user.email.title'}}
{{#unless active}} -
{{i18n admin.users.not_verified}}
+
{{i18n 'admin.users.not_verified'}}
{{/unless}} {{#if email}} {{email}} {{else}} - + {{/if}}
-
{{i18n user.associated_accounts}}
+
{{i18n 'user.associated_accounts'}}
{{#if associated_accounts}} {{associated_accounts}} {{else}} - + {{/if}}
{{/if}}
-
{{i18n user.avatar.title}}
+
{{i18n 'user.avatar.title'}}
{{avatar content imageSize="large"}}
-
{{i18n user.title.title}}
+
{{i18n 'user.title.title'}}
{{#if editingTitle}} {{text-field value=title autofocus="autofocus"}} @@ -75,23 +75,23 @@
{{#if editingTitle}} - - {{i18n cancel}} + + {{i18n 'cancel'}} {{else}} - + {{/if}}
{{#if currentUser.admin}}
-
{{i18n admin.groups.title}}
+
{{i18n 'admin.groups.title'}}
{{admin-group-selector selected=model.groups available=availableGroups}}
{{#if custom_groups}} - {{i18n admin.groups.primary}} + {{i18n 'admin.groups.primary'}} {{combo-box content=custom_groups value=primary_group_id nameProperty="name" none="admin.groups.no_primary"}} {{/if}} {{#if primaryGroupDirty}} @@ -103,36 +103,36 @@ {{/if}}
-
{{i18n user.ip_address.title}}
+
{{i18n 'user.ip_address.title'}}
{{ip_address}}
- {{#if currentUser.admin}} + {{#if currentUser.staff}} - {{ip-lookup ip=ip_address user_id=id}} + {{ip-lookup ip=ip_address userId=id}} {{/if}}
-
{{i18n user.registration_ip_address.title}}
+
{{i18n 'user.registration_ip_address.title'}}
{{registration_ip_address}}
- {{#if currentUser.admin}} - {{ip-lookup ip=registration_ip_address user_id=id}} + {{#if currentUser.staff}} + {{ip-lookup ip=registration_ip_address userId=id}} {{/if}}
{{#if showBadges}}
-
{{i18n admin.badges.title}}
+
{{i18n 'admin.badges.title'}}
- {{i18n badges.badge_count count=badge_count}} + {{i18n 'badges.badge_count' count=badge_count}}
- {{#link-to 'adminUser.badges' this class="btn"}}{{i18n admin.badges.edit_badges}}{{/link-to}} + {{#link-to 'adminUser.badges' this class="btn"}}{{i18n 'admin.badges.edit_badges'}}{{/link-to}}
{{/if}} @@ -140,12 +140,12 @@ {{#if userFields}}
- {{#each userFields}} + {{#each uf in userFields}}
-
{{name}}
+
{{uf.name}}
- {{#if value}} - {{value}} + {{#if uf.value}} + {{uf.value}} {{else}} — {{/if}} @@ -157,30 +157,30 @@
-

{{i18n admin.user.permissions}}

+

{{i18n 'admin.user.permissions'}}

{{#if showApproval}}
-
{{i18n admin.users.approved}}
+
{{i18n 'admin.users.approved'}}
{{#if approved}} - {{i18n admin.user.approved_by}} + {{i18n 'admin.user.approved_by'}} {{#link-to 'adminUser' approvedBy}}{{avatar approvedBy imageSize="small"}}{{/link-to}} {{#link-to 'adminUser' approvedBy}}{{approvedBy.username}}{{/link-to}} {{else}} - {{i18n no_value}} + {{i18n 'no_value'}} {{/if}}
{{#if approved}} - {{i18n admin.user.approve_success}} + {{i18n 'admin.user.approve_success'}} {{else}} {{#if can_approve}} {{/if}} {{/if}} @@ -189,31 +189,31 @@ {{/if}}
-
{{i18n admin.users.active}}
+
{{i18n 'admin.users.active'}}
{{#if active}} - {{i18n yes_value}} + {{i18n 'yes_value'}} {{else}} - {{i18n no_value}} + {{i18n 'no_value'}} {{/if}}
{{#if active}} {{#if can_deactivate}} - - {{i18n admin.user.deactivate_explanation}} + + {{i18n 'admin.user.deactivate_explanation'}} {{/if}} {{else}} {{#if can_send_activation_email}} {{/if}} {{#if can_activate}} {{/if}} {{/if}} @@ -221,57 +221,57 @@
-
{{i18n admin.api.key}}
+
{{i18n 'admin.api.key'}}
{{#if api_key}}
{{api_key.key}} - - + +
{{else}}
- +
{{/if}}
-
{{i18n admin.user.admin}}
+
{{i18n 'admin.user.admin'}}
{{admin}}
{{#if can_revoke_admin}} {{/if}} {{#if can_grant_admin}} {{/if}}
-
{{i18n admin.user.moderator}}
+
{{i18n 'admin.user.moderator'}}
{{moderator}}
{{#if can_revoke_moderation}} {{/if}} {{#if can_grant_moderation}} {{/if}}
@@ -279,7 +279,7 @@
-
{{i18n trust_level}}
+
{{i18n 'trust_level'}}
{{combo-box content=trustLevels value=trust_level nameProperty="detailedName"}} {{#if dirty}} @@ -292,36 +292,36 @@
{{#if model.canLockTrustLevel}} {{#if model.trust_level_locked}} - + {{else}} - + {{/if}} {{/if}} {{#if tl3Requirements}} - {{#link-to 'adminUser.tl3Requirements' this class="btn"}}{{i18n admin.user.trust_level_3_requirements}}{{/link-to}} + {{#link-to 'adminUser.tl3Requirements' this class="btn"}}{{i18n 'admin.user.trust_level_3_requirements'}}{{/link-to}} {{/if}}
-
{{i18n admin.user.suspended}}
+
{{i18n 'admin.user.suspended'}}
{{isSuspended}}
{{#if isSuspended}} {{suspendDuration}} - {{i18n admin.user.suspended_explanation}} + {{i18n 'admin.user.suspended_explanation'}} {{else}} {{#if canSuspend}} - {{i18n admin.user.suspended_explanation}} + {{i18n 'admin.user.suspended_explanation'}} {{/if}} {{/if}}
@@ -329,64 +329,64 @@ {{#if isSuspended}}
-
{{i18n admin.user.suspended_by}}
+
{{i18n 'admin.user.suspended_by'}}
{{#link-to 'adminUser' suspendedBy}}{{avatar suspendedBy imageSize="tiny"}}{{/link-to}} {{#link-to 'adminUser' suspendedBy}}{{suspendedBy.username}}{{/link-to}}
- {{i18n admin.user.suspend_reason}}: + {{i18n 'admin.user.suspend_reason'}}: {{suspend_reason}}
{{/if}}
-
{{i18n admin.user.blocked}}
+
{{i18n 'admin.user.blocked'}}
{{blocked}}
{{#if blocked}} - {{i18n admin.user.block_explanation}} + {{i18n 'admin.user.block_explanation'}} {{/if}}
-

{{i18n admin.user.activity}}

+

{{i18n 'admin.user.activity'}}

-
{{i18n created}}
+
{{i18n 'created'}}
{{{created_at_age}}}
-
{{i18n admin.users.last_emailed}}
+
{{i18n 'admin.users.last_emailed'}}
{{{last_emailed_age}}}
-
{{i18n last_seen}}
+
{{i18n 'last_seen'}}
{{{last_seen_age}}}
-
{{i18n admin.user.like_count}}
+
{{i18n 'admin.user.like_count'}}
{{like_given_count}} / {{like_count}}
-
{{i18n admin.user.topics_entered}}
+
{{i18n 'admin.user.topics_entered'}}
{{topics_entered}}
-
{{i18n admin.user.post_count}}
+
{{i18n 'admin.user.post_count'}}
{{post_count}}
{{#if can_delete_all_posts}} {{else}} {{deleteAllPostsExplanation}} @@ -394,54 +394,54 @@
-
{{i18n admin.user.posts_read_count}}
+
{{i18n 'admin.user.posts_read_count'}}
{{posts_read_count}}
-
{{i18n admin.user.warnings_received_count}}
+
{{i18n 'admin.user.warnings_received_count'}}
{{warnings_received_count}}
-
{{i18n admin.user.flags_given_received_count}}
+
{{i18n 'admin.user.flags_given_received_count'}}
{{flags_given_count}} / {{flags_received_count}}
-
{{i18n admin.user.private_topics_count}}
+
{{i18n 'admin.user.private_topics_count'}}
{{private_topics_count}}
-
{{i18n admin.user.time_read}}
+
{{i18n 'admin.user.time_read'}}
{{{time_read}}}
-
{{i18n user.invited.days_visited}}
+
{{i18n 'user.invited.days_visited'}}
{{{days_visited}}}
{{#if single_sign_on_record}}
-

{{i18n admin.user.sso.title}}

+

{{i18n 'admin.user.sso.title'}}

{{#with single_sign_on_record}}
-
{{i18n admin.user.sso.external_id}}
+
{{i18n 'admin.user.sso.external_id'}}
{{external_id}}
-
{{i18n admin.user.sso.external_username}}
+
{{i18n 'admin.user.sso.external_username'}}
{{external_username}}
-
{{i18n admin.user.sso.external_name}}
+
{{i18n 'admin.user.sso.external_name'}}
{{external_name}}
-
{{i18n admin.user.sso.external_email}}
+
{{i18n 'admin.user.sso.external_email'}}
{{external_email}}
-
{{i18n admin.user.sso.external_avatar_url}}
+
{{i18n 'admin.user.sso.external_avatar_url'}}
{{external_avatar_url}}
{{/with}} @@ -452,7 +452,7 @@
{{#if deleteExplanation}}
diff --git a/app/assets/javascripts/admin/templates/user_tl3_requirements.hbs b/app/assets/javascripts/admin/templates/user_tl3_requirements.hbs index 7bb228867b..db924081e5 100644 --- a/app/assets/javascripts/admin/templates/user_tl3_requirements.hbs +++ b/app/assets/javascripts/admin/templates/user_tl3_requirements.hbs @@ -2,104 +2,102 @@
-

{{username}} - {{i18n admin.user.tl3_requirements.title}}

+

{{username}} - {{i18n 'admin.user.tl3_requirements.title'}}


-

{{i18n admin.user.tl3_requirements.table_title}}

+

{{i18n 'admin.user.tl3_requirements.table_title'}}

- {{#with tl3Requirements}} -
{{i18n admin.badges.badge}}{{i18n admin.badges.granted_by}}{{i18n admin.badges.granted_at}}{{i18n 'admin.badges.badge'}}{{i18n 'admin.badges.granted_by'}}{{i18n 'admin.badges.granted_at'}}
{{age-with-tooltip granted_at}} - +
-

{{i18n admin.badges.no_user_badges name=user.username}}

+

{{i18n 'admin.badges.no_user_badges' name=user.username}}

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{{i18n admin.user.tl3_requirements.value_heading}}{{i18n admin.user.tl3_requirements.requirement_heading}}
{{i18n admin.user.tl3_requirements.visits}} - {{days_visited_percent}}% ({{days_visited}} / {{time_period}} {{i18n admin.user.tl3_requirements.days}}) - {{min_days_visited_percent}}%
{{i18n admin.user.tl3_requirements.topics_replied_to}}{{num_topics_replied_to}}{{min_topics_replied_to}}
{{i18n admin.user.tl3_requirements.topics_viewed}}{{topics_viewed}}{{min_topics_viewed}}
{{i18n admin.user.tl3_requirements.topics_viewed_all_time}}{{topics_viewed_all_time}}{{min_topics_viewed_all_time}}
{{i18n admin.user.tl3_requirements.posts_read}}{{posts_read}}{{min_posts_read}}
{{i18n admin.user.tl3_requirements.posts_read_all_time}}{{posts_read_all_time}}{{min_posts_read_all_time}}
{{i18n admin.user.tl3_requirements.flagged_posts}}{{num_flagged_posts}}{{i18n max_of_count count=max_flagged_posts}}
{{i18n admin.user.tl3_requirements.flagged_by_users}}{{num_flagged_by_users}}{{i18n max_of_count count=max_flagged_by_users}}
{{i18n admin.user.tl3_requirements.likes_given}}{{num_likes_given}}{{min_likes_given}}
{{i18n admin.user.tl3_requirements.likes_received}}{{num_likes_received}}{{min_likes_received}}
{{i18n admin.user.tl3_requirements.likes_received_days}}{{num_likes_received_days}}{{min_likes_received_days}}
{{i18n admin.user.tl3_requirements.likes_received_users}}{{num_likes_received_users}}{{min_likes_received_users}}
- {{/with}} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{{i18n 'admin.user.tl3_requirements.value_heading'}}{{i18n 'admin.user.tl3_requirements.requirement_heading'}}
{{i18n 'admin.user.tl3_requirements.visits'}} + {{tl3Requirements.days_visited_percent}}% ({{tl3Requirements.days_visited}} / {{tl3Requirements.time_period}} {{i18n 'admin.user.tl3_requirements.days'}}) + {{tl3Requirements.min_days_visited_percent}}%
{{i18n 'admin.user.tl3_requirements.topics_replied_to'}}{{tl3Requirements.num_topics_replied_to}}{{tl3Requirements.min_topics_replied_to}}
{{i18n 'admin.user.tl3_requirements.topics_viewed'}}{{tl3Requirements.topics_viewed}}{{tl3Requirements.min_topics_viewed}}
{{i18n 'admin.user.tl3_requirements.topics_viewed_all_time'}}{{tl3Requirements.topics_viewed_all_time}}{{tl3Requirements.min_topics_viewed_all_time}}
{{i18n 'admin.user.tl3_requirements.posts_read'}}{{tl3Requirements.posts_read}}{{tl3Requirements.min_posts_read}}
{{i18n 'admin.user.tl3_requirements.posts_read_all_time'}}{{tl3Requirements.posts_read_all_time}}{{tl3Requirements.min_posts_read_all_time}}
{{i18n 'admin.user.tl3_requirements.flagged_posts'}}{{tl3Requirements.num_flagged_posts}}{{i18n 'max_of_count' count=tl3Requirements.max_flagged_posts}}
{{i18n 'admin.user.tl3_requirements.flagged_by_users'}}{{tl3Requirements.num_flagged_by_users}}{{i18n 'max_of_count' count=tl3Requirements.max_flagged_by_users}}
{{i18n 'admin.user.tl3_requirements.likes_given'}}{{tl3Requirements.num_likes_given}}{{tl3Requirements.min_likes_given}}
{{i18n 'admin.user.tl3_requirements.likes_received'}}{{tl3Requirements.num_likes_received}}{{tl3Requirements.min_likes_received}}
{{i18n 'admin.user.tl3_requirements.likes_received_days'}}{{tl3Requirements.num_likes_received_days}}{{tl3Requirements.min_likes_received_days}}
{{i18n 'admin.user.tl3_requirements.likes_received_users'}}{{tl3Requirements.num_likes_received_users}}{{tl3Requirements.min_likes_received_users}}

@@ -107,31 +105,31 @@ {{#if tl3Requirements.requirements_lost}} {{! tl implicitly not locked }} {{#if tl3Requirements.on_grace_period}} - {{i18n admin.user.tl3_requirements.on_grace_period}} + {{i18n 'admin.user.tl3_requirements.on_grace_period'}} {{else}} {{! not on grace period }} - {{i18n admin.user.tl3_requirements.does_not_qualify}} - {{i18n admin.user.tl3_requirements.will_be_demoted}} + {{i18n 'admin.user.tl3_requirements.does_not_qualify'}} + {{i18n 'admin.user.tl3_requirements.will_be_demoted'}} {{/if}} {{else}} {{! requirements not lost - remains tl3 }} {{#if tl3Requirements.trust_level_locked}} - {{i18n admin.user.tl3_requirements.locked_will_not_be_demoted}} + {{i18n 'admin.user.tl3_requirements.locked_will_not_be_demoted'}} {{else}} {{! tl not locked }} - {{i18n admin.user.tl3_requirements.qualifies}} + {{i18n 'admin.user.tl3_requirements.qualifies'}} {{#if tl3Requirements.on_grace_period}} - {{i18n admin.user.tl3_requirements.on_grace_period}} + {{i18n 'admin.user.tl3_requirements.on_grace_period'}} {{/if}} {{/if}} {{/if}} {{else}} {{! is not tl3 }} {{#if tl3Requirements.requirements_met}} {{! met & not tl3 - will be promoted}} - {{i18n admin.user.tl3_requirements.qualifies}} - {{i18n admin.user.tl3_requirements.will_be_promoted}} + {{i18n 'admin.user.tl3_requirements.qualifies'}} + {{i18n 'admin.user.tl3_requirements.will_be_promoted'}} {{else}} {{! requirements not met - remains regular }} {{#if tl3Requirements.trust_level_locked}} - {{i18n admin.user.tl3_requirements.locked_will_not_be_promoted}} + {{i18n 'admin.user.tl3_requirements.locked_will_not_be_promoted'}} {{else}} - {{i18n admin.user.tl3_requirements.does_not_qualify}} + {{i18n 'admin.user.tl3_requirements.does_not_qualify'}} {{/if}} {{/if}} {{/if}} diff --git a/app/assets/javascripts/admin/templates/users-list-show.hbs b/app/assets/javascripts/admin/templates/users-list-show.hbs new file mode 100644 index 0000000000..750685509a --- /dev/null +++ b/app/assets/javascripts/admin/templates/users-list-show.hbs @@ -0,0 +1,84 @@ +{{#if hasSelection}} +

+ + +
+{{/if}} + +
+
+

{{title}}

+
+
+
+ {{text-field value=listFilter placeholder=searchHint}} + {{#unless showEmails}} +
+ +
+ {{/unless}} +
+ +{{#loading-spinner condition=refreshing}} + {{#if model}} + + + {{#if showApproval}} + + {{/if}} + + + + + + + + + + {{#if showApproval}} + + {{/if}} + + + + {{#each user in model}} + + {{#if controller.showApproval}} + + {{/if}} + + + + + + + + + + + + {{#if showApproval}} + + {{/if}} + + + {{/each}} + +
{{input type="checkbox" checked=selectAll}} {{i18n 'username'}}{{i18n 'email'}}{{i18n 'admin.users.last_emailed'}}{{i18n 'last_seen'}}{{i18n 'admin.user.topics_entered'}}{{i18n 'admin.user.posts_read_count'}}{{i18n 'admin.user.time_read'}}{{i18n 'created'}}{{i18n 'admin.users.approved'}} 
+ {{#if user.can_approve}} + {{input type="checkbox" checked=user.selected}} + {{/if}} + {{avatar user imageSize="small"}}{{#link-to 'adminUser' user}}{{unbound user.username}}{{/link-to}}{{{unbound user.email}}}{{{unbound user.last_emailed_age}}}{{{unbound user.last_seen_age}}}{{{unbound user.topics_entered}}}{{{unbound user.posts_read_count}}}{{{unbound user.time_read}}}{{{unbound user.created_at_age}}} + {{#if user.approved}} + {{i18n 'yes_value'}} + {{else}} + {{i18n 'no_value'}} + {{/if}} + + {{#if user.admin}}{{/if}} + {{#if user.moderator}}{{/if}} +
+ {{else}} +

{{i18n 'search.no_results'}}

+ {{/if}} +{{/loading-spinner}} diff --git a/app/assets/javascripts/admin/templates/users_list.hbs b/app/assets/javascripts/admin/templates/users_list.hbs index c4ab6dac64..45879d1b6f 100644 --- a/app/assets/javascripts/admin/templates/users_list.hbs +++ b/app/assets/javascripts/admin/templates/users_list.hbs @@ -1,103 +1,23 @@
-
- {{text-field value=username placeholder=searchHint}} -
- + {{d-button action="sendInvites" title="admin.invite.button_title" icon="envelope" label="admin.invite.button_text"}} + {{d-button action="exportUsers" title="admin.export_csv.button_title.user" icon="download" label="admin.export_csv.button_text"}}
- {{#if hasSelection}} -
- - -
- {{/if}} - -
-
-

{{title}}

-
-
- -
-
- - {{#loading-spinner condition=loading}} - {{#if model.length}} - - - {{#if showApproval}} - - {{/if}} - - - - - - - - - - {{#if showApproval}} - - {{/if}} - - - - {{#each model}} - - {{#if controller.showApproval}} - - {{/if}} - - - - - - - - - - - - {{#if showApproval}} - - {{/if}} - - {{/each}} - -
{{view Ember.Checkbox checkedBinding="selectAll"}} {{i18n username}}{{i18n email}}{{i18n admin.users.last_emailed}}{{i18n last_seen}}{{i18n admin.user.topics_entered}}{{i18n admin.user.posts_read_count}}{{i18n admin.user.time_read}}{{i18n created}}{{i18n admin.users.approved}} 
- {{#if can_approve}} - {{view Ember.Checkbox checkedBinding="selected"}} - {{/if}} - {{#link-to 'adminUser' this}}{{avatar this imageSize="small"}}{{/link-to}}{{#link-to 'adminUser' this}}{{unbound username}}{{/link-to}}{{{unbound email}}}{{{unbound last_emailed_age}}}{{{unbound last_seen_age}}}{{{unbound topics_entered}}}{{{unbound posts_read_count}}}{{{unbound time_read}}}{{{unbound created_at_age}}} - {{#if approved}} - {{i18n yes_value}} - {{else}} - {{i18n no_value}} - {{/if}} - - {{#if admin}}{{/if}} - {{#if moderator}}{{/if}} - -
- {{else}} -

{{i18n search.no_results}}

- {{/if}} - {{/loading-spinner}} + {{outlet}}
diff --git a/app/assets/javascripts/admin/templates/version_checks.hbs b/app/assets/javascripts/admin/templates/version_checks.hbs index 49009e47dc..fefee6fec5 100644 --- a/app/assets/javascripts/admin/templates/version_checks.hbs +++ b/app/assets/javascripts/admin/templates/version_checks.hbs @@ -5,15 +5,15 @@ {{custom-html 'upgrade-header'}}   - {{i18n admin.dashboard.installed_version}} - {{i18n admin.dashboard.latest_version}} + {{i18n 'admin.dashboard.installed_version'}} + {{i18n 'admin.dashboard.latest_version'}}     {{#unless loading}} - {{i18n admin.dashboard.version}} + {{i18n 'admin.dashboard.version'}} {{ versionCheck.installed_version }} {{#if versionCheck.noCheckPerformed}} @@ -22,7 +22,7 @@ - {{i18n admin.dashboard.no_check_performed}} + {{i18n 'admin.dashboard.no_check_performed'}} {{else}} {{#if versionCheck.staleData}} @@ -37,9 +37,9 @@ {{#if versionCheck.version_check_pending}} - {{i18n admin.dashboard.version_check_pending}} + {{i18n 'admin.dashboard.version_check_pending'}} {{else}} - {{i18n admin.dashboard.stale_data}} + {{i18n 'admin.dashboard.stale_data'}} {{/if}} @@ -60,11 +60,11 @@ {{#if versionCheck.upToDate }} - {{i18n admin.dashboard.up_to_date}} + {{i18n 'admin.dashboard.up_to_date'}} {{else}} - {{i18n admin.dashboard.critical_available}} - {{i18n admin.dashboard.updates_available}} - {{i18n admin.dashboard.please_upgrade}} + {{i18n 'admin.dashboard.critical_available'}} + {{i18n 'admin.dashboard.updates_available'}} + {{i18n 'admin.dashboard.please_upgrade'}} {{/if}} {{/if}} diff --git a/app/assets/javascripts/admin/views/ace_editor_view.js b/app/assets/javascripts/admin/views/ace_editor_view.js index e25a49e421..b598e5d1e3 100644 --- a/app/assets/javascripts/admin/views/ace_editor_view.js +++ b/app/assets/javascripts/admin/views/ace_editor_view.js @@ -35,18 +35,19 @@ Discourse.AceEditorView = Discourse.View.extend({ didInsertElement: function() { - var aceEditorView = this; + var self = this; var initAce = function() { - aceEditorView.editor = ace.edit(aceEditorView.$('.ace')[0]); - aceEditorView.editor.setTheme("ace/theme/chrome"); - aceEditorView.editor.setShowPrintMargin(false); - aceEditorView.editor.getSession().setMode("ace/mode/" + (aceEditorView.get('mode'))); - aceEditorView.editor.on("change", function() { - aceEditorView.skipContentChangeEvent = true; - aceEditorView.set('content', aceEditorView.editor.getSession().getValue()); - aceEditorView.skipContentChangeEvent = false; + self.editor = ace.edit(self.$('.ace')[0]); + self.editor.setTheme("ace/theme/chrome"); + self.editor.setShowPrintMargin(false); + self.editor.getSession().setMode("ace/mode/" + (self.get('mode'))); + self.editor.on("change", function() { + self.skipContentChangeEvent = true; + self.set('content', self.editor.getSession().getValue()); + self.skipContentChangeEvent = false; }); + self.$().data('editor', self.editor); }; if (window.ace) { diff --git a/app/assets/javascripts/admin/views/admin-backups-logs.js.es6 b/app/assets/javascripts/admin/views/admin-backups-logs.js.es6 index 49d24732e2..a7c2af453b 100644 --- a/app/assets/javascripts/admin/views/admin-backups-logs.js.es6 +++ b/app/assets/javascripts/admin/views/admin-backups-logs.js.es6 @@ -9,7 +9,7 @@ export default Discourse.View.extend({ this.setProperties({ formattedLogs: "", index: 0 }); }, - _updateFormattedLogs: function() { + _updateFormattedLogs: Discourse.debounce(function() { var logs = this.get("controller.model"); if (logs.length === 0) { this._reset(); // reset the cached logs whenever the model is reset @@ -17,7 +17,7 @@ export default Discourse.View.extend({ // do the log formatting only once for HELLish performance var formattedLogs = this.get("formattedLogs"); for (var i = this.get("index"), length = logs.length; i < length; i++) { - var date = moment(logs[i].get("timestamp")).format("YYYY-MM-DD HH:mm:ss"), + var date = logs[i].get("timestamp"), message = Handlebars.Utils.escapeExpression(logs[i].get("message")); formattedLogs += "[" + date + "] " + message + "\n"; } @@ -26,7 +26,7 @@ export default Discourse.View.extend({ // force rerender this.rerender(); } - }.observes("controller.model.@each"), + }, 150).observes("controller.model.@each"), render: function(buffer) { var formattedLogs = this.get("formattedLogs"); diff --git a/app/assets/javascripts/admin/views/admin-flags-list.js.es6 b/app/assets/javascripts/admin/views/admin-flags-list.js.es6 index b3035fe742..0f720b1e56 100644 --- a/app/assets/javascripts/admin/views/admin-flags-list.js.es6 +++ b/app/assets/javascripts/admin/views/admin-flags-list.js.es6 @@ -1,4 +1,6 @@ -export default Discourse.View.extend(Discourse.LoadMore, { +import LoadMore from "discourse/mixins/load-more"; + +export default Discourse.View.extend(LoadMore, { loading: false, eyelineSelector: '.admin-flags tbody tr', diff --git a/app/assets/javascripts/admin/views/admin_customize_view.js b/app/assets/javascripts/admin/views/admin_customize_view.js index ac1b0603c0..37ecfaceb1 100644 --- a/app/assets/javascripts/admin/views/admin_customize_view.js +++ b/app/assets/javascripts/admin/views/admin_customize_view.js @@ -12,33 +12,57 @@ Discourse.AdminCustomizeView = Discourse.View.extend({ templateName: 'admin/templates/customize', classNames: ['customize'], selected: 'stylesheet', + mobile: false, - headerActive: Em.computed.equal('selected', 'header'), - footerActive: Em.computed.equal('selected', 'footer'), stylesheetActive: Em.computed.equal('selected', 'stylesheet'), - mobileHeaderActive: Em.computed.equal('selected', 'mobileHeader'), - mobileFooterActive: Em.computed.equal('selected', 'mobileFooter'), - mobileStylesheetActive: Em.computed.equal('selected', 'mobileStylesheet'), + headerActive: Em.computed.equal('selected', 'header'), + topActive: Em.computed.equal('selected', 'top'), + footerActive: Em.computed.equal('selected', 'footer'), + headTagActive: Em.computed.equal('selected', 'head_tag'), + bodyTagActive: Em.computed.equal('selected', 'body_tag'), + + mobileStylesheetActive: Em.computed.equal('selected', 'mobile_stylesheet'), + mobileHeaderActive: Em.computed.equal('selected', 'mobile_header'), + mobileTopActive: Em.computed.equal('selected', 'mobile_top'), + mobileFooterActive: Em.computed.equal('selected', 'mobile_footer'), actions: { - selectHeader: function() { this.set('selected', 'header'); }, - selectFooter: function() { this.set('selected', 'footer'); }, - selectStylesheet: function() { this.set('selected', 'stylesheet'); }, - selectMobileHeader: function() { this.set('selected', 'mobileHeader'); }, - selectMobileFooter: function() { this.set('selected', 'mobileFooter'); }, - selectMobileStylesheet: function() { this.set('selected', 'mobileStylesheet'); } + toggleMobile: function() { + // auto-select best tab + var tab = this.get("selected"); + if (/_tag$/.test(tab)) { tab = "stylesheet"; } + if (this.get("mobile")) { tab = tab.replace("mobile_", ""); } + else { tab = "mobile_" + tab; } + this.set("selected", tab); + // toggle mobile + this.toggleProperty("mobile"); + }, + + select: function(tab) { + this.set('selected', tab); + }, + + toggleMaximize: function() { + this.set("maximized", !this.get("maximized")); + + Em.run.scheduleOnce('afterRender', this, function(){ + $('.ace-wrapper').each(function(){ + $(this).data("editor").resize(); + }); + }); + }, }, - didInsertElement: function() { + _init: function() { var controller = this.get('controller'); Mousetrap.bindGlobal('mod+s', function() { controller.send("save"); return false; }); - }, + }.on("didInsertElement"), - willDestroyElement: function() { + _cleanUp: function() { Mousetrap.unbindGlobal('mod+s'); - } + }.on("willDestroyElement") }); diff --git a/app/assets/javascripts/admin/views/group-member.js.es6 b/app/assets/javascripts/admin/views/group-member.js.es6 new file mode 100644 index 0000000000..7889fd59cf --- /dev/null +++ b/app/assets/javascripts/admin/views/group-member.js.es6 @@ -0,0 +1,4 @@ +export default Discourse.View.extend({ + classNames: ["item"], + templateName: "admin/templates/group_member" +}); diff --git a/app/assets/javascripts/admin/views/site_setting_view.js b/app/assets/javascripts/admin/views/site_setting_view.js index 0a3983bd24..4f5719d17b 100644 --- a/app/assets/javascripts/admin/views/site_setting_view.js +++ b/app/assets/javascripts/admin/views/site_setting_view.js @@ -9,6 +9,16 @@ Discourse.SiteSettingView = Discourse.View.extend(Discourse.ScrollTop, { classNameBindings: [':row', ':setting', 'content.overridden'], + preview: function() { + var preview = this.get('content.preview'); + if(preview){ + return new Handlebars.SafeString("
" + + preview.replace("{{value}}",this.get('content.value')) + + "
" + ); + } + }.property('content.value'), + templateName: function() { // If we're editing a boolean, show a checkbox if (this.get('content.type') === 'bool') return 'admin/templates/site_settings/setting_bool'; diff --git a/app/assets/javascripts/application.js.erb b/app/assets/javascripts/application.js.erb index 89ec703135..64a1f7dd39 100644 --- a/app/assets/javascripts/application.js.erb +++ b/app/assets/javascripts/application.js.erb @@ -6,4 +6,16 @@ require_asset ("./main_include.js") DiscoursePluginRegistry.javascripts.each { |js| require_asset(js) } DiscoursePluginRegistry.handlebars.each { |hb| require_asset(hb) } +# Load any glob dependencies +DiscoursePluginRegistry.asset_globs.each do |g| + root, extension = *g + Dir.glob("#{root}/**/*") do |f| + if File.directory?(f) + depend_on(f) + elsif f.to_s.end_with?(".#{extension}") + require_asset(f) + end + end +end + %> diff --git a/app/assets/javascripts/discourse.js b/app/assets/javascripts/discourse.js index 27b12c53ab..c426b54e65 100644 --- a/app/assets/javascripts/discourse.js +++ b/app/assets/javascripts/discourse.js @@ -1,18 +1,13 @@ /*global Favcount:true*/ - -/** - The main Discourse Application - - @class Discourse - @extends Ember.Application -**/ var DiscourseResolver = require('discourse/ember/resolver').default; window.Discourse = Ember.Application.createWithMixins(Discourse.Ajax, { rootElement: '#main', - _docTitle: null, + _docTitle: document.title, getURL: function(url) { + if (!url) { return url; } + // If it's a non relative URL, return it. if (url.indexOf('http') === 0) return url; @@ -24,6 +19,12 @@ window.Discourse = Ember.Application.createWithMixins(Discourse.Ajax, { return u + url; }, + getURLWithCDN: function(url) { + url = this.getURL(url); + if (Discourse.CDN) { url = Discourse.CDN + url; } + return url; + }, + Resolver: DiscourseResolver, _titleChanged: function() { @@ -40,13 +41,7 @@ window.Discourse = Ember.Application.createWithMixins(Discourse.Ajax, { title = "(" + notifyCount + ") " + title; } - if (title !== document.title) { - // chrome bug workaround see: http://stackoverflow.com/questions/2952384/changing-the-window-title-when-focussing-the-window-doesnt-work-in-chrome - window.setTimeout(function() { - document.title = "."; - document.title = title; - }, 200); - } + document.title = title; }.observes('_docTitle', 'hasFocus', 'notifyCount'), faviconChanged: function() { diff --git a/app/assets/javascripts/discourse/adapters/rest.js.es6 b/app/assets/javascripts/discourse/adapters/rest.js.es6 new file mode 100644 index 0000000000..a01491da97 --- /dev/null +++ b/app/assets/javascripts/discourse/adapters/rest.js.es6 @@ -0,0 +1,46 @@ +const ADMIN_MODELS = ['plugin']; + +function plural(type) { + return type + 's'; +} + +function pathFor(type) { + const path = "/" + plural(type); + + if (ADMIN_MODELS.indexOf(type) !== -1) { + return "/admin/" + path; + } + + return path; +} + +const _identityMap = {}; + +export default Ember.Object.extend({ + findAll(type) { + var self = this; + return Discourse.ajax(pathFor(type)).then(function(result) { + return result[plural(type)].map(obj => self._hydrate(type, obj)); + }); + }, + + _hydrate(type, obj) { + if (!obj) { throw "Can't hydrate " + type + " of `null`"; } + if (!obj.id) { throw "Can't hydrate " + type + " without an `id`"; } + + _identityMap[type] = _identityMap[type] || {}; + + const existing = _identityMap[type][obj.id]; + if (existing) { + delete obj.id; + existing.setProperties(obj); + return existing; + } + + const klass = this.container.lookupFactory('model:' + type) || Ember.Object; + const model = klass.create(obj); + _identityMap[type][obj.id] = model; + return model; + } + +}); diff --git a/app/assets/javascripts/discourse/components/auto-close-form.js.es6 b/app/assets/javascripts/discourse/components/auto-close-form.js.es6 index edd56818e4..f203799b22 100644 --- a/app/assets/javascripts/discourse/components/auto-close-form.js.es6 +++ b/app/assets/javascripts/discourse/components/auto-close-form.js.es6 @@ -28,7 +28,7 @@ export default Ember.Component.extend({ // only # of hours in limited mode return t.match(/^(\d+\.)?\d+$/); } else { - if (t.match(/^\d{4}-\d{1,2}-\d{1,2} \d{1,2}:\d{2}(\s?[AP]M)?$/i)) { + if (t.match(/^\d{4}-\d{1,2}-\d{1,2}(?: \d{1,2}:\d{2}(\s?[AP]M)?){0,1}$/i)) { // timestamp must be in the future return moment(t).isAfter(); } else { diff --git a/app/assets/javascripts/discourse/components/avatar-uploader.js.es6 b/app/assets/javascripts/discourse/components/avatar-uploader.js.es6 index 353fb5a4c5..b67da9c65f 100644 --- a/app/assets/javascripts/discourse/components/avatar-uploader.js.es6 +++ b/app/assets/javascripts/discourse/components/avatar-uploader.js.es6 @@ -14,17 +14,23 @@ export default Em.Component.extend(UploadMixin, { uploadDone: function(data) { var self = this; - // indicates the users is using an uploaded avatar - this.set("custom_avatar_upload_id", data.result.upload_id); - // display a warning whenever the image is not a square this.set("imageIsNotASquare", data.result.width !== data.result.height); + // in order to be as much responsive as possible, we're cheating a bit here // indeed, the server gives us back the url to the file we've just uploaded // often, this file is not a square, so we need to crop it properly // this will also capture the first frame of animated avatars when they're not allowed Discourse.Utilities.cropAvatar(data.result.url, data.files[0].type).then(function(avatarTemplate) { self.set("uploadedAvatarTemplate", avatarTemplate); + + // indicates the users is using an uploaded avatar (must happen after cropping, otherwise + // we will attempt to load an invalid avatar and cache a redirect to old one, uploadedAvatarTemplate + // trumps over custom avatar upload id) + self.set("custom_avatar_upload_id", data.result.upload_id); }); + + // the upload is now done + this.sendAction("done"); } }); diff --git a/app/assets/javascripts/discourse/components/basic-topic-list.js.es6 b/app/assets/javascripts/discourse/components/basic-topic-list.js.es6 index 1b505c6130..448deec2c0 100644 --- a/app/assets/javascripts/discourse/components/basic-topic-list.js.es6 +++ b/app/assets/javascripts/discourse/components/basic-topic-list.js.es6 @@ -1,11 +1,3 @@ -/** - This view is used for rendering a basic list of topics. - - @class BasicTopicListComponent - @extends Discourse.View - @namespace Discourse - @module Discourse -**/ export default Ember.Component.extend({ loading: Ember.computed.not('loaded'), @@ -38,12 +30,6 @@ export default Ember.Component.extend({ // Without a topic list, we assume it's loaded always. this.set('loaded', true); } - }, - - actions: { - clickedPosts: function(data) { - this.sendAction('postsAction', data); - } } }); diff --git a/app/assets/javascripts/discourse/components/category-drop.js.es6 b/app/assets/javascripts/discourse/components/category-drop.js.es6 index b05b9dcd77..faa87ea45c 100644 --- a/app/assets/javascripts/discourse/components/category-drop.js.es6 +++ b/app/assets/javascripts/discourse/components/category-drop.js.es6 @@ -1,13 +1,12 @@ -/** - Renders a drop down for selecting a category +var get = Ember.get; - @class CategoryDropComponent - @extends Ember.Component - @namespace Discourse - @module Discourse -**/ export default Ember.Component.extend({ - classNameBindings: ['category::no-category', 'categories:has-drop'], + classNameBindings: ['category::no-category', 'categories:has-drop','categoryStyle'], + + categoryStyle: function(){ + return Discourse.SiteSettings.category_style; + }.property(), + tagName: 'li', iconClass: function() { @@ -42,13 +41,38 @@ export default Ember.Component.extend({ return result; }.property('category'), + categoryColor: function() { + var category = this.get('category'); + + if (category) { + var color = get(category, 'color'); + + if (color) { + var style = ""; + if (color) { style += "background-color: #" + color + ";" } + return style; + } + } + + return "background-color: #eee;"; + }.property('category'), + badgeStyle: function() { var category = this.get('category'); + if (category) { - return Discourse.HTML.categoryStyle(category); - } else { - return "background-color: #eee; color: #333"; + var color = get(category, 'color'), + textColor = get(category, 'text_color'); + + if (color || textColor) { + var style = ""; + if (color) { style += "background-color: #" + color + "; border-color: #" + color + ";"; } + if (textColor) { style += "color: #" + textColor + "; "; } + return style; + } } + + return "background-color: #eee; color: #333"; }.property('category'), clickEventName: function() { @@ -81,24 +105,33 @@ export default Ember.Component.extend({ self.close(); }); - $('html').on(this.get('clickEventName'), function(e) { - var $target = $(e.target), - closest = $target.closest($dropdown); + Em.run.next(function(){ + self.$('.cat a').add('html').on(self.get('clickEventName'), function(e) { + var $target = $(e.target), + closest = $target.closest($dropdown); - return ($(e.currentTarget).hasClass('badge-category') || (closest.length && closest[0] === $dropdown)) ? true : self.close(); + if ($(e.currentTarget).hasClass('badge-wrapper')){ + self.close(); + } + + return ($(e.currentTarget).hasClass('badge-category') || (closest.length && closest[0] === $dropdown)) ? true : self.close(); + }); }); } }, - close: function() { + removeEvents: function(){ $('html').off(this.get('clickEventName')); this.$('a[data-drop-close]').off('click.category-drop'); + }, + + close: function() { + this.removeEvents(); this.set('expanded', false); }, willDestroyElement: function() { - $('html').off(this.get('clickEventName')); - this.$('a[data-drop-close]').off('click.category-drop'); + this.removeEvents(); } }); diff --git a/app/assets/javascripts/discourse/components/category-group.js.es6 b/app/assets/javascripts/discourse/components/category-group.js.es6 index e6d719afe4..14caa32bfe 100644 --- a/app/assets/javascripts/discourse/components/category-group.js.es6 +++ b/app/assets/javascripts/discourse/components/category-group.js.es6 @@ -1,22 +1,10 @@ -// Compile and memoize a template -var compiled; -function templateFunction() { - compiled = compiled || Handlebars.compile("
" + - "" + - "
"); - return compiled; -} +import { categoryBadgeHTML } from 'discourse/helpers/category-link'; export default Ember.Component.extend({ - didInsertElement: function(){ + _initializeAutocomplete: function(){ var self = this; + var template = this.container.lookup('template:category-group-autocomplete.raw'); this.$('input').autocomplete({ items: this.get('categories'), @@ -37,10 +25,11 @@ export default Ember.Component.extend({ }); self.set("categories", categories); }, - template: templateFunction(), + template: template, transformComplete: function(category) { - return Discourse.HTML.categoryBadge(category, {allowUncategorized: true}); + return categoryBadgeHTML(category, {allowUncategorized: true}); } }); - } + }.on('didInsertElement') + }); diff --git a/app/assets/javascripts/discourse/components/category-title-link.js.es6 b/app/assets/javascripts/discourse/components/category-title-link.js.es6 index 6358476cbd..e00efa6836 100644 --- a/app/assets/javascripts/discourse/components/category-title-link.js.es6 +++ b/app/assets/javascripts/discourse/components/category-title-link.js.es6 @@ -3,19 +3,17 @@ export default Em.Component.extend({ render: function(buffer) { var category = this.get('category'), - logoUrl = category.get('logo_url'); + logoUrl = category.get('logo_url'), + categoryUrl = Discourse.getURL('/c/') + Discourse.Category.slugFor(category), + categoryName = Handlebars.Utils.escapeExpression(category.get('name')); - if (category.get('read_restricted')) { - buffer.push(" "); - } + if (category.get('read_restricted')) { buffer.push(""); } - buffer.push(""); + buffer.push(""); + buffer.push("" + categoryName + ""); + + if (!Em.isEmpty(logoUrl)) { buffer.push(""); } - var noLogo = Em.isEmpty(logoUrl); - buffer.push(Handlebars.Utils.escapeExpression(category.get('name'))); - if (!noLogo) { - buffer.push("
"); - } buffer.push("
"); } }); diff --git a/app/assets/javascripts/discourse/components/cdn-img.js.es6 b/app/assets/javascripts/discourse/components/cdn-img.js.es6 new file mode 100644 index 0000000000..007d9cb903 --- /dev/null +++ b/app/assets/javascripts/discourse/components/cdn-img.js.es6 @@ -0,0 +1,8 @@ +export default Ember.Component.extend({ + tagName: 'img', + attributeBindings: ['cdnSrc:src'], + + cdnSrc: function() { + return Discourse.getURLWithCDN(this.get('src')); + }.property('src') +}); diff --git a/app/assets/javascripts/discourse/components/conditional-loading-spinner.js.es6 b/app/assets/javascripts/discourse/components/conditional-loading-spinner.js.es6 index ca6d0386ae..19103360dd 100644 --- a/app/assets/javascripts/discourse/components/conditional-loading-spinner.js.es6 +++ b/app/assets/javascripts/discourse/components/conditional-loading-spinner.js.es6 @@ -1,8 +1,19 @@ export default Ember.Component.extend({ classNameBindings: ['containerClass'], - layoutName: 'components/conditional-loading-spinner', containerClass: function() { return (this.get('size') === 'small') ? 'inline-spinner' : undefined; - }.property('size') + }.property('size'), + + render: function(buffer) { + if (this.get('condition')) { + buffer.push('
'); + } else { + return this._super(); + } + }, + + _conditionChanged: function() { + this.rerender(); + }.observes('condition') }); diff --git a/app/assets/javascripts/discourse/components/d-button.js.es6 b/app/assets/javascripts/discourse/components/d-button.js.es6 new file mode 100644 index 0000000000..21ebedc8e8 --- /dev/null +++ b/app/assets/javascripts/discourse/components/d-button.js.es6 @@ -0,0 +1,36 @@ +import { iconHTML } from 'discourse/helpers/fa-icon'; + +export default Ember.Component.extend({ + tagName: 'button', + classNameBindings: [':btn'], + attributeBindings: ['disabled', 'translatedTitle:title'], + + translatedTitle: function() { + const title = this.get('title'); + return title ? I18n.t(title) : this.get('translatedLabel'); + }.property('title', 'translatedLabel'), + + translatedLabel: function() { + const label = this.get('label'); + if (label) { + return I18n.t(this.get('label')); + } + }.property('label'), + + render(buffer) { + const label = this.get('translatedLabel'), + icon = this.get('icon'); + + if (label || icon) { + if (icon) { buffer.push(iconHTML(icon) + ' '); } + if (label) { buffer.push(label); } + } else { + // If no label or icon is present, yield + return this._super(); + } + }, + + click() { + this.sendAction("action", this.get("actionParam")); + } +}); diff --git a/app/assets/javascripts/discourse/components/disabled-icon.js.es6 b/app/assets/javascripts/discourse/components/disabled-icon.js.es6 new file mode 100644 index 0000000000..abcd8e7279 --- /dev/null +++ b/app/assets/javascripts/discourse/components/disabled-icon.js.es6 @@ -0,0 +1,4 @@ +export default Ember.Component.extend({ + tagName: 'span', + classNameBindings: [':fa-stack'], +}); diff --git a/app/assets/javascripts/discourse/components/discourse-action-history.js.es6 b/app/assets/javascripts/discourse/components/discourse-action-history.js.es6 index d941bca62c..7dba3c5828 100644 --- a/app/assets/javascripts/discourse/components/discourse-action-history.js.es6 +++ b/app/assets/javascripts/discourse/components/discourse-action-history.js.es6 @@ -1,23 +1,16 @@ -/** - This component handles rendering of what actions have been taken on a post. It uses - buffer rendering for performance rather than a template. +import StringBuffer from 'discourse/mixins/string-buffer'; - @class ActionsHistoryComponent - @extends Em.Component - @namespace Discourse - @module Discourse -**/ -export default Em.Component.extend({ +export default Em.Component.extend(StringBuffer, { tagName: 'section', classNameBindings: [':post-actions', 'hidden'], actionsHistory: Em.computed.alias('post.actionsHistory'), emptyHistory: Em.computed.empty('actionsHistory'), hidden: Em.computed.and('emptyHistory', 'post.notDeleted'), - shouldRerender: Discourse.View.renderIfChanged('actionsHistory.@each', 'actionsHistory.users.length', 'post.deleted'), + + rerenderTriggers: ['actionsHistory.@each', 'actionsHistory.users.length', 'post.deleted'], // This was creating way too many bound ifs and subviews in the handlebars version. - render: function(buffer) { - + renderString: function(buffer) { if (!this.get('emptyHistory')) { this.get('actionsHistory').forEach(function(c) { buffer.push("
"); diff --git a/app/assets/javascripts/discourse/components/discourse-banner.js.es6 b/app/assets/javascripts/discourse/components/discourse-banner.js.es6 index 421510f072..a3f8f07d67 100644 --- a/app/assets/javascripts/discourse/components/discourse-banner.js.es6 +++ b/app/assets/javascripts/discourse/components/discourse-banner.js.es6 @@ -10,8 +10,8 @@ export default VisibleComponent.extend({ if (bannerKey) { bannerKey = parseInt(bannerKey, 10); } if (dismissedBannerKey) { dismissedBannerKey = parseInt(dismissedBannerKey, 10); } - return bannerKey && dismissedBannerKey !== bannerKey; - }.property("user.dismissed_banner_key", "banner.key"), + return !this.get("hide") && bannerKey && dismissedBannerKey !== bannerKey; + }.property("user.dismissed_banner_key", "banner.key", "hide"), actions: { dismiss: function () { diff --git a/app/assets/javascripts/discourse/components/emoji-uploader.js.es6 b/app/assets/javascripts/discourse/components/emoji-uploader.js.es6 new file mode 100644 index 0000000000..59f65b3d55 --- /dev/null +++ b/app/assets/javascripts/discourse/components/emoji-uploader.js.es6 @@ -0,0 +1,19 @@ +import UploadMixin from 'discourse/mixins/upload'; + +export default Em.Component.extend(UploadMixin, { + type: "emoji", + uploadUrl: "/admin/customize/emojis", + + hasName: Em.computed.notEmpty("name"), + addDisabled: Em.computed.not("hasName"), + + data: function() { + return Ember.isBlank(this.get("name")) ? {} : { name: this.get("name") }; + }.property("name"), + + uploadDone: function (data) { + this.set("name", null); + this.sendAction("done", data.result); + } + +}); diff --git a/app/assets/javascripts/discourse/components/global-notice.js.es6 b/app/assets/javascripts/discourse/components/global-notice.js.es6 index 7dca11a0cf..bde5b4aaa5 100644 --- a/app/assets/javascripts/discourse/components/global-notice.js.es6 +++ b/app/assets/javascripts/discourse/components/global-notice.js.es6 @@ -1,14 +1,19 @@ -export default Ember.Component.extend({ +import StringBuffer from 'discourse/mixins/string-buffer'; - shouldRerender: Discourse.View.renderIfChanged("site.isReadOnly"), +export default Ember.Component.extend(StringBuffer, { + rerenderTriggers: ['site.isReadOnly'], - render: function(buffer) { + renderString: function(buffer) { var notices = []; if (this.site.get("isReadOnly")) { notices.push(I18n.t("read_only_mode.enabled")); } + if (this.siteSettings.disable_emails) { + notices.push(I18n.t("emails_are_disabled")); + } + if (Discourse.User.currentProp('admin') && this.siteSettings.show_create_topics_notice) { var topic_count = 0, post_count = 0; diff --git a/app/assets/javascripts/discourse/components/group-selector.js.es6 b/app/assets/javascripts/discourse/components/group-selector.js.es6 index 8fe798f48a..e88505ca9f 100644 --- a/app/assets/javascripts/discourse/components/group-selector.js.es6 +++ b/app/assets/javascripts/discourse/components/group-selector.js.es6 @@ -1,29 +1,13 @@ -var compiled; - -function templateFunction() { - compiled = compiled || Handlebars.compile( - "
" + - "
    " + - "{{#each options}}" + - "
  • " + - "{{this.name}}" + - "
  • " + - "{{/each}}" + - "
" + - "
" - ); - return compiled; -} - -export default Em.Component.extend({ +export default Ember.Component.extend({ placeholder: function(){ return I18n.t(this.get("placeholderKey")); }.property("placeholderKey"), - didInsertElement: function() { + _initializeAutocomplete: function() { var self = this; var selectedGroups; + var template = this.container.lookup('template:group-selector-autocomplete.raw'); self.$('input').autocomplete({ allowAny: false, onChangeItems: function(items){ @@ -45,7 +29,7 @@ export default Em.Component.extend({ }); }); }, - template: templateFunction() + template: template }); - } + }.on('didInsertElement') }); diff --git a/app/assets/javascripts/discourse/components/header-extra-info.js.es6 b/app/assets/javascripts/discourse/components/header-extra-info.js.es6 new file mode 100644 index 0000000000..8948e24c95 --- /dev/null +++ b/app/assets/javascripts/discourse/components/header-extra-info.js.es6 @@ -0,0 +1,47 @@ +const TopicCategoryComponent = Ember.Component.extend({ + needsSecondRow: Ember.computed.gt('secondRowItems.length', 0), + secondRowItems: function() { return []; }.property(), + + showPrivateMessageGlyph: function() { + return !this.get('topic.is_warning') && this.get('topic.isPrivateMessage'); + }.property('topic.is_warning', 'topic.isPrivateMessage'), + + actions: { + jumpToTopPost() { + const topic = this.get('topic'); + if (topic) { + Discourse.URL.routeTo(topic.get('firstPostUrl')); + } + } + } + +}); + +let id = 0; + +// Allow us (and plugins) to register themselves as needing a second +// row in the header. If there is at least one thing in the second row +// the style changes to accomodate it. +function needsSecondRowIf(prop, cb) { + const rowId = "_second_row_" + (id++), + methodHash = {}; + + methodHash[id] = function() { + const secondRowItems = this.get('secondRowItems'), + propVal = this.get(prop); + if (cb.call(this, propVal)) { + secondRowItems.addObject(rowId); + } else { + secondRowItems.removeObject(rowId); + } + }.observes(prop).on('init'); + + TopicCategoryComponent.reopen(methodHash); +} + +needsSecondRowIf('topic.category', function(cat) { + return cat && (!cat.get('isUncategorizedCategory') || !this.siteSettings.suppress_uncategorized_badge); +}); + +export default TopicCategoryComponent; +export { needsSecondRowIf }; diff --git a/app/assets/javascripts/discourse/components/navigation-item.js.es6 b/app/assets/javascripts/discourse/components/navigation-item.js.es6 index ff5b01acd5..e66fdd276c 100644 --- a/app/assets/javascripts/discourse/components/navigation-item.js.es6 +++ b/app/assets/javascripts/discourse/components/navigation-item.js.es6 @@ -1,17 +1,11 @@ -/** - This view handles rendering of a navigation item +import StringBuffer from 'discourse/mixins/string-buffer'; - @class NavigationItemComponent - @extends Ember.Component - @namespace Discourse - @module Discourse -**/ -export default Ember.Component.extend({ +export default Ember.Component.extend(StringBuffer, { tagName: 'li', classNameBindings: ['active', 'content.hasIcon:has-icon'], attributeBindings: ['title'], hidden: Em.computed.not('content.visible'), - shouldRerender: Discourse.View.renderIfChanged('content.count'), + rerenderTriggers: ['content.count'], title: function() { var categoryName = this.get('content.categoryName'), @@ -42,7 +36,7 @@ export default Ember.Component.extend({ return I18n.t("filters." + name + ".title", extra); }.property('content.count'), - render: function(buffer) { + renderString: function(buffer) { var content = this.get('content'); buffer.push(""); if (content.get('hasIcon')) { diff --git a/app/assets/javascripts/discourse/components/password-field.js.es6 b/app/assets/javascripts/discourse/components/password-field.js.es6 index 902b0a651f..67fca9b9c9 100644 --- a/app/assets/javascripts/discourse/components/password-field.js.es6 +++ b/app/assets/javascripts/discourse/components/password-field.js.es6 @@ -3,16 +3,11 @@ import TextField from 'discourse/components/text-field'; /** Same as text-field, but with special features for a password input. Be sure to test on a variety of browsers and operating systems when changing this logic. - - @class PasswordFieldView - @extends Discourse.TextFieldView - @namespace Discourse - @module Discourse **/ export default TextField.extend({ canToggle: false, - keyPress: function(e) { + keyPress(e) { if ((e.which >= 65 && e.which <= 90 && !e.shiftKey) || (e.which >= 97 && e.which <= 122 && e.shiftKey)) { this.set('canToggle', true); this.set('capsLockOn', true); @@ -22,17 +17,17 @@ export default TextField.extend({ } }, - keyUp: function(e) { - if (e.which == 20 && this.get('canToggle')) { + keyUp(e) { + if (e.which === 20 && this.get('canToggle')) { this.toggleProperty('capsLockOn'); } }, - focusOut: function(e) { + focusOut() { this.set('capsLockOn', false); }, - focusIn: function() { + focusIn() { this.set('canToggle', false); // can't know the state of caps lock yet. keyPress will figure it out. } }); diff --git a/app/assets/javascripts/discourse/components/post-gap.js.es6 b/app/assets/javascripts/discourse/components/post-gap.js.es6 index 8cb313f6d1..38e40fa446 100644 --- a/app/assets/javascripts/discourse/components/post-gap.js.es6 +++ b/app/assets/javascripts/discourse/components/post-gap.js.es6 @@ -1,11 +1,3 @@ -/** - Handles a gap between posts with a click to load more - - @class PostGapComponent - @extends Ember.Component - @namespace Discourse - @module Discourse -**/ export default Ember.Component.extend({ classNameBindings: [':gap', 'gap::hidden'], diff --git a/app/assets/javascripts/discourse/components/post-gutter.js.es6 b/app/assets/javascripts/discourse/components/post-gutter.js.es6 index 6f263277d5..31f887adb8 100644 --- a/app/assets/javascripts/discourse/components/post-gutter.js.es6 +++ b/app/assets/javascripts/discourse/components/post-gutter.js.es6 @@ -1,8 +1,12 @@ var MAX_SHOWN = 5; -export default Em.Component.extend({ +import StringBuffer from 'discourse/mixins/string-buffer'; + +export default Em.Component.extend(StringBuffer, { classNameBindings: [':gutter'], + rerenderTriggers: ['expanded'], + // Roll up links to avoid duplicates collapsed: function() { var seen = {}, @@ -21,7 +25,7 @@ export default Em.Component.extend({ return result; }.property('links'), - render: function(buffer) { + renderString: function(buffer) { var links = this.get('collapsed'), toRender = links, collapsed = !this.get('expanded'); @@ -33,11 +37,11 @@ export default Em.Component.extend({ buffer.push("
diff --git a/app/assets/javascripts/discourse/templates/application.hbs b/app/assets/javascripts/discourse/templates/application.hbs index 8577bf11f3..6b9b133219 100644 --- a/app/assets/javascripts/discourse/templates/application.hbs +++ b/app/assets/javascripts/discourse/templates/application.hbs @@ -1,10 +1,14 @@ {{render "header"}} -
+
{{outlet}} {{render "user-card"}}
+{{#if showFooter}} + {{custom-html "footer"}} +{{/if}} + {{render "modal"}} {{render "topic-entrance"}} {{render "composer"}} diff --git a/app/assets/javascripts/discourse/templates/badges/index.hbs b/app/assets/javascripts/discourse/templates/badges/index.hbs index 5c8197ec3e..2d6de7fbda 100644 --- a/app/assets/javascripts/discourse/templates/badges/index.hbs +++ b/app/assets/javascripts/discourse/templates/badges/index.hbs @@ -1,23 +1,21 @@
-

{{i18n badges.title}}

+

{{i18n 'badges.title'}}

- {{#each badgeGroups}} + {{#each bg in badgeGroups}} - + - {{#each this.badges}} + {{#each b in bg.badges}} - - - - + + + + {{/each}} {{/each}}

{{this.badgeGrouping.displayName}}

{{bg.badgeGrouping.displayName}}

{{#if this.has_badge}}{{/if}}{{user-badge badge=this}}{{{displayDescriptionHtml}}}{{grant_count}}{{#if b.has_badge}}{{/if}}{{user-badge badge=b}}{{{b.displayDescriptionHtml}}}{{b.grant_count}}
- -{{custom-html "footer"}} diff --git a/app/assets/javascripts/discourse/templates/badges/show.hbs b/app/assets/javascripts/discourse/templates/badges/show.hbs index d36ae91417..c686df798e 100644 --- a/app/assets/javascripts/discourse/templates/badges/show.hbs +++ b/app/assets/javascripts/discourse/templates/badges/show.hbs @@ -1,6 +1,6 @@

- {{#link-to 'badges.index'}}{{i18n badges.title}}{{/link-to}} + {{#link-to 'badges.index'}}{{i18n 'badges.title'}}{{/link-to}} {{displayName}}

@@ -9,9 +9,9 @@ {{user-badge badge=this}} - {{displayDescription}} - {{i18n badges.granted count=grant_count}} - {{i18n badges.allow_title}} {{{view.allowTitle}}}
{{i18n badges.multiple_grant}} {{{view.multipleGrant}}} + {{{displayDescriptionHtml}}} + {{i18n 'badges.granted' count=grant_count}} + {{i18n 'badges.allow_title'}} {{{view.allowTitle}}}
{{i18n 'badges.multiple_grant'}} {{{view.multipleGrant}}} @@ -19,26 +19,23 @@ {{#if userBadges}}
- {{#each userBadges}} + {{#each ub in userBadges}}
- {{#link-to 'user' user classNames="badge-info"}} - {{avatar user imageSize="large"}} + {{#link-to 'user' ub.user classNames="badge-info"}} + {{avatar ub.user imageSize="large"}}
- {{user.username}} - {{format-date granted_at}} + {{ub.user.username}} + {{format-date ub.granted_at}}
{{/link-to}} - {{#if post_number}} - {{topic.title}} + {{#if ub.post_number}} + {{ub.topic.title}} {{/if}}
{{/each}}
{{loading-spinner condition=canLoadMore}} - {{#unless canLoadMore}} - {{custom-html "footer"}} - {{/unless}} {{/if}}
diff --git a/app/assets/javascripts/discourse/templates/category-group-autocomplete.raw.hbs b/app/assets/javascripts/discourse/templates/category-group-autocomplete.raw.hbs new file mode 100644 index 0000000000..76ba9ebddf --- /dev/null +++ b/app/assets/javascripts/discourse/templates/category-group-autocomplete.raw.hbs @@ -0,0 +1,7 @@ +
+ +
diff --git a/app/assets/javascripts/discourse/templates/choose_topic.hbs b/app/assets/javascripts/discourse/templates/choose_topic.hbs index c06bca4cc5..b13c977890 100644 --- a/app/assets/javascripts/discourse/templates/choose_topic.hbs +++ b/app/assets/javascripts/discourse/templates/choose_topic.hbs @@ -1,21 +1,21 @@ - + {{text-field value=view.topicTitle placeholderKey="choose_topic.title.placeholder" id="choose-topic-title"}} {{#if view.loading}} -

{{i18n loading}}

+

{{i18n 'loading'}}

{{else}} {{#if view.noResults}} -

{{i18n choose_topic.none_found}}

+

{{i18n 'choose_topic.none_found'}}

{{else}} - {{#each view.topics}} + {{#each t in view.topics}}
{{/each}} diff --git a/app/assets/javascripts/discourse/templates/components/admin-group-selector.hbs b/app/assets/javascripts/discourse/templates/components/admin-group-selector.hbs index f897810ab9..4856de5179 100644 --- a/app/assets/javascripts/discourse/templates/components/admin-group-selector.hbs +++ b/app/assets/javascripts/discourse/templates/components/admin-group-selector.hbs @@ -1,3 +1 @@ - - diff --git a/app/assets/javascripts/discourse/templates/components/auto-close-form.hbs b/app/assets/javascripts/discourse/templates/components/auto-close-form.hbs index c6fe0211cb..4234d0673b 100644 --- a/app/assets/javascripts/discourse/templates/components/auto-close-form.hbs +++ b/app/assets/javascripts/discourse/templates/components/auto-close-form.hbs @@ -1,8 +1,8 @@
@@ -13,7 +13,7 @@
diff --git a/app/assets/javascripts/discourse/templates/components/avatar-uploader.hbs b/app/assets/javascripts/discourse/templates/components/avatar-uploader.hbs index 80c9517a18..21761c67bd 100644 --- a/app/assets/javascripts/discourse/templates/components/avatar-uploader.hbs +++ b/app/assets/javascripts/discourse/templates/components/avatar-uploader.hbs @@ -1,10 +1,10 @@ - - + {{#if uploading}} - {{i18n upload_selector.uploading}} {{view.uploadProgress}}% + {{i18n 'upload_selector.uploading'}} {{view.uploadProgress}}% {{/if}} {{#if imageIsNotASquare}} -
{{i18n user.change_avatar.image_is_not_a_square}}
+
{{i18n 'user.change_avatar.image_is_not_a_square'}}
{{/if}} diff --git a/app/assets/javascripts/discourse/templates/components/basic-topic-list.hbs b/app/assets/javascripts/discourse/templates/components/basic-topic-list.hbs index a29a6b4f90..9fa600c9b9 100644 --- a/app/assets/javascripts/discourse/templates/components/basic-topic-list.hbs +++ b/app/assets/javascripts/discourse/templates/components/basic-topic-list.hbs @@ -1,58 +1,12 @@ {{#loading-spinner condition=loading}} {{#if topics}} - - - - - {{#unless controller.hideCategory}} - - {{/unless}} - - {{#if controller.showParticipants}} - - {{/if}} - - - - - - - {{#each topics}} - - - - {{raw "list/category-column" hideCategory=controller.hideCategory category=category}} - - {{posts-count-column topic=this class="num" action="clickedPosts"}} - - {{#if controller.showParticipants}} - - {{/if}} - - - - {{raw "list/activity-column" topic=this class="num" tagName="td"}} - - {{/each}} - - -
{{i18n topic.title}}{{i18n category_title}}{{i18n posts}}{{i18n users}}{{i18n views}}{{i18n activity}}
- {{#each participants}} - {{avatar this usernamePath="user.username" imageSize="small"}} - {{/each}} - - {{number views numberKey="views_long"}} -
+ {{topic-list + showParticipants=showParticipants + hideCategory=hideCategory + topics=topics}} {{else}}
- {{i18n choose_topic.none_found}} + {{i18n 'choose_topic.none_found'}}
{{/if}} {{/loading-spinner}} diff --git a/app/assets/javascripts/discourse/templates/components/category-drop.hbs b/app/assets/javascripts/discourse/templates/components/category-drop.hbs index 835e9ceb7e..9e50cf11c5 100644 --- a/app/assets/javascripts/discourse/templates/components/category-drop.hbs +++ b/app/assets/javascripts/discourse/templates/components/category-drop.hbs @@ -1,27 +1,28 @@ {{#if category}} - + + {{#if category.read_restricted}} - + {{fa-icon "lock"}} {{/if}} {{category.name}} {{else}} {{#if noSubcategories}} - {{i18n categories.no_subcategory}} + {{i18n 'categories.no_subcategory'}} {{else}} - {{allCategoriesLabel}} + {{allCategoriesLabel}} {{/if}} {{/if}} {{#if categories}} - +
{{#if subCategory}} - + {{/if}} {{#if renderCategories}} - {{#each categories}}
{{category-link this allowUncategorized=true}}
{{/each}} + {{#each c in categories}}
{{category-link c allowUncategorized=true hideParent=subCategory}}
{{/each}} {{/if}}
{{/if}} diff --git a/app/assets/javascripts/discourse/templates/components/conditional-loading-spinner.hbs b/app/assets/javascripts/discourse/templates/components/conditional-loading-spinner.hbs deleted file mode 100644 index d1d27ae58e..0000000000 --- a/app/assets/javascripts/discourse/templates/components/conditional-loading-spinner.hbs +++ /dev/null @@ -1,5 +0,0 @@ -{{#if condition}} -
-{{else}} - {{yield}} -{{/if}} diff --git a/app/assets/javascripts/discourse/templates/components/disabled-icon.hbs b/app/assets/javascripts/discourse/templates/components/disabled-icon.hbs new file mode 100644 index 0000000000..b7749fecee --- /dev/null +++ b/app/assets/javascripts/discourse/templates/components/disabled-icon.hbs @@ -0,0 +1,5 @@ +{{fa-icon icon modifier="stack-2x"}} + +{{#if disabled}} + {{fa-icon "ban" modifier="stack-2x"}} +{{/if}} diff --git a/app/assets/javascripts/discourse/templates/components/discourse-banner.hbs b/app/assets/javascripts/discourse/templates/components/discourse-banner.hbs index c675c2f5e8..825bcc44be 100644 --- a/app/assets/javascripts/discourse/templates/components/discourse-banner.hbs +++ b/app/assets/javascripts/discourse/templates/components/discourse-banner.hbs @@ -1,7 +1,7 @@
diff --git a/app/assets/javascripts/discourse/templates/components/emoji-uploader.hbs b/app/assets/javascripts/discourse/templates/components/emoji-uploader.hbs new file mode 100644 index 0000000000..8d9b297cf9 --- /dev/null +++ b/app/assets/javascripts/discourse/templates/components/emoji-uploader.hbs @@ -0,0 +1,6 @@ +{{text-field name="name" placeholderKey="admin.emoji.name" value=name}} + diff --git a/app/assets/javascripts/discourse/templates/components/header-extra-info.hbs b/app/assets/javascripts/discourse/templates/components/header-extra-info.hbs new file mode 100644 index 0000000000..c6326286ee --- /dev/null +++ b/app/assets/javascripts/discourse/templates/components/header-extra-info.hbs @@ -0,0 +1,21 @@ +
+
+
+

+ {{#if showPrivateMessageGlyph}} + {{fa-icon "envelope"}} + {{/if}} + + {{#if topic.details.loaded}} + {{topic-status topic=topic}} + {{{topic.fancy_title}}} + {{else}} + {{#if topic.errorLoading}} + {{topic.errorTitle}} + {{/if}} + {{/if}} +

+ {{topic-category topic=topic}} +
+
+
diff --git a/app/assets/javascripts/discourse/templates/components/home-logo.hbs b/app/assets/javascripts/discourse/templates/components/home-logo.hbs index b2b8f9e6db..976aefd88f 100644 --- a/app/assets/javascripts/discourse/templates/components/home-logo.hbs +++ b/app/assets/javascripts/discourse/templates/components/home-logo.hbs @@ -1,15 +1,18 @@ {{#if showSmallLogo}} {{#if smallLogoUrl}} + {{else}} {{/if}} {{else}} {{#if showMobileLogo}} + {{else}} {{#if bigLogoUrl}} + {{else}} diff --git a/app/assets/javascripts/discourse/templates/components/image-uploader.hbs b/app/assets/javascripts/discourse/templates/components/image-uploader.hbs index 67ba6ca3e2..32c0dce129 100644 --- a/app/assets/javascripts/discourse/templates/components/image-uploader.hbs +++ b/app/assets/javascripts/discourse/templates/components/image-uploader.hbs @@ -1,10 +1,12 @@ -
- - {{#if backgroundStyle}} - - {{/if}} - {{i18n upload_selector.uploading}} {{uploadProgress}}% + + {{#if backgroundStyle}} + + {{/if}} + {{i18n 'upload_selector.uploading'}} {{uploadProgress}}%
diff --git a/app/assets/javascripts/discourse/templates/components/ip-lookup.hbs b/app/assets/javascripts/discourse/templates/components/ip-lookup.hbs index 4afa200b19..56df09f1aa 100644 --- a/app/assets/javascripts/discourse/templates/components/ip-lookup.hbs +++ b/app/assets/javascripts/discourse/templates/components/ip-lookup.hbs @@ -1,60 +1,79 @@ {{#if ip}} {{/if}} {{#if show}}
-

{{i18n ip_lookup.title}}

+
{{fa-icon "times"}} +

{{i18n 'ip_lookup.title'}}

{{#if location}} {{#if location.hostname}} -
{{i18n ip_lookup.hostname}}
+
{{i18n 'ip_lookup.hostname'}}
{{location.hostname}}
{{/if}} -
{{i18n ip_lookup.location}}
+
{{i18n 'ip_lookup.location'}}
{{#if location.loc}} {{location.loc}}
{{city}} {{else}} - {{i18n ip_lookup.location_not_found}} + {{i18n 'ip_lookup.location_not_found'}} {{/if}}
{{#if location.org}} -
{{i18n ip_lookup.organisation}}
+
{{i18n 'ip_lookup.organisation'}}
{{location.org}}
{{/if}} {{#if location.phone}} -
{{i18n ip_lookup.phone}}
+
{{i18n 'ip_lookup.phone'}}
{{location.phone}}
{{/if}} {{else}} {{loading-spinner size="small"}} {{/if}} -
{{i18n ip_lookup.other_accounts}} {{other_accounts.length}}
-
- {{#loading-spinner size="small" condition=otherAccountsLoading}} - {{#if other_accounts}} -
    - {{#each other_accounts}} -
  • - {{#link-to "adminUser" this}}{{avatar this usernamePath="user.username" imageSize="small"}} {{username}}{{/link-to}} - ({{trustLevel.name}}), - {{i18n ip_lookup.read_time}} {{time_read}}, - {{i18n ip_lookup.topics_entered}} {{topics_entered}} -
  • - {{/each}} - {{else}} - {{i18n ip_lookup.no_other_accounts}} - {{/if}} - {{/loading-spinner}} -
    +
    + {{i18n 'ip_lookup.other_accounts'}} + {{totalOthersWithSameIP}} + {{#if other_accounts.length}} + + {{/if}} +
    + {{#loading-spinner size="small" condition=otherAccountsLoading}} + {{#if other_accounts.length}} +
    + + + + + + + + + + + + {{#each a in other_accounts}} + + + + + + + + {{/each}} + +
    {{i18n 'ip_lookup.username'}}{{i18n 'ip_lookup.trust_level'}}{{i18n 'ip_lookup.read_time'}}{{i18n 'ip_lookup.topics_entered'}}{{i18n 'ip_lookup.post_count'}}
    {{#link-to "adminUser" a}}{{avatar a usernamePath="user.username" imageSize="small"}} {{username}}{{/link-to}}{{a.trustLevel.id}}{{a.time_read}}{{a.topics_entered}}{{a.post_count}}
    +
    + {{/if}} + {{/loading-spinner}}
-
{{/if}} diff --git a/app/assets/javascripts/discourse/templates/components/login-buttons.hbs b/app/assets/javascripts/discourse/templates/components/login-buttons.hbs index a474886436..d6efea1866 100644 --- a/app/assets/javascripts/discourse/templates/components/login-buttons.hbs +++ b/app/assets/javascripts/discourse/templates/components/login-buttons.hbs @@ -1,3 +1,3 @@ -{{#each buttons}} - +{{#each b in buttons}} + {{/each}} diff --git a/app/assets/javascripts/discourse/templates/components/private-message-map.hbs b/app/assets/javascripts/discourse/templates/components/private-message-map.hbs index 54360aea35..fa82b721e8 100644 --- a/app/assets/javascripts/discourse/templates/components/private-message-map.hbs +++ b/app/assets/javascripts/discourse/templates/components/private-message-map.hbs @@ -1,26 +1,26 @@ -

{{i18n private_message_info.title}}

+

{{i18n 'private_message_info.title'}}

- {{#each details.allowed_groups}} + {{#each ag in details.allowed_groups}}
- #{{unbound name}} + #{{unbound ag.name}}
{{/each}} - {{#each details.allowed_users}} + {{#each au in details.allowed_users}}
- {{#link-to 'user' this}} - {{avatar this imageSize="small"}} + {{#link-to 'user' au}} + {{avatar au imageSize="small"}} {{/link-to}} - {{#link-to 'user' this}} - {{unbound username}} + {{#link-to 'user' au}} + {{unbound au.username}} {{/link-to}} - {{#if details.can_remove_allowed_users}} - + {{#if au.details.can_remove_allowed_users}} + {{/if}}
{{/each}}
{{#if details.can_invite_to}}
- +
{{/if}} diff --git a/app/assets/javascripts/discourse/templates/components/screened-ip-address-form.hbs b/app/assets/javascripts/discourse/templates/components/screened-ip-address-form.hbs index 6b497d6ab5..5f58a282c4 100644 --- a/app/assets/javascripts/discourse/templates/components/screened-ip-address-form.hbs +++ b/app/assets/javascripts/discourse/templates/components/screened-ip-address-form.hbs @@ -1,4 +1,4 @@ -{{i18n admin.logs.screened_ips.form.label}} +{{i18n 'admin.logs.screened_ips.form.label'}} {{text-field value=ip_address disabled=formSubmitted class="ip-address-input" placeholderKey="admin.logs.screened_ips.form.ip_address" autocorrect="off" autocapitalize="off"}} {{combo-box content=actionNames value=actionName}} - + diff --git a/app/assets/javascripts/discourse/templates/components/share-source.hbs b/app/assets/javascripts/discourse/templates/components/share-source.hbs new file mode 100644 index 0000000000..252538793e --- /dev/null +++ b/app/assets/javascripts/discourse/templates/components/share-source.hbs @@ -0,0 +1 @@ + diff --git a/app/assets/javascripts/discourse/templates/components/sortable-heading.hbs b/app/assets/javascripts/discourse/templates/components/sortable-heading.hbs deleted file mode 100644 index 36824ad2b1..0000000000 --- a/app/assets/javascripts/discourse/templates/components/sortable-heading.hbs +++ /dev/null @@ -1,2 +0,0 @@ -{{yield}} - diff --git a/app/assets/javascripts/discourse/templates/components/toggle-deleted.hbs b/app/assets/javascripts/discourse/templates/components/toggle-deleted.hbs index c6b6d21c39..5475461580 100644 --- a/app/assets/javascripts/discourse/templates/components/toggle-deleted.hbs +++ b/app/assets/javascripts/discourse/templates/components/toggle-deleted.hbs @@ -1,7 +1,7 @@ {{#if postStream.show_deleted}} -

{{i18n deleted_filter.disabled_description}}

- +

{{i18n 'deleted_filter.disabled_description'}}

+ {{else}} -

{{i18n deleted_filter.enabled_description}}

- +

{{i18n 'deleted_filter.enabled_description'}}

+ {{/if}} diff --git a/app/assets/javascripts/discourse/templates/components/toggle-summary.hbs b/app/assets/javascripts/discourse/templates/components/toggle-summary.hbs index bfc26874f5..6eed02a1cd 100644 --- a/app/assets/javascripts/discourse/templates/components/toggle-summary.hbs +++ b/app/assets/javascripts/discourse/templates/components/toggle-summary.hbs @@ -1,12 +1,12 @@ {{#if postStream.summary}} -

{{{i18n summary.enabled_description}}}

- +

{{{i18n 'summary.enabled_description'}}}

+ {{else}} {{#if topic.estimatedReadingTime}} -

{{{i18n summary.description_time count="topic.posts_count" readingTime="topic.estimatedReadingTime"}}}

+

{{{i18n 'summary.description_time' count=topic.posts_count readingTime=topic.estimatedReadingTime}}}

{{else}} -

{{{i18n summary.description count="topic.posts_count"}}}

+

{{{i18n 'summary.description' count=topic.posts_count}}}

{{/if}} - + {{/if}} diff --git a/app/assets/javascripts/discourse/templates/components/top-period-buttons.hbs b/app/assets/javascripts/discourse/templates/components/top-period-buttons.hbs index 718880e718..4b6c2ffab8 100644 --- a/app/assets/javascripts/discourse/templates/components/top-period-buttons.hbs +++ b/app/assets/javascripts/discourse/templates/components/top-period-buttons.hbs @@ -1,3 +1,3 @@ -{{#each period.availablePeriods}} - {{top-title-button period=this}} +{{#each p in period.availablePeriods}} + {{top-title-button period=p}} {{/each}} diff --git a/app/assets/javascripts/discourse/templates/components/top-period-chooser.hbs b/app/assets/javascripts/discourse/templates/components/top-period-chooser.hbs index 31a0107a81..53181310a9 100644 --- a/app/assets/javascripts/discourse/templates/components/top-period-chooser.hbs +++ b/app/assets/javascripts/discourse/templates/components/top-period-chooser.hbs @@ -3,8 +3,8 @@
diff --git a/app/assets/javascripts/discourse/templates/components/topic-category.hbs b/app/assets/javascripts/discourse/templates/components/topic-category.hbs new file mode 100644 index 0000000000..8a914440e0 --- /dev/null +++ b/app/assets/javascripts/discourse/templates/components/topic-category.hbs @@ -0,0 +1,6 @@ +{{#if topic.category.parentCategory}} + {{bound-category-link topic.category.parentCategory}} +{{/if}} +{{bound-category-link topic.category hideParent=true}} + +{{plugin-outlet "topic-category"}} diff --git a/app/assets/javascripts/discourse/templates/components/topic-list.hbs b/app/assets/javascripts/discourse/templates/components/topic-list.hbs new file mode 100644 index 0000000000..410d531de7 --- /dev/null +++ b/app/assets/javascripts/discourse/templates/components/topic-list.hbs @@ -0,0 +1,20 @@ +{{#unless skipHeader}} + + {{raw "topic-list-header" + currentUser=currentUser + canBulkSelect=canBulkSelect + toggleInTitle=toggleInTitle + hideCategory=hideCategory + showPosters=showPosters + showLikes=showLikes + showOpLikes=showOpLikes + showParticipants=showParticipants + order=order + ascending=ascending + sortable=sortable + bulkSelectEnabled=bulkSelectEnabled}} + +{{/unless}} + + {{each topic in topics itemView="topic-list-item"}} + diff --git a/app/assets/javascripts/discourse/templates/components/topic-map.hbs b/app/assets/javascripts/discourse/templates/components/topic-map.hbs index 1ea79eb44f..bf572caa3e 100644 --- a/app/assets/javascripts/discourse/templates/components/topic-map.hbs +++ b/app/assets/javascripts/discourse/templates/components/topic-map.hbs @@ -1,5 +1,5 @@ @@ -8,46 +8,46 @@
  • -

    {{i18n created_lowercase}}

    +

    {{i18n 'created_lowercase'}}

    {{avatar details.created_by imageSize="tiny"}} {{format-date topic.created_at}}
  • -

    {{i18n last_post_lowercase}}

    +

    {{i18n 'last_post_lowercase'}}

    {{avatar details.last_poster imageSize="tiny"}} {{format-date topic.last_posted_at}}
  • - {{posts-count-column topic=topic tagName="span"}} -

    {{i18n posts_lowercase}}

    + {{number topic.posts_count}} +

    {{i18n 'posts_lowercase'}}

  • {{number topic.views class=topic.viewsHeat}} -

    {{i18n views_lowercase}}

    +

    {{i18n 'views_lowercase'}}

  • {{number topic.participant_count}} -

    {{i18n users_lowercase}}

    +

    {{i18n 'users_lowercase'}}

  • {{#if topic.like_count}}
  • {{number topic.like_count}} -

    {{i18n likes_lowercase}}

    +

    {{i18n 'likes_lowercase'}}

  • {{/if}} {{#if details.links.length}}
  • {{number details.links.length}} -

    {{i18n links_lowercase}}

    +

    {{i18n 'links_lowercase'}}

  • {{/if}} {{#if showPosterAvatar}}
  • - {{#each details.fewParticipants}} - {{topic-participant participant=this}} + {{#each p in details.fewParticipants}} + {{topic-participant participant=p}} {{/each}}
  • {{/if}} @@ -56,8 +56,8 @@ {{#unless mapCollapsed}}
    - {{#each details.participants}} - {{topic-participant participant=this}} + {{#each p in details.participants}} + {{topic-participant participant=p}} {{/each}}
    @@ -65,16 +65,16 @@
@@ -74,11 +58,11 @@ {{loading-spinner condition=loadingMore}} {{#if allLoaded}} {{#if showDismissRead}} - - + + {{/if}} {{#if showResetNew}} - + {{/if}} {{#if latest}} @@ -87,12 +71,12 @@

{{footerMessage}} - {{#if can_create_topic}}{{i18n topic.suggest_create_topic}}{{/if}} + {{#if can_create_topic}}{{i18n 'topic.suggest_create_topic'}}{{/if}}

{{else}} {{#if top}}

- {{#link-to "discovery.categories"}}{{i18n topic.browse_all_categories}}{{/link-to}}, {{#link-to 'discovery.latest'}}{{i18n topic.view_latest_topics}}{{/link-to}} {{i18n or}} {{i18n filters.top.other_periods}} + {{#link-to "discovery.categories"}}{{i18n 'topic.browse_all_categories'}}{{/link-to}}, {{#link-to 'discovery.latest'}}{{i18n 'topic.view_latest_topics'}}{{/link-to}} {{i18n 'or'}} {{i18n 'filters.top.other_periods'}} {{top-period-buttons period=period}}

{{else}} @@ -100,7 +84,7 @@ {{{footerEducation}}}

- {{footerMessage}}{{#link-to "discovery.categories"}} {{i18n topic.browse_all_categories}}{{/link-to}} {{i18n or}} {{#link-to 'discovery.latest'}}{{i18n topic.view_latest_topics}}{{/link-to}} + {{footerMessage}}{{#link-to "discovery.categories"}} {{i18n 'topic.browse_all_categories'}}{{/link-to}} {{i18n 'or'}} {{#link-to 'discovery.latest'}}{{i18n 'topic.view_latest_topics'}}{{/link-to}}

{{/if}} {{/if}} diff --git a/app/assets/javascripts/discourse/templates/embedded_post.hbs b/app/assets/javascripts/discourse/templates/embedded_post.hbs index 6deb81d0c7..853e1f632d 100644 --- a/app/assets/javascripts/discourse/templates/embedded_post.hbs +++ b/app/assets/javascripts/discourse/templates/embedded_post.hbs @@ -2,17 +2,18 @@
- {{poster-avatar post=this classNames="main-avatar"}} + {{raw "post/poster-avatar" post=this classNames="main-avatar"}}
{{{unbound cooked}}} - {{#unless view.parentView.previousPost}}{{/unless}} +
diff --git a/app/assets/javascripts/discourse/templates/emoji-selector-autocomplete.raw.hbs b/app/assets/javascripts/discourse/templates/emoji-selector-autocomplete.raw.hbs new file mode 100644 index 0000000000..a01fbde0bd --- /dev/null +++ b/app/assets/javascripts/discourse/templates/emoji-selector-autocomplete.raw.hbs @@ -0,0 +1,9 @@ +
+ +
diff --git a/app/assets/javascripts/discourse/templates/emoji-toolbar.raw.hbs b/app/assets/javascripts/discourse/templates/emoji-toolbar.raw.hbs new file mode 100644 index 0000000000..8a519786e8 --- /dev/null +++ b/app/assets/javascripts/discourse/templates/emoji-toolbar.raw.hbs @@ -0,0 +1,35 @@ +
+ +
+ + {{#each row in rows}} + + {{#each item in row}} + + {{/each}} + + {{/each}} +
+
+ +
+ +
+
diff --git a/app/assets/javascripts/discourse/templates/exception.hbs b/app/assets/javascripts/discourse/templates/exception.hbs index ba19a38b81..e52073f2f2 100644 --- a/app/assets/javascripts/discourse/templates/exception.hbs +++ b/app/assets/javascripts/discourse/templates/exception.hbs @@ -3,7 +3,7 @@
:(
{{reason}}
- {{i18n errors.prev_page}} {{requestUrl}} + {{i18n 'errors.prev_page'}} {{requestUrl}}
{{#if networkFixed}} diff --git a/app/assets/javascripts/discourse/templates/group-selector-autocomplete.raw.hbs b/app/assets/javascripts/discourse/templates/group-selector-autocomplete.raw.hbs new file mode 100644 index 0000000000..e842a91b34 --- /dev/null +++ b/app/assets/javascripts/discourse/templates/group-selector-autocomplete.raw.hbs @@ -0,0 +1,7 @@ +
+ +
diff --git a/app/assets/javascripts/discourse/templates/group.hbs b/app/assets/javascripts/discourse/templates/group.hbs index d88ae8f126..f1af1a0ff9 100644 --- a/app/assets/javascripts/discourse/templates/group.hbs +++ b/app/assets/javascripts/discourse/templates/group.hbs @@ -2,12 +2,12 @@
diff --git a/app/assets/javascripts/discourse/templates/list/action-list.raw.hbs b/app/assets/javascripts/discourse/templates/list/action-list.raw.hbs new file mode 100644 index 0000000000..a859ec7e39 --- /dev/null +++ b/app/assets/javascripts/discourse/templates/list/action-list.raw.hbs @@ -0,0 +1,8 @@ +{{#if postNumbers}} +
+ + {{#each postNumber in postNumbers}} + #{{postNumber}} + {{/each}} +
+{{/if}} diff --git a/app/assets/javascripts/discourse/templates/list/category-column.raw.hbs b/app/assets/javascripts/discourse/templates/list/category-column.raw.hbs index 0527596b02..10c175aac6 100644 --- a/app/assets/javascripts/discourse/templates/list/category-column.raw.hbs +++ b/app/assets/javascripts/discourse/templates/list/category-column.raw.hbs @@ -1,3 +1 @@ -{{#unless hideCategory}} -{{category-link category showParent=true}} -{{/unless}} +{{category-link category}} diff --git a/app/assets/javascripts/discourse/templates/list/posters-column.raw.hbs b/app/assets/javascripts/discourse/templates/list/posters-column.raw.hbs index eae7670c36..1b837fb5a7 100644 --- a/app/assets/javascripts/discourse/templates/list/posters-column.raw.hbs +++ b/app/assets/javascripts/discourse/templates/list/posters-column.raw.hbs @@ -1,5 +1,5 @@ -{{#each posters}} -{{avatar this usernamePath="user.username" imageSize="small"}} +{{#each poster in posters}} +{{avatar poster usernamePath="user.username" imageSize="small"}} {{/each}} diff --git a/app/assets/javascripts/discourse/templates/list/posts-count-column.raw.hbs b/app/assets/javascripts/discourse/templates/list/posts-count-column.raw.hbs new file mode 100644 index 0000000000..511345e3bc --- /dev/null +++ b/app/assets/javascripts/discourse/templates/list/posts-count-column.raw.hbs @@ -0,0 +1,3 @@ +<{{view.tagName}} class='num posts-map posts {{view.likesHeat}}' title='{{view.title}}'> + {{topic.posts_count}} + diff --git a/app/assets/javascripts/discourse/templates/list/topic-excerpt.raw.hbs b/app/assets/javascripts/discourse/templates/list/topic-excerpt.raw.hbs index 94a3e629ff..76be2c412f 100644 --- a/app/assets/javascripts/discourse/templates/list/topic-excerpt.raw.hbs +++ b/app/assets/javascripts/discourse/templates/list/topic-excerpt.raw.hbs @@ -2,7 +2,7 @@
{{{topic.excerpt}}} {{#if topic.excerptTruncated}} - {{i18n read_more}} + {{i18n 'read_more'}} {{/if}}
{{/if}} diff --git a/app/assets/javascripts/discourse/templates/list/topic_list_item.hbs b/app/assets/javascripts/discourse/templates/list/topic_list_item.hbs deleted file mode 100644 index 439f226e1c..0000000000 --- a/app/assets/javascripts/discourse/templates/list/topic_list_item.hbs +++ /dev/null @@ -1,29 +0,0 @@ -{{#if bulkSelectEnabled}} - - {{input type="checkbox" checked=checked}} - -{{else}} - {{#if canStar}} - - - - {{/if}} -{{/if}} - - - {{topic-status topic=this.model}} - {{topic-link this}} - {{#if showTopicPostBadges}} - {{topic-post-badges unread=unread newPosts=displayNewPosts unseen=unseen url=lastUnreadUrl}} - {{/if}} - - {{raw "list/topic-excerpt" topic=model}} - - -{{raw "list/category-column" hideCategory=hideCategory category=category}} -{{raw "list/posters-column" posters=posters}} - -{{posts-count-column topic=model class="num" action="showTopicEntrance"}} -{{number views numberKey="views_long"}} - -{{raw "list/activity-column" topic=model class="num" tagName="td"}} diff --git a/app/assets/javascripts/discourse/templates/list/topic_list_item.raw.hbs b/app/assets/javascripts/discourse/templates/list/topic_list_item.raw.hbs new file mode 100644 index 0000000000..9ec92ec1df --- /dev/null +++ b/app/assets/javascripts/discourse/templates/list/topic_list_item.raw.hbs @@ -0,0 +1,54 @@ +{{#if controller.bulkSelectEnabled}} + + + +{{/if}} + + {{raw "topic-status" topic=topic}} + {{topic-link topic}} + {{#if controller.showTopicPostBadges}} + {{raw "topic-post-badges" unread=topic.unread newPosts=topic.displayNewPosts unseen=topic.unseen url=topic.lastUnreadUrl}} + {{/if}} + {{#if expandPinned}} + {{raw "list/topic-excerpt" topic=topic}} + {{/if}} + {{raw "list/action-list" topic=topic postNumbers=topic.liked_post_numbers className="likes" icon="heart"}} + + +{{#unless controller.hideCategory}} + {{#unless topic.isPinnedUncategorized}} + {{raw "list/category-column" category=topic.category}} + {{/unless}} +{{/unless}} + +{{#if controller.showPosters}} + {{raw "list/posters-column" posters=topic.posters}} +{{/if}} + +{{raw "list/posts-count-column" topic=topic}} + +{{#if controller.showParticipants}} + {{raw "list/posters-column" posters=topic.participants}} +{{/if}} + +{{#if controller.showLikes}} + + {{#if hasLikes}} + + {{number topic.like_count}} + + {{/if}} +{{/if}} + +{{#if controller.showOpLikes}} + + {{#if hasOpLikes}} + + {{number topic.op_like_count}} + + {{/if}} +{{/if}} + +{{number topic.views numberKey="views_long"}} + +{{raw "list/activity-column" topic=topic class="num" tagName="td"}} diff --git a/app/assets/javascripts/discourse/templates/list/user_topics_list.hbs b/app/assets/javascripts/discourse/templates/list/user_topics_list.hbs index ac54a240bf..a63fc87f94 100644 --- a/app/assets/javascripts/discourse/templates/list/user_topics_list.hbs +++ b/app/assets/javascripts/discourse/templates/list/user_topics_list.hbs @@ -1,3 +1,9 @@ +{{#if showNewPM}} +
+ {{fa-icon "envelope"}}{{i18n 'user.new_private_message'}} +
+{{/if}} + {{basic-topic-list topicList=model hideCategory=hideCategory showParticipants=showParticipants diff --git a/app/assets/javascripts/discourse/templates/mobile/components/basic-topic-list.hbs b/app/assets/javascripts/discourse/templates/mobile/components/basic-topic-list.hbs index 8c0a851e18..99aa9d66bf 100644 --- a/app/assets/javascripts/discourse/templates/mobile/components/basic-topic-list.hbs +++ b/app/assets/javascripts/discourse/templates/mobile/components/basic-topic-list.hbs @@ -2,43 +2,43 @@ {{#if topics}} - {{#each topics}} - + {{#each t in topics}} + - {{/each}} -
- {{posts-count-column topic=this tagName="div" class="num posts" action="clickedPosts"}} - {{raw "list/activity-column" topic=this tagName="div" class="num activity last"}} + {{raw "list/posts-count-column" topic=t tagName="div"}} + {{raw "list/activity-column" topic=t tagName="div" class="num activity last"}}
{{#unless controller.hideCategory}}
- {{category-link category showParent=true}} + {{category-link t.category}}
{{/unless}} {{#if controller.showParticipants}} {{/if}} @@ -46,11 +46,12 @@
- {{else}} -
- {{i18n choose_topic.none_found}} + {{/each}} + + + {{else}} +
+ {{i18n 'choose_topic.none_found'}}
{{/if}} {{/loading-spinner}} diff --git a/app/assets/javascripts/discourse/templates/mobile/discovery/categories.hbs b/app/assets/javascripts/discourse/templates/mobile/discovery/categories.hbs index cbe47e1690..cbee5af14d 100644 --- a/app/assets/javascripts/discourse/templates/mobile/discovery/categories.hbs +++ b/app/assets/javascripts/discourse/templates/mobile/discovery/categories.hbs @@ -1,52 +1,52 @@ -{{#each categories}} +{{#each c in categories}}
- {{#if description_excerpt}} + {{#if c.description_excerpt}} {{/if}} - {{#each topics}} - + {{#each t in c.topics}} + - - + + {{/each}} - {{#if subcategories}} + {{#if c.subcategories}} @@ -55,12 +55,12 @@
- {{category-link this allowUncategorized=true}} + {{category-link c allowUncategorized=true}}
- {{{description_excerpt}}} + {{{c.description_excerpt}}}
- {{#each subcategory in subcategories}} - {{category-link subcategory showParent=true}} + {{#each subcategory in c.subcategories}} + {{category-link subcategory}} {{/each}}
-
{{number topics_year}}
{{i18n category.this_year}}
-
{{number topics_month}}
{{i18n month}}
-
{{number topics_week}}
{{i18n week}}
+
{{number c.topics_year}}
{{i18n 'category.this_year'}}
+
{{number c.topics_month}}
{{i18n 'month'}}
+
{{number c.topics_week}}
{{i18n 'week'}}
{{#if controller.canEdit}} - {{i18n category.edit}} + {{i18n 'category.edit'}} {{/if}}
diff --git a/app/assets/javascripts/discourse/templates/mobile/discovery/topics.hbs b/app/assets/javascripts/discourse/templates/mobile/discovery/topics.hbs index 10f7529a15..984a40198a 100644 --- a/app/assets/javascripts/discourse/templates/mobile/discovery/topics.hbs +++ b/app/assets/javascripts/discourse/templates/mobile/discovery/topics.hbs @@ -4,22 +4,20 @@ {{top-period-chooser period=period}}
{{/if}} + + {{#if topicTrackingState.hasIncoming}} +
+ {{countI18n topic_count_ suffix=topicTrackingState.filter count=topicTrackingState.incomingCount}} + {{i18n 'click_to_show'}} +
+ {{/if}} + {{#if hasTopics}} - - {{#if topicTrackingState.hasIncoming}} - - - - - - {{/if}} - {{collection contentBinding="topics" tagName="tbody" itemView="topic-list-item"}} -
-
- {{countI18n topic_count_ suffix=topicTrackingState.filter count=topicTrackingState.incomingCount}} - {{i18n click_to_show}} -
-
+ {{topic-list + showPosters=true + currentUser=currentUser + hideCategory=hideCategory + topics=topics}} {{/if}}
@@ -27,11 +25,11 @@ {{loading-spinner condition=loadingMore}} {{#if allLoaded}} {{#if showDismissRead}} - - + + {{/if}} {{#if showResetNew}} - + {{/if}} {{#if latest}} @@ -40,12 +38,12 @@

{{footerMessage}} - {{#if can_create_topic}}{{i18n topic.suggest_create_topic}}{{/if}} + {{#if can_create_topic}}{{i18n 'topic.suggest_create_topic'}}{{/if}}

{{else}} {{#if top}}

- {{#link-to "discovery.categories"}}{{i18n topic.browse_all_categories}}{{/link-to}}, {{#link-to 'discovery.latest'}}{{i18n topic.view_latest_topics}}{{/link-to}} {{i18n or}} {{i18n filters.top.other_periods}} + {{#link-to "discovery.categories"}}{{i18n 'topic.browse_all_categories'}}{{/link-to}}, {{#link-to 'discovery.latest'}}{{i18n 'topic.view_latest_topics'}}{{/link-to}} {{i18n 'or'}} {{i18n 'filters.top.other_periods'}}
{{top-period-buttons period=period}}

@@ -54,7 +52,7 @@ {{{footerEducation}}}

- {{footerMessage}}{{#link-to "discovery.categories"}} {{i18n topic.browse_all_categories}}{{/link-to}} {{i18n or}} {{#link-to 'discovery.latest'}}{{i18n topic.view_latest_topics}}{{/link-to}} + {{footerMessage}}{{#link-to "discovery.categories"}} {{i18n 'topic.browse_all_categories'}}{{/link-to}} {{i18n 'or'}} {{#link-to 'discovery.latest'}}{{i18n 'topic.view_latest_topics'}}{{/link-to}}

{{/if}} {{/if}} diff --git a/app/assets/javascripts/discourse/templates/mobile/list/topic_list_item.hbs b/app/assets/javascripts/discourse/templates/mobile/list/topic_list_item.hbs deleted file mode 100644 index cbcbec9694..0000000000 --- a/app/assets/javascripts/discourse/templates/mobile/list/topic_list_item.hbs +++ /dev/null @@ -1,39 +0,0 @@ - - - -
- {{#unless controller.hideCategory}} -
- {{category-link category showParent=true}} -
- {{/unless}} - -
- {{posts-count-column topic=this tagName="div" class="num posts" action="showTopicEntrance"}} -
- {{last_poster_username}} - {{raw "list/activity-column" topic=this tagName="span" class="age"}} -
-
-
-
- diff --git a/app/assets/javascripts/discourse/templates/mobile/list/topic_list_item.raw.hbs b/app/assets/javascripts/discourse/templates/mobile/list/topic_list_item.raw.hbs new file mode 100644 index 0000000000..f52e48ba43 --- /dev/null +++ b/app/assets/javascripts/discourse/templates/mobile/list/topic_list_item.raw.hbs @@ -0,0 +1,27 @@ + + + +
+ {{#unless controller.hideCategory}} +
+ {{category-link content.category}} +
+ {{/unless}} + +
+ {{raw "list/posts-count-column" topic=content tagName="div"}} +
+ {{content.last_poster_username}} + {{raw "list/activity-column" topic=content tagName="span" class="age"}} +
+
+
+
+ diff --git a/app/assets/javascripts/discourse/templates/mobile/modal/login.hbs b/app/assets/javascripts/discourse/templates/mobile/modal/login.hbs index 806ffd6548..c3db1acafc 100644 --- a/app/assets/javascripts/discourse/templates/mobile/modal/login.hbs +++ b/app/assets/javascripts/discourse/templates/mobile/modal/login.hbs @@ -6,7 +6,7 @@
- + {{text-field value=loginName placeholderKey="login.email_placeholder" id="login-account-name" autocorrect="off" autocapitalize="off"}} @@ -14,7 +14,7 @@
- + {{text-field value=loginPassword type="password" id="login-account-password" maxlength="200"}}   @@ -23,7 +23,7 @@
- {{i18n forgot_password.action}} + {{i18n 'forgot_password.action'}}
@@ -35,7 +35,7 @@ diff --git a/app/assets/javascripts/discourse/templates/modal/auto_close.hbs b/app/assets/javascripts/discourse/templates/modal/auto_close.hbs index 0c037bfb83..8c0cd9e64e 100644 --- a/app/assets/javascripts/discourse/templates/modal/auto_close.hbs +++ b/app/assets/javascripts/discourse/templates/modal/auto_close.hbs @@ -6,8 +6,8 @@ limited=details.auto_close_based_on_last_post }} diff --git a/app/assets/javascripts/discourse/templates/modal/avatar_selector.hbs b/app/assets/javascripts/discourse/templates/modal/avatar_selector.hbs index cb459c9278..caad9187ac 100644 --- a/app/assets/javascripts/discourse/templates/modal/avatar_selector.hbs +++ b/app/assets/javascripts/discourse/templates/modal/avatar_selector.hbs @@ -2,12 +2,12 @@
- +
- - + +
@@ -16,20 +16,21 @@ {{#if view.uploadedAvatarTemplate}} {{bound-avatar-template view.uploadedAvatarTemplate "large"}} {{else}} - {{bound-avatar controller "large" custom_avatar_upload_id}} {{i18n user.change_avatar.uploaded_avatar}} + {{bound-avatar controller "large" custom_avatar_upload_id}} {{i18n 'user.change_avatar.uploaded_avatar'}} {{/if}} {{else}} - {{i18n user.change_avatar.uploaded_avatar_empty}} + {{i18n 'user.change_avatar.uploaded_avatar_empty'}} {{/if}} {{avatar-uploader username=username uploadedAvatarTemplate=view.uploadedAvatarTemplate - custom_avatar_upload_id=controller.custom_avatar_upload_id}} + custom_avatar_upload_id=controller.custom_avatar_upload_id + done="useUploadedAvatar"}}
diff --git a/app/assets/javascripts/discourse/templates/modal/bulk_actions_buttons.hbs b/app/assets/javascripts/discourse/templates/modal/bulk_actions_buttons.hbs index 3301aa6af0..40ace4bc37 100644 --- a/app/assets/javascripts/discourse/templates/modal/bulk_actions_buttons.hbs +++ b/app/assets/javascripts/discourse/templates/modal/bulk_actions_buttons.hbs @@ -1,10 +1,10 @@

- - - - + + + +

- - + +

diff --git a/app/assets/javascripts/discourse/templates/modal/bulk_change_category.hbs b/app/assets/javascripts/discourse/templates/modal/bulk_change_category.hbs index 4861aa42b9..e67173ff70 100644 --- a/app/assets/javascripts/discourse/templates/modal/bulk_change_category.hbs +++ b/app/assets/javascripts/discourse/templates/modal/bulk_change_category.hbs @@ -3,7 +3,7 @@

{{category-chooser value=newCategoryId}}

{{#if loading}} -
{{i18n loading}}
+
{{i18n 'loading'}}
{{else}} {{/if}} diff --git a/app/assets/javascripts/discourse/templates/modal/bulk_notification_level.hbs b/app/assets/javascripts/discourse/templates/modal/bulk_notification_level.hbs index 4e37543dba..cb40e1802c 100644 --- a/app/assets/javascripts/discourse/templates/modal/bulk_notification_level.hbs +++ b/app/assets/javascripts/discourse/templates/modal/bulk_notification_level.hbs @@ -7,4 +7,4 @@ {{/each}} - + diff --git a/app/assets/javascripts/discourse/templates/modal/change_owner.hbs b/app/assets/javascripts/discourse/templates/modal/change_owner.hbs index e8cc1c2ee0..1330351a6f 100644 --- a/app/assets/javascripts/discourse/templates/modal/change_owner.hbs +++ b/app/assets/javascripts/discourse/templates/modal/change_owner.hbs @@ -1,12 +1,12 @@ diff --git a/app/assets/javascripts/discourse/templates/modal/create-account.hbs b/app/assets/javascripts/discourse/templates/modal/create-account.hbs index 2faa73d77b..1e5934d206 100644 --- a/app/assets/javascripts/discourse/templates/modal/create-account.hbs +++ b/app/assets/javascripts/discourse/templates/modal/create-account.hbs @@ -10,7 +10,7 @@ - + - + - + - + - + - + {{#if passwordRequired}} - + {{/if}} - + diff --git a/app/assets/javascripts/discourse/templates/topic-list-header.raw.hbs b/app/assets/javascripts/discourse/templates/topic-list-header.raw.hbs new file mode 100644 index 0000000000..762822cf70 --- /dev/null +++ b/app/assets/javascripts/discourse/templates/topic-list-header.raw.hbs @@ -0,0 +1,24 @@ +{{#if bulkSelectEnabled}} + +{{/if}} +{{raw "topic-list-header-column" order='default' name='topic.title' showBulkToggle=toggleInTitle}} +{{#unless hideCategory}} + {{raw "topic-list-header-column" sortable=sortable order='category' name='category_title'}} +{{/unless}} +{{#if showPosters}} + {{raw "topic-list-header-column" order='posters' name='users'}} +{{/if}} +{{raw "topic-list-header-column" sortable=sortable number='true' order='posts' name='posts'}} +{{#if showParticipants}} + {{raw "topic-list-header-column" order='participants' name='users'}} +{{/if}} +{{#if showLikes}} + {{raw "topic-list-header-column" sortable=sortable number='true' order='likes' name='likes'}} +{{/if}} +{{#if showOpLikes}} + {{raw "topic-list-header-column" sortable=sortable number='true' order='op_likes' name='likes'}} +{{/if}} +{{raw "topic-list-header-column" sortable=sortable number='true' order='views' name='views'}} +{{raw "topic-list-header-column" sortable=sortable number='true' order='activity' name='activity'}} diff --git a/app/assets/javascripts/discourse/templates/topic-post-badges.raw.hbs b/app/assets/javascripts/discourse/templates/topic-post-badges.raw.hbs new file mode 100644 index 0000000000..549970a1b2 --- /dev/null +++ b/app/assets/javascripts/discourse/templates/topic-post-badges.raw.hbs @@ -0,0 +1,11 @@ + + {{#if unread ~}} + {{unread}} + {{/if }} + {{#if newPosts ~}} + {{newPosts}} + {{/if}} + {{#if unseen ~}} + {{i18n 'filters.new.lower_title'}} + {{/if}} + diff --git a/app/assets/javascripts/discourse/templates/topic-progress.hbs b/app/assets/javascripts/discourse/templates/topic-progress.hbs index a04f5bec48..b891376082 100644 --- a/app/assets/javascripts/discourse/templates/topic-progress.hbs +++ b/app/assets/javascripts/discourse/templates/topic-progress.hbs @@ -1,19 +1,22 @@ {{#if expanded}} {{/if}} -
{{input type="email" value=accountEmail id="new-account-email" disabled=emailValidated name="email" autofocus="autofocus"}}  {{input-tip validation=emailValidation}} @@ -18,11 +18,11 @@
{{input value=accountUsername id="new-account-username" name="username" maxlength=maxUsernameLength}}  {{input-tip validation=usernameValidation id="username-validation"}} @@ -30,11 +30,11 @@
{{text-field value=accountName id="new-account-name"}}  {{input-tip validation=nameValidation}} @@ -42,12 +42,12 @@
{{password-field value=accountPassword type="password" id="new-account-password" capsLockOn=capsLockOn}}  {{input-tip validation=passwordValidation}} @@ -57,13 +57,13 @@ -
{{i18n login.caps_lock_warning}}
+
{{i18n 'login.caps_lock_warning'}}
{{input type="password" value=accountPasswordConfirm id="new-account-confirmation"}} {{input value=accountChallenge id="new-account-challenge"}} @@ -74,8 +74,8 @@ {{#if userFields}}
- {{#each userFields}} - {{user-field field=field value=value}} + {{#each f in userFields}} + {{user-field field=f.field value=f.value}} {{/each}}
{{/if}} @@ -87,10 +87,10 @@ {{#if showCreateForm}} diff --git a/app/assets/javascripts/discourse/templates/modal/edit-category-general.hbs b/app/assets/javascripts/discourse/templates/modal/edit-category-general.hbs index 69232f908d..2722479ed3 100644 --- a/app/assets/javascripts/discourse/templates/modal/edit-category-general.hbs +++ b/app/assets/javascripts/discourse/templates/modal/edit-category-general.hbs @@ -1,18 +1,24 @@
- - {{text-field value=name placeholderKey="category.name_placeholder" maxlength="50"}} +
+ + {{text-field value=name placeholderKey="category.name_placeholder" maxlength="50"}} +
+
+ + {{text-field value=slug placeholderKey="category.slug_placeholder" maxlength="255"}} +
{{#if canSelectParentCategory}}
{{#if subCategories}} - - {{#each subCategories}} - {{category-badge this}} + + {{#each s in subCategories}} + {{category-badge s hideParent="true"}} {{/each}} {{else}} - + {{category-chooser valueAttribute="id" value=parent_category_id categories=parentCategories rootNone=true}} {{/if}}
@@ -20,33 +26,33 @@ {{#if showDescription}}
- + {{#if description}} {{description}} {{else}} - {{i18n category.no_description}} + {{i18n 'category.no_description'}} {{/if}} {{#if topic_url}}
- + {{/if}}
{{/if}}
- +
{{{categoryBadgePreview}}}
- {{i18n category.background_color}}: + {{i18n 'category.background_color'}}: #{{text-field value=color placeholderKey="category.color_placeholder" maxlength="6"}} {{color-picker colors=backgroundColors usedColors=usedBackgroundColors value=color}}
- {{i18n category.foreground_color}}: + {{i18n 'category.foreground_color'}}: #{{text-field value=text_color placeholderKey="category.color_placeholder" maxlength="6"}} {{color-picker colors=foregroundColors value=text_color}}
diff --git a/app/assets/javascripts/discourse/templates/modal/edit-category-images.hbs b/app/assets/javascripts/discourse/templates/modal/edit-category-images.hbs index 73fd9cf8f3..e1bacab397 100644 --- a/app/assets/javascripts/discourse/templates/modal/edit-category-images.hbs +++ b/app/assets/javascripts/discourse/templates/modal/edit-category-images.hbs @@ -1,9 +1,9 @@
- + {{image-uploader uploadUrl=categoryUploadUrl imageUrl=logo_url type="logo"}}
- + {{image-uploader uploadUrl=categoryUploadUrl imageUrl=background_url type="background"}}
diff --git a/app/assets/javascripts/discourse/templates/modal/edit-category-security.hbs b/app/assets/javascripts/discourse/templates/modal/edit-category-security.hbs index 8291af7492..ef70bd291d 100644 --- a/app/assets/javascripts/discourse/templates/modal/edit-category-security.hbs +++ b/app/assets/javascripts/discourse/templates/modal/edit-category-security.hbs @@ -1,21 +1,21 @@
    - {{#each permissions}} + {{#each p in permissions}}
  • - {{group_name}} + {{p.group_name}} {{{i18n "category.can"}}} - {{permission.description}} + {{p.permission.description}} {{#if controller.editingPermissions}} - + {{/if}}
  • {{/each}}
{{#if controller.editingPermissions}} - {{view Ember.Select content=availableGroups value=selectedGroup}} - {{view Ember.Select class="permission-selector" optionValuePath="content.id" optionLabelPath="content.description" content=availablePermissions value=selectedPermission}} - + {{view 'select' content=availableGroups value=selectedGroup}} + {{view 'select' class="permission-selector" optionValuePath="content.id" optionLabelPath="content.description" content=availablePermissions value=selectedPermission}} + {{else}} - + {{/if}}
diff --git a/app/assets/javascripts/discourse/templates/modal/edit-category-settings.hbs b/app/assets/javascripts/discourse/templates/modal/edit-category-settings.hbs index f31d4699f9..618742f62b 100644 --- a/app/assets/javascripts/discourse/templates/modal/edit-category-settings.hbs +++ b/app/assets/javascripts/discourse/templates/modal/edit-category-settings.hbs @@ -8,7 +8,7 @@
{{input type="checkbox" checked=allow_badges}} - {{i18n category.allow_badges_label}} + {{i18n 'category.allow_badges_label'}}
@@ -18,28 +18,28 @@ {{#if emailInEnabled}}
- {{i18n category.email_in}} + {{i18n 'category.email_in'}} {{text-field value=email_in}}
{{else}} - {{i18n category.email_in_disabled}} - {{i18n category.email_in_disabled_click}} + {{i18n 'category.email_in_disabled'}} + {{i18n 'category.email_in_disabled_click'}} {{/if}}
{{#if showPositionInput}} - + {{text-field value=position class="position-input"}} {{else}} - {{i18n category.position_disabled}} - {{i18n category.position_disabled_click}} + {{i18n 'category.position_disabled'}} + {{i18n 'category.position_disabled_click'}} {{/if}}
diff --git a/app/assets/javascripts/discourse/templates/modal/flag.hbs b/app/assets/javascripts/discourse/templates/modal/flag.hbs index 33adba35ab..154c192669 100644 --- a/app/assets/javascripts/discourse/templates/modal/flag.hbs +++ b/app/assets/javascripts/discourse/templates/modal/flag.hbs @@ -1,34 +1,34 @@
diff --git a/app/assets/javascripts/discourse/templates/modal/forgot_password.hbs b/app/assets/javascripts/discourse/templates/modal/forgot_password.hbs index 13c4ae09a1..2d5e39f8c4 100644 --- a/app/assets/javascripts/discourse/templates/modal/forgot_password.hbs +++ b/app/assets/javascripts/discourse/templates/modal/forgot_password.hbs @@ -1,9 +1,9 @@
diff --git a/app/assets/javascripts/discourse/templates/modal/history.hbs b/app/assets/javascripts/discourse/templates/modal/history.hbs index 9b9b52da5d..f31794f869 100644 --- a/app/assets/javascripts/discourse/templates/modal/history.hbs +++ b/app/assets/javascripts/discourse/templates/modal/history.hbs @@ -1,32 +1,32 @@ - {{outlet modalBody}} + {{outlet "modalBody"}} - {{#each errors}} + {{#each error in errors}}
- {{this}} + {{error}}
{{/each}} diff --git a/app/assets/javascripts/discourse/templates/modal/not_activated.hbs b/app/assets/javascripts/discourse/templates/modal/not_activated.hbs index cee8245db0..4266126c8d 100644 --- a/app/assets/javascripts/discourse/templates/modal/not_activated.hbs +++ b/app/assets/javascripts/discourse/templates/modal/not_activated.hbs @@ -1,11 +1,11 @@ diff --git a/app/assets/javascripts/discourse/templates/modal/option_boolean.hbs b/app/assets/javascripts/discourse/templates/modal/option_boolean.hbs index 2cb9c13848..0b04b45d29 100644 --- a/app/assets/javascripts/discourse/templates/modal/option_boolean.hbs +++ b/app/assets/javascripts/discourse/templates/modal/option_boolean.hbs @@ -1,5 +1,5 @@ diff --git a/app/assets/javascripts/discourse/templates/modal/raw_email.hbs b/app/assets/javascripts/discourse/templates/modal/raw_email.hbs index beaf5cb723..ad9f7ec508 100644 --- a/app/assets/javascripts/discourse/templates/modal/raw_email.hbs +++ b/app/assets/javascripts/discourse/templates/modal/raw_email.hbs @@ -2,6 +2,6 @@ {{#if rawEmail}} {{textarea value=rawEmail class="raw-email-textarea"}} {{else}} - {{i18n raw_email.not_available}} + {{i18n 'raw_email.not_available'}} {{/if}} diff --git a/app/assets/javascripts/discourse/templates/modal/search_help.hbs b/app/assets/javascripts/discourse/templates/modal/search_help.hbs index 5a9bf72d98..044ded4330 100644 --- a/app/assets/javascripts/discourse/templates/modal/search_help.hbs +++ b/app/assets/javascripts/discourse/templates/modal/search_help.hbs @@ -1,6 +1,6 @@ diff --git a/app/assets/javascripts/discourse/templates/modal/split_topic.hbs b/app/assets/javascripts/discourse/templates/modal/split_topic.hbs index ce83a0f191..ff72aab89c 100644 --- a/app/assets/javascripts/discourse/templates/modal/split_topic.hbs +++ b/app/assets/javascripts/discourse/templates/modal/split_topic.hbs @@ -5,13 +5,13 @@ {{/if}} - {{{i18n topic.split_topic.instructions count="selectedPostsCount"}}} + {{{i18n 'topic.split_topic.instructions' count=selectedPostsCount}}} - + {{text-field value=topicName placeholderKey="composer.title_placeholder" elementId='split-topic-name'}} - + {{category-chooser value=categoryId}} diff --git a/app/assets/javascripts/discourse/templates/modal/topic_bulk_actions.hbs b/app/assets/javascripts/discourse/templates/modal/topic_bulk_actions.hbs index 92d3a03e6a..330de1ad81 100644 --- a/app/assets/javascripts/discourse/templates/modal/topic_bulk_actions.hbs +++ b/app/assets/javascripts/discourse/templates/modal/topic_bulk_actions.hbs @@ -1,7 +1,7 @@ diff --git a/app/assets/javascripts/discourse/templates/modal/upload_selector.hbs b/app/assets/javascripts/discourse/templates/modal/upload_selector.hbs index cba1067239..9957e2ffc0 100644 --- a/app/assets/javascripts/discourse/templates/modal/upload_selector.hbs +++ b/app/assets/javascripts/discourse/templates/modal/upload_selector.hbs @@ -2,7 +2,7 @@ {{#if allowLocal}}
- + {{#if local}}

@@ -13,7 +13,7 @@ {{/if}}
- + {{#if remote}}

@@ -25,7 +25,7 @@

- {{i18n upload_selector.image_link}} + {{i18n 'upload_selector.image_link'}}
{{/if}} @@ -39,8 +39,8 @@ diff --git a/app/assets/javascripts/discourse/templates/navigation/categories.hbs b/app/assets/javascripts/discourse/templates/navigation/categories.hbs index 7644841f58..1db91ae46e 100644 --- a/app/assets/javascripts/discourse/templates/navigation/categories.hbs +++ b/app/assets/javascripts/discourse/templates/navigation/categories.hbs @@ -8,8 +8,8 @@ {{#if canCreateCategory}} - + {{/if}} {{#if canCreateTopic}} - + {{/if}} diff --git a/app/assets/javascripts/discourse/templates/navigation/category.hbs b/app/assets/javascripts/discourse/templates/navigation/category.hbs index ef35bd249f..5e511700e2 100644 --- a/app/assets/javascripts/discourse/templates/navigation/category.hbs +++ b/app/assets/javascripts/discourse/templates/navigation/category.hbs @@ -15,18 +15,18 @@ {{/if}} {{#if canCreateTopic}} - + {{d-button id="create-topic" class="btn-default" action="createTopic" icon="plus" label="topic.create"}} {{/if}} {{#if canEditCategory}} - + {{d-button class="btn-default" action="editCategory" actionParam=category icon="wrench" label="category.edit_long"}} {{/if}}
{{#if category.logo_url}} - + {{cdn-img src=category.logo_url class="category-logo"}} {{#if category.description}} -

{{{category.description}}}

+

{{{category.description}}}

{{/if}} {{/if}}
diff --git a/app/assets/javascripts/discourse/templates/navigation/default.hbs b/app/assets/javascripts/discourse/templates/navigation/default.hbs index 88b276198f..bd3015d44a 100644 --- a/app/assets/javascripts/discourse/templates/navigation/default.hbs +++ b/app/assets/javascripts/discourse/templates/navigation/default.hbs @@ -8,5 +8,5 @@ {{#if canCreateTopic}} - + {{/if}} diff --git a/app/assets/javascripts/discourse/templates/notifications.hbs b/app/assets/javascripts/discourse/templates/notifications.hbs index 491ece6270..75df3d8e30 100644 --- a/app/assets/javascripts/discourse/templates/notifications.hbs +++ b/app/assets/javascripts/discourse/templates/notifications.hbs @@ -2,15 +2,15 @@ {{#loading-spinner condition=loadingNotifications}} {{#if content}} {{else}} -
{{i18n notifications.none}}
+
{{i18n 'notifications.none'}}
{{/if}} {{/loading-spinner}} diff --git a/app/assets/javascripts/discourse/templates/post.hbs b/app/assets/javascripts/discourse/templates/post.hbs index 47a468537f..8bb99ee016 100644 --- a/app/assets/javascripts/discourse/templates/post.hbs +++ b/app/assets/javascripts/discourse/templates/post.hbs @@ -6,24 +6,14 @@
- {{#if showUserReplyTab}} - - {{#if loadingReplyHistory}} - {{i18n loading}} - {{else}} - - {{avatar reply_to_user imageSize="tiny"}} - {{reply_to_user.username}} - {{/if}} - - {{/if}} +
- {{#unless userDeleted}} - {{poster-avatar post=this classNames="main-avatar"}} - {{else}} + {{#unboundIf userDeleted}} - {{/unless}} + {{else}} + {{raw "post/poster-avatar" post=this classNames="main-avatar"}} + {{/unboundIf}} {{plugin-outlet "poster-avatar-bottom"}}
@@ -37,49 +27,60 @@ {{#if hasHistory}} {{/if}} {{#if wiki}} - + {{/if}} - {{#if via_email}} + {{#unboundIf via_email}} {{#if canViewRawEmail}} - + {{else}} - + {{/if}} - {{/if}} -
{{fa-icon "circle"}}
+ {{/unboundIf}} + {{#unboundIf showUserReplyTab}} + + {{#if loadingReplyHistory}} + {{i18n 'loading'}} + {{else}} + {{fa-icon "mail-forward"}} + {{avatar reply_to_user imageSize="tiny"}} + {{reply_to_user.username}} + {{/if}} + + {{/unboundIf}} +
{{fa-icon "circle"}}
- +
-
+
{{{cooked}}}
{{#if cooked_hidden}} - {{i18n post.show_hidden}} + {{i18n 'post.show_hidden'}} {{/if}} {{#if view.showExpandButton}} {{#if controller.loadingExpanded}} - + {{else}} - + {{/if}} {{/if}} {{view 'post-menu' post=this adminMenu=view.adminMenu}} diff --git a/app/assets/javascripts/discourse/templates/post/poster-avatar.raw.hbs b/app/assets/javascripts/discourse/templates/post/poster-avatar.raw.hbs new file mode 100644 index 0000000000..bc07a2ea44 --- /dev/null +++ b/app/assets/javascripts/discourse/templates/post/poster-avatar.raw.hbs @@ -0,0 +1 @@ +{{avatar post imageSize="large"}} diff --git a/app/assets/javascripts/discourse/templates/preferences/card-badge.hbs b/app/assets/javascripts/discourse/templates/preferences/card-badge.hbs index 8a36a795d4..e62caac308 100644 --- a/app/assets/javascripts/discourse/templates/preferences/card-badge.hbs +++ b/app/assets/javascripts/discourse/templates/preferences/card-badge.hbs @@ -3,7 +3,7 @@
-

{{i18n user.card_badge.title}}

+

{{i18n 'user.card_badge.title'}}

@@ -17,7 +17,7 @@
- {{#if saved}}{{i18n saved}}{{/if}} + {{#if saved}}{{i18n 'saved'}}{{/if}}
diff --git a/app/assets/javascripts/discourse/templates/search.hbs b/app/assets/javascripts/discourse/templates/search.hbs index 85a04bf4c0..dbf7f7c8fa 100644 --- a/app/assets/javascripts/discourse/templates/search.hbs +++ b/app/assets/javascripts/discourse/templates/search.hbs @@ -5,7 +5,7 @@ {{input type="checkbox" name="searchContext" checked=searchContextEnabled}} {{searchContextDescription}} {{/if}} - {{i18n show_help}} + {{i18n 'show_help'}}
{{#if loading}}
{{loading-spinner}}
@@ -21,14 +21,14 @@
{{#if resultType.more}} - {{i18n show_more}} + {{i18n 'show_more'}} {{else}} {{/if}}
{{/each}} {{else}}
- {{i18n search.no_results}} + {{i18n 'search.no_results'}}
{{/unless}}
diff --git a/app/assets/javascripts/discourse/templates/search/post_result.hbs b/app/assets/javascripts/discourse/templates/search/post_result.hbs index ca9caad16e..197ba51641 100644 --- a/app/assets/javascripts/discourse/templates/search/post_result.hbs +++ b/app/assets/javascripts/discourse/templates/search/post_result.hbs @@ -1,6 +1,6 @@ - {{i18n search.post_format post_number=post_number username=username}} + {{i18n 'search.post_format' post_number=post_number username=username}} {{#unless controller.site.mobileView}} diff --git a/app/assets/javascripts/discourse/templates/selected_posts.hbs b/app/assets/javascripts/discourse/templates/selected_posts.hbs index a64a0aa3f7..9444999c61 100644 --- a/app/assets/javascripts/discourse/templates/selected_posts.hbs +++ b/app/assets/javascripts/discourse/templates/selected_posts.hbs @@ -1,25 +1,25 @@

{{countI18n topic.multi_select.description countBinding="selectedPostsCount"}}

{{#if canSelectAll}} -

{{i18n topic.multi_select.select_all}}

+

{{i18n 'topic.multi_select.select_all'}}

{{/if}} {{#if canDeselectAll}} -

{{i18n topic.multi_select.deselect_all}}

+

{{i18n 'topic.multi_select.deselect_all'}}

{{/if}} {{#if canDeleteSelected}} - + {{/if}} {{#if canSplitTopic}} - + {{/if}} {{#if canMergeTopic}} - + {{/if}} {{#if canChangeOwner}} - + {{/if}} -

{{i18n topic.multi_select.cancel}}

+

{{i18n 'topic.multi_select.cancel'}}

diff --git a/app/assets/javascripts/discourse/templates/share.hbs b/app/assets/javascripts/discourse/templates/share.hbs index d81da9289b..0797ac7233 100644 --- a/app/assets/javascripts/discourse/templates/share.hbs +++ b/app/assets/javascripts/discourse/templates/share.hbs @@ -1,13 +1,20 @@ -

{{view.title}}

+{{#if controller.link}} +

{{view.title}}

-{{#if date}} - {{displayDate}} + {{#if date}} + {{displayDate}} + {{/if}} + +
+ + +
+ + {{#each s in sources}} + {{share-source source=s title=title action="share"}} + {{/each}} + + {{/if}} - -
- -{{each shareLinks itemView='share-link'}} - - diff --git a/app/assets/javascripts/discourse/templates/share_link.hbs b/app/assets/javascripts/discourse/templates/share_link.hbs deleted file mode 100644 index c10ee05981..0000000000 --- a/app/assets/javascripts/discourse/templates/share_link.hbs +++ /dev/null @@ -1,5 +0,0 @@ -{{#if openInPopup}} - -{{else}} - -{{/if}} diff --git a/app/assets/javascripts/discourse/templates/site-map.hbs b/app/assets/javascripts/discourse/templates/site-map.hbs new file mode 100644 index 0000000000..a1efed37ab --- /dev/null +++ b/app/assets/javascripts/discourse/templates/site-map.hbs @@ -0,0 +1,62 @@ +
+ + + {{#if categories}} + + {{/if}} +
diff --git a/app/assets/javascripts/discourse/templates/site_map.hbs b/app/assets/javascripts/discourse/templates/site_map.hbs deleted file mode 100644 index 2270759076..0000000000 --- a/app/assets/javascripts/discourse/templates/site_map.hbs +++ /dev/null @@ -1,59 +0,0 @@ -
- - - {{#if categories}} - - {{/if}} -
diff --git a/app/assets/javascripts/discourse/templates/static.hbs b/app/assets/javascripts/discourse/templates/static.hbs index 5e094986f9..ff09643a72 100644 --- a/app/assets/javascripts/discourse/templates/static.hbs +++ b/app/assets/javascripts/discourse/templates/static.hbs @@ -3,9 +3,7 @@ {{{html}}} {{#if showLoginButton}} - + {{/if}}
- -{{custom-html "footer"}} diff --git a/app/assets/javascripts/discourse/templates/topic-admin-menu.hbs b/app/assets/javascripts/discourse/templates/topic-admin-menu.hbs index b2ec69b1e3..ef204205eb 100644 --- a/app/assets/javascripts/discourse/templates/topic-admin-menu.hbs +++ b/app/assets/javascripts/discourse/templates/topic-admin-menu.hbs @@ -1,49 +1,49 @@ -

{{i18n admin_title}}

+

{{i18n 'admin_title'}}

    -
  • - -
  • +
  • + {{d-button action="toggleMultiSelect" icon="tasks" label="topic.actions.multi_select" class="btn-admin"}} +
  • {{#if details.can_delete}}
  • - + {{d-button action="deleteTopic" icon="trash-o" label="topic.actions.delete" class="btn-admin btn-danger"}}
  • {{/if}} {{#if showRecover}}
  • - + {{d-button action="recoverTopic" icon="undo" label="topic.actions.recover" class="btn-admin"}}
  • {{/if}}
  • {{#if closed}} - + {{d-button action="toggleClosed" icon="unlock" label="topic.actions.open" class="btn-admin"}} {{else}} - - + {{d-button action="toggleClosed" icon="lock" label="topic.actions.close" class="btn-admin"}} + {{d-button action="showAutoClose" icon="clock-o" label="topic.actions.auto_close" class="btn-admin"}} {{/if}}
  • {{#unless isPrivateMessage}}
  • {{#if isBanner}} - + {{d-button action="removeBanner" icon="bullhorn" label="topic.actions.remove_banner" class="btn-admin"}} {{else}} {{#if visible}} - + {{d-button action="makeBanner" icon="bullhorn" label="topic.actions.make_banner" class="btn-admin"}} {{/if}} {{/if}}
  • {{#if pinned_at}} - + {{d-button action="togglePinned" icon="thumb-tack" label="topic.actions.unpin" class="btn-admin"}} {{else}} {{#if visible}} - - + {{d-button action="togglePinned" icon="thumb-tack" label="topic.actions.pin" class="btn-admin"}} + {{d-button action="togglePinnedGlobally" icon="thumb-tack" label="topic.actions.pin_globally" class="btn-admin"}} {{/if}} {{/if}}
  • @@ -51,18 +51,19 @@
  • {{#if archived}} - + {{d-button action="toggleArchived" icon="folder" label="topic.actions.unarchive" class="btn-admin"}} {{else}} - + {{d-button action="toggleArchived" icon="folder" label="topic.actions.archive" class="btn-admin"}} {{/if}}
  • {{#if visible}} - + {{d-button action="toggleVisibility" icon="eye-slash" label="topic.actions.invisible" class="btn-admin"}} {{else}} - + {{d-button action="toggleVisibility" icon="eye" label="topic.actions.visible" class="btn-admin"}} {{/if}}
  • + {{plugin-outlet "topic-admin-menu-buttons"}}
diff --git a/app/assets/javascripts/discourse/templates/topic-entrance.hbs b/app/assets/javascripts/discourse/templates/topic-entrance.hbs index 8613848bb7..3dbe96f871 100644 --- a/app/assets/javascripts/discourse/templates/topic-entrance.hbs +++ b/app/assets/javascripts/discourse/templates/topic-entrance.hbs @@ -1,6 +1,7 @@ - - +{{#d-button action="enterTop" class="full no-text jump-top"}} + {{fa-icon 'caret-up'}} {{{topDate}}} +{{/d-button}} + +{{#d-button action="enterBottom" class="full no-text jump-button"}} + {{{bottomDate}}} {{fa-icon 'caret-down'}} +{{/d-button}} diff --git a/app/assets/javascripts/discourse/templates/topic-list-header-column.raw.hbs b/app/assets/javascripts/discourse/templates/topic-list-header-column.raw.hbs new file mode 100644 index 0000000000..3d3da42d96 --- /dev/null +++ b/app/assets/javascripts/discourse/templates/topic-list-header-column.raw.hbs @@ -0,0 +1,9 @@ +
+ {{~#if showBulkToggle}} + + {{/if ~}} + {{view.localizedName}} + {{~#if view.isSorting}} + + {{/if ~}} + + +
- - + + {{#if can_see_invite_details}} - - - - - + + + + + {{/if}} {{#each invite in model.invites}} @@ -42,27 +42,27 @@ - + {{{unbound invite.user.days_since_created}}} {{/if}} {{else}} {{/if}} @@ -73,9 +73,9 @@ {{else}} {{#if canBulkInvite}} - {{{i18n user.invited.bulk_invite.none}}} + {{{i18n 'user.invited.bulk_invite.none'}}} {{else}} - {{i18n user.invited.none}} + {{i18n 'user.invited.none'}} {{/if}} {{/if}} diff --git a/app/assets/javascripts/discourse/templates/user/notifications.hbs b/app/assets/javascripts/discourse/templates/user/notifications.hbs index 2a8816a57b..ab18934d4e 100644 --- a/app/assets/javascripts/discourse/templates/user/notifications.hbs +++ b/app/assets/javascripts/discourse/templates/user/notifications.hbs @@ -1,24 +1,24 @@ {{#if model.error}}
{{#if model.forbidden}} - {{i18n errors.reasons.forbidden}} + {{i18n 'errors.reasons.forbidden'}} {{else}} - {{i18n errors.desc.unknown}} + {{i18n 'errors.desc.unknown'}} {{/if}}
{{/if}} {{#if showDismissButton}}
- +
{{/if}} -{{#each itemController="notification"}} -
- {{notification-item notification=this scope=scope}} +{{#each n in model itemController="notification"}} +
+ {{notification-item notification=n scope=n.scope}} - {{format-date path="created_at" leaveAgo="true"}} + {{format-date n.created_at leaveAgo="true"}}
{{/each}} @@ -27,7 +27,7 @@ {{#unless canLoadMore}} {{#if showDismissButton}}
- +
{{/if}} {{/unless}} diff --git a/app/assets/javascripts/discourse/templates/user/posts.hbs b/app/assets/javascripts/discourse/templates/user/posts.hbs index 4dc27bb7d6..f3f6e67545 100644 --- a/app/assets/javascripts/discourse/templates/user/posts.hbs +++ b/app/assets/javascripts/discourse/templates/user/posts.hbs @@ -1,28 +1,28 @@ -{{#each model.content}} -
- +
{{username}} {{#if can_edit_username}} @@ -17,13 +17,13 @@ {{/if}}
- {{{i18n user.username.short_instructions username="username"}}} + {{{i18n 'user.username.short_instructions' username=username}}}
{{#if canEditName}}
- +
{{#if can_edit_name}} {{text-field value=newNameInput classNames="input-xxlarge"}} @@ -32,14 +32,14 @@ {{/if}}
- {{i18n user.name.instructions}} + {{i18n 'user.name.instructions'}}
{{/if}} {{#if canSelectTitle}}
- +
{{title}} {{#link-to "preferences.badgeTitle" class="btn btn-small pad-left no-text"}}{{fa-icon "pencil"}}{{/link-to}} @@ -49,7 +49,7 @@ {{#if canCheckEmails}}
- + {{#if email}}
{{email}} @@ -58,11 +58,11 @@ {{/if}}
- {{i18n user.email.instructions}} + {{i18n 'user.email.instructions'}}
{{else}}
- +
{{/if}}
@@ -70,13 +70,13 @@ {{#if canChangePassword}}
- +
{{#if no_password}} - {{i18n user.change_password.set_password}} + {{i18n 'user.change_password.set_password'}} {{else}} - {{i18n user.change_password.action}} + {{i18n 'user.change_password.action'}} {{/if}} {{passwordProgress}} @@ -85,20 +85,22 @@ {{/if}}
- +
{{bound-avatar model "large"}} {{#if allowAvatarUpload}} - - {{else}} {{#unless ssoOverridesAvatar}} - - {{/unless}} {{/if}} + + {{else}} + {{#unless ssoOverridesAvatar}} + {{fa-icon "pencil"}} + {{/unless}} + {{/if}}
{{#if allowBackgrounds}}
- +
{{image-uploader uploadUrl=imageUploadUrl imageUrl=profile_background @@ -106,12 +108,12 @@ type="profile_background"}}
- {{i18n user.change_profile_background.instructions}} + {{i18n 'user.change_profile_background.instructions'}}
- +
{{image-uploader uploadUrl=imageUploadUrl imageUrl=card_background @@ -119,46 +121,46 @@ type="card_background"}}
- {{i18n user.change_card_background.instructions}} + {{i18n 'user.change_card_background.instructions'}}
{{/if}} {{#if allowUserLocale}}
- +
{{combo-box valueAttribute="value" content=availableLocales value=locale none="user.locale.default"}}
- {{i18n user.locale.instructions}} + {{i18n 'user.locale.instructions'}}
{{/if}}
- +
{{pagedown-editor value=bio_raw}}
- +
{{input type="text" value=location class="input-xxlarge"}}
- +
{{input type="text" value=website class="input-xxlarge"}}
- +
{{#if card_image_badge}} {{icon-or-image card_image_badge}} @@ -168,12 +170,14 @@
- - {{preference-checkbox labelKey="user.email_digests.title" checked=email_digests}} - {{#if email_digests}} -
- {{combo-box valueAttribute="value" content=digestFrequencies value=digest_after_days}} -
+ + {{#if canReceiveDigest}} + {{preference-checkbox labelKey="user.email_digests.title" checked=email_digests}} + {{#if email_digests}} +
+ {{combo-box valueAttribute="value" content=digestFrequencies value=digest_after_days}} +
+ {{/if}} {{/if}} {{preference-checkbox labelKey="user.email_private_messages" checked=email_private_messages}} {{preference-checkbox labelKey="user.email_direct" checked=email_direct}} @@ -181,19 +185,19 @@ {{preference-checkbox labelKey="user.email_always" checked=email_always}}
- {{i18n user.email.frequency}} + {{i18n 'user.email.frequency' count=siteSettings.email_time_window_mins}}
- +
- + {{combo-box valueAttribute="value" content=autoTrackDurations value=auto_track_topics_after_msecs}}
- + {{combo-box valueAttribute="value" content=considerNewTopicOptions value=new_topic_duration_minutes}}
@@ -208,27 +212,27 @@ {{plugin-outlet "user_custom_preferences"}}
- {{#each userFields}} - {{user-field field=field value=value}} + {{#each uf in userFields}} + {{user-field field=uf.field value=uf.value}} {{/each}}
- +
- + {{category-group categories=watchedCategories blacklist=selectedCategories}}
-
{{i18n user.watched_categories_instructions}}
+
{{i18n 'user.watched_categories_instructions'}}
- + {{category-group categories=trackedCategories blacklist=selectedCategories}}
-
{{i18n user.tracked_categories_instructions}}
+
{{i18n 'user.tracked_categories_instructions'}}
- + {{category-group categories=mutedCategories blacklist=selectedCategories}}
-
{{i18n user.muted_categories_instructions}}
+
{{i18n 'user.muted_categories_instructions'}}
@@ -241,7 +245,7 @@ {{/if}} diff --git a/app/assets/javascripts/discourse/templates/user/preferences/_save_button.hbs b/app/assets/javascripts/discourse/templates/user/preferences/_save_button.hbs index 5437e4d358..cc18c3efb2 100644 --- a/app/assets/javascripts/discourse/templates/user/preferences/_save_button.hbs +++ b/app/assets/javascripts/discourse/templates/user/preferences/_save_button.hbs @@ -1,2 +1,2 @@ -{{#if saved}}{{i18n saved}}{{/if}} +{{#if saved}}{{i18n 'saved'}}{{/if}} diff --git a/app/assets/javascripts/discourse/templates/user/stream.hbs b/app/assets/javascripts/discourse/templates/user/stream.hbs index ccb0df5574..ed66367d51 100644 --- a/app/assets/javascripts/discourse/templates/user/stream.hbs +++ b/app/assets/javascripts/discourse/templates/user/stream.hbs @@ -1,25 +1,26 @@ -{{#each model.content}} -
- + +
{{i18n user.invited.user}}{{i18n user.invited.redeemed_at}}{{i18n 'user.invited.user'}}{{i18n 'user.invited.redeemed_at'}}{{i18n user.last_seen}}{{i18n user.invited.topics_entered}}{{i18n user.invited.posts_read_count}}{{i18n user.invited.time_read}}{{i18n user.invited.days_visited}}{{i18n 'user.last_seen'}}{{i18n 'user.invited.topics_entered'}}{{i18n 'user.invited.posts_read_count'}}{{i18n 'user.invited.time_read'}}{{i18n 'user.invited.days_visited'}}
{{number invite.user.topics_entered}} {{number invite.user.posts_read_count}} {{{unbound invite.user.time_read}}}{{{unbound invite.user.days_visited}}} + {{{unbound invite.user.days_visited}}} / - {{{unbound invite.user.days_since_created}}}{{unbound invite.email}} {{#if invite.expired}} - {{i18n user.invited.expired}} + {{i18n 'user.invited.expired'}}      {{/if}} {{#if invite.rescinded}} - {{i18n user.invited.rescinded}} + {{i18n 'user.invited.rescinded'}} {{else}} - + {{/if}}      {{#if invite.reinvited}} - {{i18n user.invited.reinvited}} + {{i18n 'user.invited.reinvited'}} {{else}} - + {{/if}}
order:viewsorder:latest
status:openstatus:closedstatus:archivedstatus:norepliesstatus:singleuser
category:foouser:foo
in:likesin:postedin:watchingin:tracking
in:likesin:postedin:watchingin:trackingin:private
in:bookmarks

diff --git a/config/locales/server.es.yml b/config/locales/server.es.yml index ac36ce390c..b39ce46393 100644 --- a/config/locales/server.es.yml +++ b/config/locales/server.es.yml @@ -20,11 +20,6 @@ es: short_date_no_year: "D MMM" short_date: "D MMM, YYYY" long_date: "D MMMM, YYYY h:mma" - time: - formats: - short: "%d-%m-%Y" - short_no_year: "%B %-d" - date_only: "%b %-d, %Y" title: "Discourse" topics: "Temas" posts: "posts" @@ -33,13 +28,49 @@ es: log_in: "Iniciar sesión" via: "%{username} vía %{site_name}" is_reserved: "está reservado" - purge_reason: "Borrado automáticamente dado que es antiguo y no está verificado" + purge_reason: "Eliminada automáticamente como cuenta abandonada, sin activar" disable_remote_images_download_reason: "La descarga de imágenes remotas se desactivó porque no había suficiente espacio disponible en disco." errors: + format: '%{attribute} %{message}' messages: too_long_validation: "está limitado a %{max} caracteres; has introducido %{length}." invalid_boolean: "Booleano no válido." - taken: "Ya ha sido tomado" + accepted: debe ser aceptado + blank: no puede dejarse en blanco + present: debe dejarse en blanco + confirmation: "no coincide %{attribute}" + empty: no puede estar vacío + equal_to: debe ser igual a %{count} + even: debe ser además + exclusion: está reservado + greater_than: debe ser más grande que %{count} + greater_than_or_equal_to: debe ser igual o más grande que %{count} + inclusion: no está incluido en la lista + invalid: no es válido + less_than: debe ser menor que %{count} + less_than_or_equal_to: debe ser menor o igual que %{count} + not_a_number: no es un número + not_an_integer: debe ser un valor entero + odd: debe ser impar + record_invalid: 'Error en la validación: %{errors}' + restrict_dependent_destroy: + one: "No se pudo eliminar el registro porque existe otro %{record} dependiente" + many: "No se puede eliminar el registro porque existen otros %{record} dependientes" + too_long: + one: es demasiado largo (el máximo es de 1 carácter) + other: es demasiado largo (el máximo es de %{count} caracteres) + too_short: + one: es demasiado corto (el mínimo es de 1 carácter) + other: es demasiado corto (el mínimo es de %{count} caracteres) + wrong_length: + one: tiene una longitud incorrecta (debería ser de 1 carácter) + other: tiene una longitud incorrecta (debería ser de %{count} caracteres) + other_than: "debe ser distinto de %{count}" + template: + body: 'Ha habido problemas con los siguientes campos:' + header: + one: 1 error impidió guardar este %{model} + other: '%{count} errores impidieron guardar este %{model}' embed: load_from_remote: "Hubo un error al cargar ese post." bulk_invite: @@ -107,6 +138,7 @@ es: latest: "Temas recientes" hot: "Temas calientes" too_late_to_edit: "Ese post fue publicado hace demasiado tiempo. No puede ser editado ni eliminado." + excerpt_image: "imagen" groups: errors: can_not_modify_automatic: "No puedes modificar un grupo automático" @@ -196,8 +228,8 @@ es: base: warning_requires_pm: "Solamente puedes enviar advertencias por mensajes privado." too_many_users: "Solamente puedes enviar advertencias a un usuario a la vez." - archetype: cant_send_pm: "Lo sentimos, no puedes enviar un mensaje privado a este usuario." + no_user_selected: "Debes seleccionar un usuario válido." user: attributes: password: @@ -381,6 +413,8 @@ es: title: 'Spam' description: 'Este post es un anuncio publicitario. No es útil ni relevante para este tema, sino de naturaleza promocional.' long_form: 'reportado como spam' + email_title: '"%{title}" fue reportado como spam' + email_body: "%{link}\n\n%{message}" inappropriate: title: 'Inapropiado' description: 'Este post contiene contenido que una persona sensata podría considerar ofensivo, abusivo o que viola nuestras directrices de comunidad.' @@ -437,7 +471,7 @@ es: unsubscribed: title: 'Dado de bajada' description: "Has sido dado de baja. ¡No te contactaremos de nuevo!" - oops: "En caso de que no quieras hacer esto, haz click abajo." + oops: "En caso de que no quieras hacer esto, haz clic abajo." error: "Error al darse de baja" preferences_link: "También puedes darte de baja de los emails de resumen en tu página de preferencias" different_user_description: "Actualmente estás logueado/a con un usuario diferente al que se le ha enviado el email de resumen. Por favor, cierra sesión e inténtalo de nuevo." @@ -510,18 +544,58 @@ es: top_referrers: title: "Los que más recomiendan" xaxis: "Usuario" - num_clicks: "Clicks" + num_clicks: "Clics" num_topics: "Temas" top_traffic_sources: title: "Las mayores fuentes de tráfico" xaxis: "Dominio" - num_clicks: "Clicks" + num_clicks: "Clics" num_topics: "Temas" num_users: "Usuarios" top_referred_topics: title: "Los temas más referidos" xaxis: "Tema" - num_clicks: "Clicks" + num_clicks: "Clics" + page_view_anon_reqs: + title: "Anónimas" + xaxis: "Día" + yaxis: "Páginas vistas de anónimos" + page_view_logged_in_reqs: + title: "Registrados" + xaxis: "Día" + yaxis: "Páginas vistas de usuarios registrados" + page_view_crawler_reqs: + title: "Rastreadores web" + xaxis: "Día" + yaxis: "Páginas vistas de rastreadores web" + page_view_total_reqs: + title: "Tota" + xaxis: "Día" + yaxis: "Páginas vistas totales" + http_background_reqs: + title: "Background" + xaxis: "Día" + yaxis: "Peticiones utilizadas para seguir y actualizar contenido" + http_2xx_reqs: + title: "Status 2xx (OK)" + xaxis: "Día" + yaxis: "Peticiones con éxito (Status 2xx)" + http_3xx_reqs: + title: "HTTP 3xx (Redirecciones)" + xaxis: "Día" + yaxis: "Peticiones de redirección (Status 3xx)" + http_4xx_reqs: + title: "HTTP 4xx (Error de cliente)" + xaxis: "Día" + yaxis: "Errores de cliente (Status 4xx)" + http_5xx_reqs: + title: "HTTP 5xx (Error de servidor)" + xaxis: "Día" + yaxis: "Errores de servidor (Status 5xx)" + http_total_reqs: + title: "Total" + xaxis: "Día" + yaxis: "Peticiones totales" dashboard: rails_env_warning: "Tu servidor está funcionando en modo de %{env}." ruby_version_warning: "Tú estás utilizando una versión de Ruby 2.0.0 que es conocida por tener problemas. Actualiza al nivel de parche 247 o más nuevo." @@ -540,15 +614,14 @@ es: s3_backup_config_warning: 'El servidor está configurado para permitir subir las copias de seguridad a s3, pero al menos una de las siguientes configuraciones no está realizada: s3_access_key_id, s3_secret_access_key o s3_backup_bucket. Ve a la Configuración del Sitio y actualiza la configuración. Revisa "How to set up image uploads to S3?" para aprender más.' image_magick_warning: 'El servidor está configurado para permitir miniaturas de imágenes grandes, pero ImageMagick no está instalado. Instala ImageMagick usando tu administrador de paquetes favorito o descárgate la última versión.' failing_emails_warning: 'Hay %{num_failed_jobs} de tareas de email que fallaron. Revisa tu archivo config/discourse.conf y asegúrate que la configuración del servidor de email esté correcta. Revisa las tareas fallidas en Sidekiq.' - default_logo_warning: "No has personalizado los imágenes del logo para tu sitio. Actualiza logo_url, logo_small_url, y favicon_url en la Configuración del Sitio." - contact_email_missing: "No has proporcionado un email de contacto para tu sitio. Por favor, actualiza contact_email en los Ajustes del sitio." - contact_email_invalid: "La dirección email de contacto es inválida. Por favor actualiza contact_email en la Configuración del Sitio." - title_nag: "El título Site Setting todavía está configurado al valor predeterminado. Por favor actualízalo con el título de tu sitio en la Configuración del Sitio." - site_description_missing: "La site_description configuración está vacía. Escribe una breve descripción de este foro en la Configuración del Sitio." + default_logo_warning: "Establece los imágenes del logo para tu sitio. Actualiza logo_url, logo_small_url, y favicon_url en la Configuración del Sitio." + contact_email_missing: "Introduce una dirección de correo electrónico de contacto para que puedan llegar mensajes urgentes sobre tu sitio. Actualízalo en Ajustes del sitio." + contact_email_invalid: "La dirección email de contacto es inválida. Actualízala en la Configuración del Sitio." + title_nag: "Introduce un nombre de tu sitio. Actualiza el nombre en Configuración del Sitio." + site_description_missing: "Introduce una breve descripción sobre tu sitio que aparecerá en los resultados de búsqueda. Actualiza site_description en Ajustes del sitio." consumer_email_warning: "Tu sitio está configurado para Utilizar Gmail (u otro servicio de email de consumidor) para enviar email. Gmail limita la cantidad de emails que puedes enviar. Considera usar un proveedor de servicio email como mandrill.com para asegurarte la entrega de los emails." - access_password_removal: "Tu sitio estaba usando la opción access_password , que ha sido eliminada. Las opciones login_required y must_approve_users han sido habilitadas, que deberían ser usadas en lugar de la anterior. Las puedes cambiar en la Configuración del Sitio. Asegúrate de aprobar los usuarios en la lista de Usuarios Pendientes. (Este mensaje desaparecerá en 2 días.)" - site_contact_username_warning: "La site_contact_username configuración está vacía. Por favor actualízala en la Configuración del Sitio. Configúrala al nombre de usuario de un administrador quien debe ser el enviador de mensajes de sistema." - notification_email_warning: "La configuración notification_email está vacía. Por favor actualízala en la Configuración del Sitio." + site_contact_username_warning: "Introduce el nombre de un usuario administrador o moderador simpático desde donde se enviarán importantes mensajes privados automáticos. Actualiza site_contact_username en Ajustes del sitio." + notification_email_warning: "Las notificaciones por email no están siendo enviadas desde una dirección de correo electrónico válida en tu dominio; la entrega de emails será errática y poco fiable. Por favor, establece para notification_email una dirección de correo electrónico local válida en Ajustes del sitio." content_types: education_new_reply: title: "Educación de nuevo usuario: primeras respuestas" @@ -597,12 +670,9 @@ es: allow_duplicate_topic_titles: "Permitir temas con títulos idénticos, duplicados." unique_posts_mins: "¿Cuántos minutos deben pasar antes de que un usuario pueda publicar el mismo contenido de nuevo?" educate_until_posts: "Cuando el usuario comienza a escribir su primer o primeros (n) posts, mostrar un pop-up con el panel de consejos para nuevos usuarios en el editor." - title: "Título breve de este sitio, utilizado en la etiqueta title." - site_description: "Describe este sitio en una frase, se utiliza en la etiqueta meta description." - contact_email: "Dirección de correo electrónico del contacto principal para el sitio. Las noticias de importancia desde discourse.org con actualizaciones críticas se enviarán a esta dirección." - company_full_name: "OBSOLETO PRÓXIMAMENTE. No se usará más, será eliminado. El nombre completo de la compañía que administra el sitio, usado en documentos legales como las Condiciones de Servicio y la Privacidad" - company_short_name: "OBSOLETO PRÓXIMAMENTE. No se usará más, será eliminado. El nombre en versión reducida de la compañía que administra el sitio, usado en documentos legales como las Condiciones de Servicio y la Privacidad" - company_domain: "OBSOLETO PRÓXIMAMENTE. No se usará más, será eliminado. El nombre de dominio propiedad de la compañía que administra el sitio, usado en documentos legales como las Condiciones de Servicio y la Privacidad" + title: "El nombre de este sitio, utilizado en la etiqueta title." + site_description: "Describe este sitio en una frase, utilizada en la etiqueta meta description." + contact_email: "Dirección de correo electrónico del responsable de este sitio. Utilizado para notificaciones críticas como reportes sin resolver, así como en la página /about de contacto para asuntos urgentes." queue_jobs: "¡SOLO DESARROLLADORES! ¡ADVERTENCIA! Poner varios trabajos en la cola de Sidekiq. Si se desactiva, el sitio dejará de funcionar." crawl_images: "Recuperar imágenes desde URLs remotas para insertarlas con las dimensiones correctas de ancho y de largo." download_remote_images_to_local: "Convertir imágenes remotas a imágenes locales descargándolas; esto previene imágenes rotas." @@ -622,11 +692,11 @@ es: post_excerpt_maxlength: "Extensión máxima del resumen / extracto de un post." post_onebox_maxlength: "Extensión máxima en caracteres de un post de Discourse en formato Onebox." onebox_domains_whitelist: "Una lista de dominios a los que permitir el formato Onebox; estos dominios deberán soportar OpenGraph u oEmbed. Pruébalos en http://iframely.com/debug" - logo_url: "La imagen de logo que irá arriba a la izquierda de tu sitio. Ejemplo: http://example.com/logo.png" + logo_url: "El logo situado en la esquina superior izquierda de tu sitio; si se deja en blanco, se mostrará un texto con el título del sitio." digest_logo_url: "El logo alternativo que irá arriba en los emails de resumen de tu sitio. Si se deja en blanco, se utilizará `logo_url`. Ejemplo: http://example.com/logo.png" - logo_small_url: "La imagen del logo pequeño que irá arriba a la izquierda de tu sitio y se verá cuando se haga scroll hacia abajo. Ejemplo: http://example.com/logo-small.png" + logo_small_url: "El logo pequeño situado en la esquina superior izquierda del sitio, mostrado cuando se hace scroll hacia abajo. Si se deja en blanco, se mostrará un glifo de una casita." favicon_url: "Un favicon para tu sitio, mira http://es.wikipedia.org/wiki/Favicon" - mobile_logo_url: "La imagen del logo en posición fija que irá arriba a la izquierda para la vista móvil de tu sitio. Si se deja en blanco, se utilizará `logo_url`. Ejemplo: http://example.com/uploads/default/logo.png" + mobile_logo_url: "El logo con posición fijada utilizado en la esquina superior izquierda de la versión móvil de tu sitio. Si se deja en blanco, se mostrará el texto del título del sitio." apple_touch_icon_url: "Icono para los dispositivos táctiles de Apple. Se recomienda un tamaño de 144px por 144px." notification_email: "La dirección de correo electrónico \"remitente\", utilizada al enviar todos los emails esenciales de sistema. El dominio especificado debe tener correctamente configurados los registros SPF, DKIM y PTR inversos para que los emails se reciban correctamente." email_custom_headers: "Lista de emails separados por una barra" @@ -665,14 +735,12 @@ es: enable_noscript_support: "Habilitar el soporte de motor de búsqueda estándar webcrawler mediante la etiqueta noscript" allow_moderators_to_create_categories: "Permitir a los moderadores crear nuevas categorías" cors_origins: "Orígenes permitidos para las cross-origin requests (CORS). Cada origen debe incluir http:// or https://. La variable env DISCOURSE_ENABLE_CORS debe establecerse a verdadero para activar CORS." - top_menu: "Determinar qué elementos aparecen en la navegación de la home y en qué orden. Ejemplo latest|new|unread|starred|categories|top|read|posted" + top_menu: "Determinar que items aparecen en la navegación de la home y en qué orden. Ejemplo latest|new|unread|categories|top|read|posted|bookmarks" post_menu: "Determina qué elementos aparecen en el menú de un post y en qué orden. Ejemplo: like|edit|flag|delete|share|bookmark|reply" post_menu_hidden_items: "Los elementos del menú a ocultar por defecto en el menú de cada post a menos que se haga clic en el botón para expandir las opciones." share_links: "Determina qué elementos aparecen en el cuadro de compartir y en qué orden" track_external_right_clicks: "Hacer seguimiento de los enlaces externos en los que se ha hecho clic con el botón derecho (ej: abrir en nueva pestaña) desactivado por defecto porque reescribe URLs" - topics_per_page: "Cuántos temas se cargan por defecto en la lista de temas y cuando se hace scroll hacia abajo para cargar más temas" - posts_chunksize: "Cuantos posts son cargados por default en un tema, y cuando se desplaza hacia abajo para cargar mas posts." - site_contact_username: "Todos los mensajes privados automatizados serán de este usuario. Si es dejado en blanco, el la cuenta del Sistema por defecto será utilizada." + site_contact_username: "Nombre de usuario de moderador o administrador desde donde se enviarán mensajes privados automáticos. Si se deja en blanco, se utilizará la cuenta de sistema por defecto." send_welcome_message: "Enviar a todos los nuevos usuarios un mensaje privado de bienvenida con una guía rápida para comenzar." suppress_reply_directly_below: "No mostrar el contador de respuestas desplegable en un post si solo hay una única respuesta y está justamente debajo del post en cuestión." suppress_reply_directly_above: "No mostrar el en-respuesta-a desplegable en un post cuando solo hay una sola respuesta justo encima del post." @@ -690,6 +758,7 @@ es: email_domains_blacklist: "Una lista de dominios de email con los cuales los usuarios no podrán registrarse. Ejemplo: mailinator.com trashmail.net" email_domains_whitelist: "Una lista de dominios de email con los que los usuarios DEBERÁN registrar sus cuentas. AVISO: ¡los usuarios con un email con diferente dominio a los listados no estarán permitidos!" forgot_password_strict: "No informar de la existencia de la cuenta de un usuario cuando ellos use la opción de olvidar contraseña." + log_out_strict: "Al cerrar sesión, cierra TODAS las sesiones del usuario en todos los dispositivos" version_checks: "Ping el 'Discourse Hub' para actualizaciones de versión y mostrar mensajes del número de versión en el dashboard /admin" new_version_emails: "Enviar un email a la dirección contact_email cuando esté disponible una nueva versión de Discourse." port: "¡SOLO DESARROLLADORES! ¡ADVERTENCIA! Usa este puerto HTTP en vez del puerto 80 por defecto. Dejar en blanco deja por defecto el puerto 80." @@ -702,9 +771,10 @@ es: max_username_length: "Longitud máxima del nombre de usuario en caracteres. ADVERTENCIA: TODOS LOS USUARIOS EXISTENTES CON NOMBRES MÁS LARGOS QUE ESTE VALOR NO PODRÁN ACCEDER AL SITIO" min_password_length: "Longitud mínima de contraseña." block_common_passwords: "No permitir contraseñas que están entre las 10.000 más comunes." - enable_sso: "Habilitar el single sign on a través de un sitio externo (Nota: deshabilita las invitaciones)" + enable_sso: "Activar single sign on a través de un sitio externo (AVISO: puede hacer que nadie se pueda registrar si no está configurado de forma apropiada; además, desactiva las invitaciones)" + enable_sso_provider: "Implementar el protocolo de SSO de Discourse en el endpoint /session/sso_provider requiere establecer un sso_secret" sso_url: "URL del endpoint para el single sign on" - sso_secret: "Cadena secreta utilizada para cifrar/descifrar la información del SSO, asegúrate de que es de 10 caracteres o más" + sso_secret: "Cadena secreta utilizada para autenticar criptográficamente la información del SSO, asegúrate de que es de 10 caracteres o más" sso_overrides_email: "Reemplaza el email local con el email del sitio externo desde el payload del SSO (AVISO: pueden ocurrir discrepancias debido a la normalización de los correos electrónicos locales)" sso_overrides_username: "Reemplaza el nombre de usuario local con el nombre de usuario del sitio externo desde el payload del SSO (AVISO: pueden ocurrir discrepancias debido a las diferencias de requirimientos/longitud en los nombres de usuario)" sso_overrides_name: "Reemplaza el nombre local con el nombre del sitio externo desde el payload del SSO (AVISO: pueden ocurrir discrepancias debido a la normalización de los nombres locales)" @@ -741,17 +811,18 @@ es: max_flags_per_day: "Máximo número de reportes por usuario y día." max_bookmarks_per_day: "Máximo número de marcadores por usuario y día." max_edits_per_day: "Máximo número de ediciones por usuario y día." - max_stars_per_day: "Máximo número temas que pueden ser añadidos a favoritos por usuario y día." max_topics_per_day: "Máximo número de temas que un usuario puede crear al día." max_private_messages_per_day: "Máximo número de mensajes privados por usuario y día." + max_invites_per_day: "Máximo número de invitaciones que un usuario puede enviar al día." suggested_topics: "Número de temas sugeridos mostrados al pie del tema." limit_suggested_to_category: "Solo mostrar temas de la categoría actual en los temas sugeridos." clean_up_uploads: "Eliminar subidas huérfanas sin referencia para prevenir hosting ilegal. AVISO: antes de habilitar esta opción quizá quieres hacer un backup de tu directorio de /uploads" clean_orphan_uploads_grace_period_hours: "Período de gracia (en horas) antes de que una subida huérfana sea eliminada." purge_deleted_uploads_grace_period_days: "Período de gracia (en días) antes de que una subida eliminada sea borrada totalmente." - purge_inactive_users_grace_period_days: "Periodo de gracia (en días) durante los cuales un usuario que no haya activado su cuenta no será aún eliminado." + purge_unactivated_users_grace_period_days: "Período de gracia (en días) que se concede a un usuario que no haya activado su cuenta antes de eliminarla." enable_s3_uploads: "Coloca los archivos subidos en el almacén Amazon S3. IMPORTANTE: requiere de credenciales de S3 validas. (ambas clave de acceso y clave de acceso secreta)." s3_use_iam_profile: 'Usar el rol de AWS EC2 IAM para descargar las llaves. NOTA: habilitando esta opción ignorará la "llave de acceso s3" y "la llave de acceso secreta s3".' + s3_upload_bucket: "El nombre del bucket Amazon S3 donde se subirán los archivos. AVISO: debe ser en minúsculas, sin puntos ni guiones bajos." s3_access_key_id: "La clave id de acceso de Amazon S3 que se utilizará para subir imágenes." s3_secret_access_key: "La clave secreta de acceso de Amazon S3 que se utilizará para subir imágenes." s3_region: "El nombre de región de Amazon S3 que se utilizará para subir imágenes." @@ -798,6 +869,7 @@ es: min_title_similar_length: "La extensión mínima que un título debe tener antes de revisar temas similares." min_body_similar_length: "La extensión mínima que el cuerpo de un post debe tener antes de revisar temas similares." category_colors: "Una lista de valores en hexadecimal de los colores para las categorías." + category_style: "Estilo visual de las etiquetas de categoría." max_image_size_kb: "El tamaño máximo, en kB, para subir imágenes. Debe ser configurado también en nginx (client_max_body_size) / apache o proxy." max_attachment_size_kb: "El tamaño máximo, en kB, para adjuntar archivos. Debe ser configurado también en nginx (client_max_body_size) / apache o proxy." authorized_extensions: "Una lista de las extensiones de archivo permitidas para subir (usa \"*\" para habilitar todos los tipos de archivo)" @@ -822,12 +894,17 @@ es: white_listed_spam_host_domains: "Una lista de dominios a excluir de las pruebas de spam. A los nuevos usuarios no se les restringirá la posibilidad de crear posts con enlaces a estos dominios." staff_like_weight: "Qué ponderación extra otorgan los me gusta provenientes de los miembros del Staff." levenshtein_distance_spammer_emails: "Al revisar coincidencias por correos electrónicos de spammers, qué número de caracteres permiten una coincidencia parcial." + max_new_accounts_per_registration_ip: "Si ya hay (n) cuentas con nivel de confianza 0 desde esta IP (y ninguna es de un miembro del staff o de nivel de confianza 2 o más), prohibir nuevos registros desde esa IP." + min_ban_entries_for_roll_up: "Al hacer clic en el botón Agrupar, se crea un nuevo rango de entradas para banear si hay al menos (N) entradas." + max_age_unmatched_emails: "Eliminar entradas de email que no coincidan después de (N) días." + max_age_unmatched_ips: "Eliminar entradas de IP que no coincidan después de (N) días." + num_flaggers_to_close_topic: "Número mínimo de denunciantes únicos requerido para pausar un tema automáticamente para intervención" + num_flags_to_close_topic: "Valor mínimo de reportes activos requerido para pausar un tema automáticamente para intervención" reply_by_email_enabled: "Habilitar la respuesta a temas por email." reply_by_email_address: "Plantilla para la dirección de email que aparecerá al recibir correos con la función de respuesta por email: %{reply_key}@respuesta.ejemplo.com o respuestas+%{reply_key}@ejemplo.com" disable_emails: "Impedir que Discourse envié cualquier tipo de e-mail." strip_images_from_short_emails: "Remover imágenes de e-mails que tengan un tamaño menor a 2800 Bytes" short_email_length: "e-mail corto longitud en Bytes" - enable_email_names: "Permitir mostrar nombres completos de usuarios en e-mails. Inhabilitar para esconder nombres completos en e-mails." pop3_polling_enabled: "Poll vía POP3 para respuestas de e-mail." pop3_polling_ssl: "Usar SSL mientras se conecta al servidor POP3. (Recomendado)" pop3_polling_period_mins: "El período en minutos entre revisiones de correo de la cuenta POP3. NOTA: requiere reiniciar." @@ -848,10 +925,13 @@ es: logout_redirect: "Ubicación a redirigir después que el usuario cierre sesión Ejem: (http://somesite.com/logout)" allow_uploaded_avatars: "Permitir a los usuarios subir su propio avatar personalizado." allow_animated_avatars: "Permitir a los usuarios usar avatares con imágenes animadas tipo gif. AVISO: ejecuta el rake task avatars:refresh después de cambiar esta opción." + allow_animated_thumbnails: "Generar miniaturas en movimiento de los gifs animados." automatically_download_gravatars: "Descargar Gravatars para usuarios cuando se creen una cuenta o cambien el email." digest_topics: "El número máximo de temas a mostrar en el resumen por email." digest_min_excerpt_length: "La extensión mínima, en caracteres, del extracto de un post en el resumen por email." default_digest_email_frequency: "Cada cuánto tiempo los usuarios recibirán emails con el resumen del sitio por defecto. Cada usuario puede cambiar esta opción para sí en sus preferencias." + suppress_digest_email_after_days: "Suprimir los emails de resumen para aquellos usuarios que no han visto el sitio desde más de (n) días." + disable_digest_emails: "Inhabilitar e-mails de resumen para todos los usuarios." default_external_links_in_new_tab: "Abrir enlaces externos en una nueva pestaña. Los usuarios pueden cambiar esto en sus preferencias." detect_custom_avatars: "Verificar o no que los usuarios han subido su propio avatar personalizado." max_daily_gravatar_crawls: "Máximo número de veces que Discourse comprobará Gravatar en busca de avatares personalizados en un día" @@ -863,7 +943,7 @@ es: suppress_uncategorized_badge: "No mostrar la etiqueta de los temas sin categoría en la lista de temas." global_notice: "Mostrar una noticia de URGENCIA o EMERGENCIA para todos los visitantes, deja este campo en blanco para ocultarla (se puede utilizar código HTML)" disable_edit_notifications: "Inhabilitar editar notificaciones por el usuario system cuando 'download_remote_images_to_local' este activo." - enable_names: "Habilitar que se muestren los nombres completos de los usuarios. Si se deshabilita, se ocultarán los nombres completos." + enable_names: "Mostrar el nombre completo del usuario en su perfil, tarjeta de usuario y emails. Desactiva esta opción para ocultar el nombre completo en todas partes." display_name_on_posts: "Mostrar el nombre completo de un usuario en sus posts, además de su @usuario." invites_per_page: "Número de invitaciones por defecto mostradas en la página de perfil del usuario." short_progress_text_threshold: "Después de que un número de posts en un tema alcance esta cifra, la barra de progreso sólo mostrará el número del post actual. Si cambias el ancho de la barra de progreso, deberías revisar este valor." @@ -885,6 +965,8 @@ es: show_create_topics_notice: "Si el sitio tiene menos de 5 temas abiertos al público, mostrar un aviso pidiendo a los administradores crear más temas." vacuum_db_days: "Correr VACUUM FULL ANALYZE para reclamar espacio en la base de datos después de las migraciones. (Poner en 0 para inhabilitar)" prevent_anons_from_downloading_files: "Impedir que los usuarios anónimos descarguen archivos. ADVERTENCIA: Esto impedirá que funcione cualquier recurso del sitio publicado como adjunto." + enable_emoji: "Habilitar emoji" + emoji_set: "¿De qué tipo os gustan los emoji?" errors: invalid_email: "Dirección de correo electrónico inválida. " invalid_username: "No existe ningún usuario con ese nombre de usuario. " @@ -897,6 +979,7 @@ es: invalid_string_min_max: "Debe contener entre %{min} y %{max} caracteres." invalid_string_min: "Debe contener como mínimo %{min} caracteres. " invalid_string_max: "No debe exceder los %{max} caracteres. " + invalid_reply_by_email_address: "El valor debe contener '%{reply_key}' y debe ser diferente del email de notificación." notification_types: mentioned: "%{display_username} te ha mencionado en %{link}" liked: "%{display_username} le ha gustado tu post en %{link}" @@ -916,6 +999,11 @@ es: category: 'Categorías' topic: 'Resultados' user: 'Usuarios' + sso: + not_found: "No se puede buscar o crear cuenta, contacta al administrador del sitio" + account_not_approved: "La cuenta está pendiente de aprobación, recibirás una notificación por email cuando se apruebe" + unknown_error: "Error al actualizar la información, contacta al administrador del sitio" + timeout_expired: "Se agotó el tiempo de espera para el inicio de sesión, por favor, intenta iniciar sesión de nuevo" original_poster: "Autor original" most_posts: "Con más posts" most_recent_poster: "Autor más reciente" @@ -932,11 +1020,33 @@ es: other: "He movido %{count} posts al tema: %{topic_link}" change_owner: post_revision_text: "Se ha transferido la propiedad de %{old_user} a %{new_user}" + emoji: + errors: + name_already_exists: "Lo sentimos, el nombre '%{name}' ya está en uso por otro emoji." + error_while_storing_emoji: "Lo sentimos, ha habido un error mientras se guardaba el emoji." topic_statuses: archived_enabled: "Este tema está ahora archivado. Se paraliza y no puede ser cambiado en ningún modo." archived_disabled: "Este tema se ha desarchivado. Se desparaliza y ahora puede ser cambiado." closed_enabled: "Este tema está ahora cerrado. No se admiten nuevas respuestas." closed_disabled: "Este tema ahora está abierto. Se admiten nuevas respuestas." + autoclosed_enabled_days: + one: "Este tema se cerró automáticamente después de 1 día. No se permiten nuevas respuestas." + other: "Este tema se cerró automáticamente después de %{count} días. No se permiten nuevas respuestas." + autoclosed_enabled_hours: + one: "Este tema se cerró automáticamente después de 1 hora. No se permiten nuevas respuestas." + other: "Este tema se cerró automáticamente después de %{count} horas. No se permiten nuevas respuestas." + autoclosed_enabled_minutes: + one: "Este tema se cerró automáticamente después de 1 minuto. No se permiten nuevas respuestas." + other: "Este tema se cerró automáticamente después de %{count} minutos. No se permiten nuevas respuestas." + autoclosed_enabled_lastpost_days: + one: "Este tema se cerró automáticamente 1 día después del último post. No se permiten nuevas respuestas." + other: "Este tema se cerró automáticamente %{count} días después del último post. No se permiten nuevas respuestas." + autoclosed_enabled_lastpost_hours: + one: "Este tema se cerró automáticamente 1 hora después del último post. No se permiten nuevas respuestas." + other: "Este tema se cerró automáticamente %{count} horas después del último post. No se permiten nuevas respuestas." + autoclosed_enabled_lastpost_minutes: + one: "Este tema se cerró automáticamente 1 minuto después del último post. No se permiten nuevas respuestas." + other: "Este tema se cerró automáticamente %{count} minutos después del último post. No se permiten nuevas respuestas." autoclosed_disabled: "El tema ahora está en abierto, se permiten respuestas." autoclosed_disabled_lastpost: "Este tema está ahora abierto. Se permiten nuevas respuestas." pinned_enabled: "Este tema ahora está destacado. Aparecerá en primer lugar en la lista de su categoría hasta que se deshaga el destacado de forma general por los moderadores o de forma particular por cada usuario para sí." @@ -958,7 +1068,7 @@ es: errors: "%{errors}" not_available: "No disponible. Prueba %{suggestion}?" something_already_taken: "Algo ha salido mal, quizá el nombre de usuario o el email ya han sido registrados. Prueba con el enlace de olvidé mi contraseña." - omniauth_error: "Lo sentimos, ha habido un error autorizando tu cuenta %{strategy}. ¿Quizás no has aprobado la autorización?" + omniauth_error: "Lo sentimos, hubo un error al autorizar tu cuenta. ¿Quizás no has aprobado la autorización?" omniauth_error_unknown: "Algo ha salido mal procesando tu inicio de sesión, por favor, vuelve a intentarlo." new_registrations_disabled: "El registro de nuevas cuentas no está permitido en este momento." password_too_long: "Las contraseñas están limitadas a 200 caracteres" @@ -998,7 +1108,7 @@ es: Esta invitación proviene de un usuario de confianza, por lo que puedes comentar en el tema inmediatamente. invite_forum_mailer: subject_template: "%{invitee_name} te invitó a unirte a %{site_domain_name}" - text_body_template: "%{invitee_name} lo ha invitado a unirse a\n\n > **%{site_title}**\n\n > %{site_description}\n\n Si esta interesado, haga click en el link de abajo: \n\n %{invite_link}\n\nEsta invitación proviene de un usuario de confianza, por lo cual no necesitara autenticarse.\n" + text_body_template: "%{invitee_name} te ha invitado a unirte a\n\n > **%{site_title}**\n\n > %{site_description}\n\n Si te interesa, haz clic en el siguiente enlace: \n\n %{invite_link}\n\nEsta invitación procede de un usuario de confianza, por lo que no necesitarás autenticarte.\n" invite_password_instructions: subject_template: "Asigna una contraseña para tu cuenta en %{site_name}" text_body_template: | @@ -1008,48 +1118,6 @@ es: %{base_url}/users/password-reset/%{email_token} test_mailer: subject_template: "[%{site_name}] Prueba de envío de email" - text_body_template: | - Este es un e-mail de prueba de - - [**%{base_url}**][0] - - La entrega de e-mail es complicada. Aquí hay unas cuantas cosas importantes que deberías de checar primero: - - - *Asegúrate* de ajustar la dirección `notificación e-mail` desde: correctamente en la configuración del sitio. **El dominio especificado en la dirección "desde" de los e-mails que envíes es el dominio que será validado**. - - - Conoce como puedes ver la fuente original de los e-mails en tu cliente de e-mail, para que puedas examinar las cabeceras y encontrar pistas importantes. En Gmail, es la opción "mostrar original" en el menu desplegable en la parte superior izquierda de cada e-mail. - - - **IMPORTANTE:** ¿Tiene tu ISP un registro DNS inverso proporcionado para asociar los nombres de dominio y la dirección IP de la cual envías e-mails? [Prueba tu registro PTR inverso][2] aquí. Si tu ISP no tiene proporcionado el registro DNS inverso apropiado, es muy improbable que cualquier e-mail que envíes sea entregado. - - - ¿Es correcto el [registro SPF][8] de tu dominio? [Prueba tu registro SPF][1] aquí. Nota que TXT es el tipo correcto oficial para SPF. - - - ¿Es correcto el [registro DKIM][3] de tu dominio? Esto mejorara significativamente la entrega de e-mails. [Prueba tu registro DKIM][7] aquí. - - - Si estas administrando tu propio servidor de correo, asegúrate que las IPs de tu servidor de correo no estén en [ninguna lista negra][4]. También verifica que este definitivamente enviando un `fully-qualified hostname` que resuelva en DNS el mensaje HELO. Si no, esto provocara que tus e-mails sean rechazados por varios servicios de correo. - - (La manera *fácil* es crear una cuenta gratis en [Mandrill][md] o [Mailgun][mg] o [Mailjet][mj], los cuales tienen generosos planes gratuitos para enviar e-mails esto será suficiente para comunidades pequeñas. Aunque vas a necesitar de todos modos ajustar los registros SPF y DKIM en tus DNS.) - - Esperamos que hayas recibido esta prueba de entrega de e-mail OK! - - Buena suerte, - - Tus amigos en [Discourse](http://www.discourse.org) - - [0]: %{base_url} - [1]: http://www.kitterman.com/spf/validate.html - [2]: http://mxtoolbox.com/ReverseLookup.aspx - [3]: http://www.dkim.org/ - [4]: http://whatismyipaddress.com/blacklist-check - [5]: %{base_url}/unsubscribe - [7]: http://dkimcore.org/tools/dkimrecordcheck.html - [8]: http://www.openspf.org/SPF_Record_Syntax - [md]: http://mandrill.com - [mg]: http://www.mailgun.com/ - [mj]: http://www.mailjet.com/pricing - - ---- - - Debe de haber un pie de página en cada correo que tu envíes, vamos a maquetar uno. Este e-mail fue enviado por Nombre de la Compañia, 55 Main Street, Anytown, USA 12345. Si deseas dejar de recibir futuros e-mails, [De clic aquí para cancelar su suscripción][5]. new_version_mailer: subject_template: "[%{site_name}] Nueva versión de Discourse, actualización disponible" text_body_template: | @@ -1090,59 +1158,43 @@ es: other: "Estos reportes fueron enviados desde hace %{count} horas." please_review: "Por favor, revísalos." post_number: "post" + how_to_disable: 'Puedes desactivar o cambiar la frecuencia de este email de recordatorio mediante la opción "notify about flags after".' subject_template: one: "1 reporte esperando ser atendidos." other: "%{count} reportes esperando ser atendidos." flag_reasons: - off_topic: "Tu post fue reportado como **off-topic**: la comunidad piensa que no se ajusta al asunto del tema, definido actualmente por su título y primer post." - inappropriate: "Tu post ha sido reportado como **inapropiado**: la comunidad piensa que puede ser ofensivo, abusivo, o que supone una violación de las [directrices de la comunidad](/guidelines)." - spam: "Tu post fue reportado como **spam**: la comunidad piensa que se trata de un anuncio publiciario, no útil o de nula relevancia para el tema, sino de naturaleza promocional." - notify_moderators: "Tu post ha sido reportado **a moderación**: la comunidad piensa que algo sobre tu post requiere la intervención de un moderador." + off_topic: "Tu post fue reportado como **off-topic**: la comunidad piensa que no se ajusta debidamente al tema, definido por el título o el primer post." + inappropriate: "Tu post fue reportado como **inapropiado**: la comunidad piensa que es ofensivo, abusivo o que vulnera alguna de [nuestros consejos de uso](/guidelines)." + spam: "Tu post fue reportado como **spam**: la comunidad piensa que se trata de un anuncio, algo de naturaleza promocional en vez de resultar útil o relevante para lo que se espera del tema." + notify_moderators: "Tu post fue reportado para la **atención de un moderador**: la comunidad piensa que algo de este post requiere la intervención manual de un miembro del staff." flags_dispositions: agreed: "Gracias por avisarnos. Coincidimos con tu reporte en que hay un problema y lo estamos revisando." agreed_and_deleted: "Gracias por avisarnos. Coincidimos con tu reporte en que hay un problema y hemos quitado el post." disagreed: "Gracias por hacérnoslo saber. Estamos revisándolo." deferred: "Gracias por hacérnoslo saber. Estamos revisándolo." deferred_and_deleted: "Gracias por hacérnoslo saber. Hemos eliminado el post." + temporarily_closed_due_to_flags: "Este tema está cerrado temporalmente por un elevado número de denuncias por parte de la comunidad" system_messages: post_hidden: subject_template: "Post oculto al haber sido reportado por la comunidad" - usage_tips: - text_body_template: "Este mensaje privado tiene unos cuantos consejos para ayudarte a empezar.\n\n## Sigue desplazándote\n\nNo hay botones de siguiente página o número de páginas - para leer más, **¡solo sigue desplazándote hacia abajo!**\n\nTan pronto como llegue nuevos posts, aparecerán automáticamente.\n\n## ¿Dónde estoy?\n\n- Para buscar, ver tu página de usuario, o el menú, usa el **los botones con el icono en la parte superior derecha**.\n\n- Cualquier título de un tema te llevara al siguiente post sin leer. Usa el tiempo de la última actividad y el contador de posts para ir al inicio o al final.\n\n- Mientras estés leyendo un tema, regresa al inicio ↑ seleccionando el título del tema. Selecciona la barra de proceso verde en la parte inferior derecha para mostrar todos los controles de navegación, o usa las teclas inicio y fin.\n\n\n\n## ¿Cómo respondo?\n\n- Para responder para el tema en general, usa el botón Responder al final de la página.\n\n- Para responder a un post especifico, usa el botón Responder en ese post.\n\n- Para llevar la conversación a una dirección diferente, pero manteniéndola enlazada, usa Responder como Tema enlazado a la derecha del post.\n\nPara citar a alguien en tu respuesta, selecciona el texto que desees citar, y presiona cualquier botón de Responder.\n\n\n\nPara mencionar a alguien en tu respuesta, menciona su nombre. Escribe `@` y el autocompletado aparecerá.\n\ - \n\n\nPara [Emoji estándares](http://www.emoji.codes/), solo empieza a teclear `:` o usa las caritas tradicionales `:)` :smile:\n\n## ¿Qué más puedo hacer?\n\nHay botones de acción al final de cada post.\n\n\n\n\tPara hacerle saber a alguien que has disfrutado de su post, usa el botón **me gusta**. Si ves algún problema con el post, privadamente hazle saber a nuestro staff, por medio del botón **reportar**.\n\nAdemás puedes **compartir** un link a un post, o **marcar** lo para futuras referencias en tu página de usuario.\n\n## ¿Quién me está hablando?\n\nCuando alguien responde a tu post, cita tu post, o menciona tu `@nombre_de_usuario`, un número aparecerá inmediatamente en la parte superior derecha de la página. Usalo para\ - \ acceder a tus **notificaciones**.\n\n\n\nNo te preocupes por perderte respuestas - un e-mail te será enviado cuando te respondan directamente (o te envíen mensajes privados) en caso que no te encuentres en línea.\n\n## ¿Cuando son las conversaciones nuevas?\n\nPor defecto todas las conversaciones que tengan menos de 2 días de ser publicadas son consideradas nuevas, y cualquier conversación en la que hayas participado (respondido, creado o leído por largo periodo de tiempo) será automáticamente rastreada.\n\nVeras un nuevo indicador en azul y con un número a lado de estos temas:\n\n\n\nPuedes cambiar el estado individual de la notificación a través del control en la parte inferior del tema (esto puede ser ajustado por cada categoría).\ - \ Para cambiar como mantienes un rastro de los temas, o de la definición de nuevo, mira [tus preferencias de usuario](/my/preferences).\n\n## ¿Por qué no puedo ver ciertas cosas?\n\nNuevos usuarios están de alguna manera limitados por razones de seguridad. Conforme participes aquí, ganaras la confianza de la comunidad, conviértete en un ciudadano completo, y esas limitaciones serán automáticamente removidas. A un [nivel de confianza](https://meta.discourse.org/t/what-do-user-trust-levels-do/4924) suficiente, ganaras aún más habilidades para ayudarnos a administrar juntos nuestra comunidad.\n" + text_body_template: | + Hola, + + Este se un mensaje automatizado de %{site_name} para informarte de que tu post fue ocultado. + + %{base_url}%{url} + + %{flag_reason} + + Varios miembros de la comunidad reportaron este post antes de ocultarlo, por favor considera revisar tu post para reflejar la retroalimentación de los demás miembros. **Puedes editar tu post después de %{edit_delay} minutos, y será automáticamente mostrado de vuelta.** Esto incrementara tu nivel de confianza. + + Sin embargo, si el post es ocultado por la comunidad una segunda vez, este se mantendrá oculto hasta que sea atendido por el staff – y tal vez vez se tomen medidas, incluyendo la posible suspensión de tu cuenta. + + Para más información, por favor consulta nuestras [reglas de comunidad](%{base_url}/guidelines). welcome_user: subject_template: "¡Bienvenido a %{site_name}!" - text_body_template: | - ¡Gracias por unirte a %{site_name}! ¡Bienvenido! - - %{new_user_tips} - - Creemos en un [comportamiento civilizado de la comunidad](%{base_url}/guidelines) en todo momento. - - ¡Disfruta tu estancia! welcome_invite: subject_template: "¡Bienvenido a %{site_name}!" - text_body_template: | - Gracias por aceptar tu invitación a %{site_name} -- ¡bienvenido! - - Hemos generado automáticamente un nombre de usuario por ti: **%{username}**, pero puedes cambiarlo en cualquier momento visitando [tu perfil de usuario][prefs]. - - Para iniciar sesión otra vez, puedes: - - 1. Iniciar sesión usando cualquier método que desees -- pero debe de resolver a la **misma dirección e-mail** que tu recibiste en tu invitación original. ¡De otra manera no seremos capaces de reconocer que eres tu! - - 2. Crea una contraseña unica para [tu perfil de usuario][prefs], y úsala para iniciar sesión. - - %{new_user_tips} - - Creemos en un [comportamiento civilizado de la comunidad](%{base_url}/guidelines) en todo momento. - - ¡Disfruta tu estancia! - - [prefs]: %{user_preferences_url} backup_succeeded: subject_template: "La copia de seguridad se completo exitosamente" text_body_template: "La copia de seguridad fue exitosa.\nVisita la [sección admin > copia de seguridad](/admin/backups) para descargar tu nueva copia de seguridad." @@ -1183,16 +1235,10 @@ es: %{logs} ``` csv_export_succeeded: - subject_template: "La exportación de datos se completó satisfactoriamente" - text_body_template: | - Los datos fueron exportados exitosamente. - - Descarga el archivo CSV: %{file_name} - - El enlace de descarga al archivo CSV expirara después de 48 horas. + subject_template: "Exportación de datos completada" csv_export_failed: - subject_template: "La exportación falló" - text_body_template: "La exportación ha fallado. Por favor revisa los logs." + subject_template: "La exportación de datos falló" + text_body_template: "Lo sentimos, pero la exportación de datos falló. Por favor, verifica los registros o contacta a un miembro del staff." email_reject_trust_level: subject_template: "Error con el email -- insuficiente nivel de confianza" text_body_template: | @@ -1201,10 +1247,19 @@ es: Tu cuenta no tiene el nivel de confianza suficiente para publicar nuevos temas a esta dirección de correo electrónico. Si crees que esto es un error, contacta con algún miembro del Staff. email_reject_no_account: subject_template: "Problema con el e-mail -- Cuenta Desconocida" + text_body_template: | + Lo sentimos, pero tu email para %{destination} (titulado %{former_title}) se ha rechazado. + + No hay una cuenta de usuario asociada con esta dirección de email. Intenta enviarlo con otra dirección de email o contacta con algún miembro del Staff. email_reject_empty: subject_template: "Error con el email -- No hay contenido" + text_body_template: "Lo sentimos, pero tu email para %{destination} (titled %{former_title}) no funcionó.\n\nNo pudimos encontrar contenido en este e-mail. Asegúrate de que hayas escrito algo al inicio del e-mail - no podemos analizar respuestas en una sola línea.\n\nSi estás teniendo este problema y _sí_ incluiste contenido, intenta otra vez con contenido HTML incluido en tu e-mail (no solo texto plano). \n" email_reject_parsing: subject_template: "Error con el email -- Contenido desconocido" + text_body_template: | + Lo sentimos, pero tu email para %{destination} (titulado %{former_title}) no funcionó. + + No hemos encontrado tu respuesta en el email. **Asegúrate de escribir tu entera respuesta al principio del email** - no podemos analizar respuestas entre líneas. email_reject_post_error: subject_template: "Error con el email -- Error de publicación" text_body_template: | @@ -1229,10 +1284,22 @@ es: La clave de respuesta proporcionada no es válida o es desconocida, por lo que no sabemos a qué responde este email. Contacta con algún miembro del staff. email_reject_destination: subject_template: "Problema con el e-mail -- Desconocido Para: Dirección" + text_body_template: | + Lo sentimos, pero tu email para %{destination} (titulado %{former_title}) no funcionó. + + No se ha podido reconocer ninguna de las direcciones destino. Por favor, asegúrate de que la dirección del sitio está en Para: (no CC ni BCC), y que lo estás enviando a la dirección de e-mail proporcionada por los administradores. email_reject_topic_not_found: subject_template: "Problema con el email -- Tema No Encontrado" + text_body_template: | + Lo sentimos, pero tu email para %{destination} (titulado %{former_title}) no funcionó. + + No hemos encontrado el tema, debe haber sido eliminado. Si crees que esto es un error, contacta a un administrador. email_reject_topic_closed: subject_template: "Problema con el email -- Tema Cerrado" + text_body_template: | + Lo sentimos, pero tu email para %{destination} (titulado %{former_title}) no funcionó. + + El tema está cerrado a respuestas. Si crees que esto es un error, contacta a un administrador. email_error_notification: subject_template: "Error con el email -- Autenticación POP errónea" text_body_template: | @@ -1353,10 +1420,12 @@ es: --- %{respond_instructions} digest: + why: "Un breve resumen de %{site_link} desde tu última visita el %{last_seen_at}" subject_template: "[%{site_name}] Resumen del %{date}" new_activity: "Actividad nueva en tus temas y posts:" top_topics: "Posts populares" other_new_topics: "Temas populares" + unsubscribe: "Este resumen es enviado desde %{site_link} cuando pasa un tiempo desde tu última visita. Para cancelar tu suscripción %{unsubscribe_link}." click_here: "clic aquí" from: "resumen de %{site_name}" read_more: "Leer más" @@ -1381,7 +1450,7 @@ es: Si no formulaste esta petición, por favor, ignora este correo. - Haz click en el siguiente enlace para escoger una contraseña: + Haz clic en el siguiente enlace para escoger una contraseña: %{base_url}/users/password-reset/%{email_token} account_created: subject_template: "[%{site_name}] Tu nueva cuenta" @@ -1398,21 +1467,6 @@ es: %{base_url}/users/authorize-email/%{email_token} signup_after_approval: subject_template: "Tú solicitud ha sido aprobada en %{site_name}!" - text_body_template: | - ¡Bienvenido a %{site_name}! - - Un miembro del staff aprobó tu cuenta en %{site_name}. - - Da clic en el siguiente enlace para confirmar y activar tu nueva cuenta: - %{base_url}/users/activate-account/%{email_token} - - Si el enlace de arriba no es clicleable, trata copiando y pegandolo en la barra de direcciones de tu navegador web. - - %{new_user_tips} - - Creemos en un [comportamiento civilizado de la comunidad](%{base_url}/guidelines) en todo momento. - - ¡Disfruta tu estancia! signup: subject_template: "[%{site_name}] Activa tu nueva cuenta" text_body_template: | @@ -1443,9 +1497,9 @@ es: pasted_image_filename: "Imagen pegada" store_failure: "Falló el almacenamiento de la subida #%{upload_id} para el usuario #%{user_id}." attachments: - too_large: "Lo sentimos, el archivo que estás intentando subir es demasiado grande (el tamaño máximo es %{max_size_kb}%kb)." + too_large: "Lo sentimos, el archivo que estás intentando subir es demasiado grande (el tamaño máximo es %{max_size_kb}%KB)." images: - too_large: "Lo sentimos, la imagen que estás intentando subir es demasiado grande (el tamaño máximo es %{max_size_kb}%kb). Por favor, modifica sus dimensiones y prueba otra vez." + too_large: "Lo sentimos, la imagen que estás intentando subir es demasiado grande (el tamaño máximo es %{max_size_kb}%KB). Por favor, modifica sus dimensiones y prueba otra vez." fetch_failure: "Lo sentimos, hubo un error al recuperar la imagen." unknown_image_type: "Lo sentimos, pero el archivo que estás intentado subir no parece ser una imagen." size_not_found: "Lo sentimos, pero no hemos podido determinar el tamaño de la imagen. ¿Quizás el archivo está corrupto?" @@ -1471,102 +1525,13 @@ es: guidelines: "Directrices" privacy: "Privacidad" edit_this_page: "Edita esta página" + csv_export: + boolean_yes: "Sí" + boolean_no: "No" static_topic_first_reply: | Edita el primer post de este tema para cambiar el contenido de la página %{page_name}. guidelines_topic: title: "Preguntas Frecuentes / Directrices" - body: | - - - ## [Este es un Lugar Civilizado para Debate Público](#civilized) - - Por favor trata a este foro de discusión con el mismo respeto que un parque público. Nosotros también somos una comunidad compartida de recursos — un lugar donde compartimos habilidades, conocimiento e intereses a través de las conversaciones actuales. - - Estas no son reglas rápidas y duras, meramente ayudan al juicio humano de nuestra comunidad. Usa estas reglas para para mantener un lugar limpio y bien iluminado para el debate público y civilizado. - - - - ## [Mejora el debate](#improve) - - Ayúdanos a hacer este un gran lugar para la discusión, tratando de mejorar la discusión de alguna manera, aunque sea pequeña. Si no estás seguro que tu post añade algo a la conversación, piensa sobre lo que quieres decir y trata después de nuevo. - - Los temas debatidos aquí nos importan, y queremos que ti también te importen. Se respetoso de los temas y de la gente que debate en ellos, incluso si tu difieres con algo de lo que se dijo. - - Una manera de mejorar la discusión es descubrir temas que ya estén ocurriendo. Por favor pasa algo de tiempo navegando los temas aquí antes de responder o iniciar un tema nuevo, de esta manera tendrás una mejor oportunidad de conocer a otras personas que comparten tus intereses. - - - - ## [Se Amable, Incluso Cuando Tu Difieras](#agreeable) - - Tal vez desees responder a algo en lo que no estés de acuerdo. Está bien. Pero, recuerda _critica ideas, no personas_. Por favor evita: - - * Insultar u ofender. - * Ataques personales. - * Responder a un tema por su autor en lugar de su contenido. - * Contradicción exabrupto. - - En lugar de eso, provee contra argumentos razonados que mejoren la conversación. - - - - ## [Tu participación cuenta](#participate) - - Las conversaciones que nosotros tenemos aquí ajuntan el tono para todos. Ayúdanos a influir en el futuro de esta comunidad escogiendo entablar in discusiones que hagan este foro un lugar interesante para estar — y evita a aquellos que no. - - Discourse provee herramientas que permiten a la comunidad a identificar colectivamente las mejores (y peores) contribuciones: favoritos, marcadores, 'me gusta', reportes, ediciones, entre otros. Usa estas herramientas para mejorar tu propia experiencia, y también la de todos los demás. - - Vamos a tratar de dejar nuestro parque mejor de lo que lo encontramos. - - - - ## [Si ves un problema, repórtalo](#flag-problems) - - Moderadores tienen autoridad especial; ellos son responsable de este foro. Pero tu también lo eres. Con tu ayuda, los moderadores pueden ser facilitadores de la comunidad, no solamente conserjes o policías. - - Cuando tú veas un mal comportamiento, no respondas. Solo fomenta el mal comportamiento reconociéndolo, consume tu energía, y desperdicia el tiempo de todos. _Solo repórtalo_. Si suficientes reportes son adquiridos, serán tomadas medidas, ya sean automáticas o por la intervención de un moderador. - - Con el fin de mantener nuestra comunidad, los moderadores se reservan el derecho de remover cualquier contenido y cualquier cuenta de usuario por cualquier motivo, en cualquier momento. Los moderadores no ven por adelantado ningún nuevo post de ninguna manera; los moderadores y los operadores del sitio no son responsables por ningún contenido publicado por la comunidad. - - - - ## [Siempre se civilizado](#be-civil) - - Nada sabotea una sana conversación como la rudeza: - - * Se civilizado. No publiques nada que una persona razonable consideraría ofensivo, abusivo, o de odio. - * Mantenlo limpio. No publiques nada obsceno o sexualmente explícito. - * Respeta a los otros. No acoses o molestes a alguien, imites a personas, o expongas su información privada. - * Respeta nuestro foro. No publiques spam o cometas actos vandálicos en el foro. - - Estos no son términos concretos con definiciones precisas — evita incluso la _aparición_ de cualquiera de estas cosas. Si no estás seguro, pregúntate a ti mismo como te sentirías si tu post apareciera en la portada del New York Times. - - Este es un foro público, y los motores de búsqueda indexan estas discusiones. Mantén el lenguaje, los enlaces e imágenes seguras para las familias y amigos. - - - - ## [Mantenlo ordenado](#keep-tidy) - - Has un esfuerzo para poner las cosas en su lugar, así podemos pasar más tiempo discutiendo y menos limpiando. Así que: - - * No inicies un nuevo tema en la categoría incorrecta. - * No publiques la misma cosa en múltiples temas. - * No publiques respuestas sin contenido. - * No diverjas un tema cambiando el centro de atención. - * No firmes tus posts — cada post tiene tu información de perfil adjuntado. - - En lugar de publicar “+1” o “Concuerdo”, usa el botón de 'me gusta'. En vez de tomar un tema existente en una dirección radicalmente diferente, usa Responder como Nuevo Tema. - - - - ## [Publica solo tus propias cosas](#stealing) - - No publiques ningún medio digital que le pertenezca a alguien más sin su permiso. No publiques descripciones, enlaces o métodos para robar la propiedad intelectual de alguien más (software video, audio, imágenes), o cualquier otro que rompa las leyes. - - - - ## [Términos de Servicio](#tos) - - Si, la jerga legal es aburrida, pero debemos de protegernos – y por extensión, a ti y tus datos – contra personas hostiles. Tenemos [Términos de Servicio] tu (y nuestro) comportamiento y derechos relacionados al contenido, privacidad y leyes. Para usar este servicio, debes de aceptar y seguir nuestros [Términos de Servicio](/tos). tos_topic: title: "Términos de Servicio" privacy_topic: diff --git a/config/locales/server.fi.yml b/config/locales/server.fi.yml index f3e8fb0a07..56f34bc633 100644 --- a/config/locales/server.fi.yml +++ b/config/locales/server.fi.yml @@ -6,6 +6,9 @@ # https://www.transifex.com/projects/p/discourse-org/ fi: + stringex: + characters: + number: "-" i18n: transliterate: rule: @@ -17,11 +20,6 @@ fi: short_date_no_year: "D. MMM" short_date: "D. MMMM[ta] YYYY" long_date: "D. MMMM[ta] YYYY, H:mm" - time: - formats: - short: "%-d.%-m.%Y" - short_no_year: "%d. %b" - date_only: "%e. %Bta %Y" title: "Discourse" topics: "Ketjut" posts: "viestit" @@ -30,13 +28,50 @@ fi: log_in: "Kirjaudu sisään" via: "%{username} sivustolta %{site_name}" is_reserved: "on varattu" - purge_reason: "Poistettiin automaattisesti, koska tiliä ei oltu aktivoitu riittävässä ajassa" + purge_reason: "Poistettu automaattisesti hylättynä, aktivoimattomana tilinä" disable_remote_images_download_reason: "Linkattujen kuvien lataaminen poistettiin käytöstä vähäisen tallennustilan vuoksi." errors: + format: '%{attribute} %{message}' messages: too_long_validation: "on rajoitettu %{max} merkkiin; sinä syötit %{length} merkkiä." invalid_boolean: "Totuusarvomuuttuja ei kelpaa" taken: "on jo varattu" + accepted: täytyy hyväksyä + blank: ei voi olla tyhjä + present: täytyy olla tyhjä + confirmation: "ei vastaa %{attribute}" + empty: ei voi olla tyhjä + equal_to: täytyy olla sama kuin %{count} + even: täytyy olla parillinen + exclusion: on varattu + greater_than: täytyy olla suurempi kuin %{count} + greater_than_or_equal_to: täytyy olla yhtä suuri, tai suurempi kuin %{count} + inclusion: ei ole listalla + invalid: ei kelpaa + less_than: täytyy olla vähemmän kuin %{count} + less_than_or_equal_to: täytyy olla yhtä suuri, tai vähemmän kuin %{count} + not_a_number: ei ole numero + not_an_integer: täytyy olla kokonaisluku + odd: täytyy olla pariton + record_invalid: 'Validointi epäonnistui: %{errors}' + restrict_dependent_destroy: + one: "Tietuetta ei voi poistaa, koska siitä riippuva tietue %{record} on olemassa" + many: "Tietueita ei voi poistaa, koska siitä riippuva tietue %{record} on olemassa." + too_long: + one: on liian pitkä (enintään 1 merkki) + other: on liian pitkä (enintään %{count} merkkiä) + too_short: + one: on liian lyhyt (vähintään 1 merkki) + other: on liian lyhyt (vähintään %{count} merkkiä) + wrong_length: + one: on väärän mittainen (pitäisi olla 1 merkki) + other: on väärän mittainen (pitäisi olla %{count} merkkiä) + other_than: "pitää olla muu kuin %{count}" + template: + body: 'Seuraavien kenttien kanssa oli ongelmia:' + header: + one: 1 virhe esti tallentamasta tätä %{model} + other: '%{count} virhettä esti tallentamasta tätä %{model}' embed: load_from_remote: "Viestin lataamisessa tapahtui virhe." bulk_invite: @@ -56,7 +91,7 @@ fi: more_replies: one: "1 vastaus lisää" other: "%{count} vastausta lisää" - loading: "Lataa keskustelua..." + loading: "Haetaan viestejä..." permalink: "Suora linkki" imported_from: "Tässä viestiketju keskustellaan kirjoituksesta osoitteessa %{link}" in_reply_to: "▶ %{username}" @@ -104,6 +139,7 @@ fi: latest: "Tuoreimmat viestiketjut" hot: "Kuumat ketjut" too_late_to_edit: "Tämä viesti luotiin liian kauan sitten. Sitä ei voi enää muokata tai poistaa." + excerpt_image: "kuva" groups: errors: can_not_modify_automatic: "Et voi muokata automaattista ryhmää" @@ -193,8 +229,8 @@ fi: base: warning_requires_pm: "Voit liittää varoituksia vain yksityisviesteihin." too_many_users: "Voit lähettää varoituksia vain yhdelle käyttäjälle kerrallaan." - archetype: cant_send_pm: "Pahoittelut, et voi lähettää tälle käyttäjälle yksityisviestiä." + no_user_selected: "Sinun täytyy valita kelpaava käyttäjä." user: attributes: password: @@ -365,26 +401,35 @@ fi: activation: action: "Vahvista käyttäjätilisi" already_done: "Pahoittelut, tämän tilin varmennuslinkki ei ole enää voimassa. Ehkäpä tili on jo varmennettu?" + please_continue: "Tilisi on nyt varmennettu; sivu ohjautuu palstan etusivulle." continue_button: "Jatka sivustolle %{site_name}" welcome_to: "Tervetuloa sivustolle %{site_name}!" approval_required: "Valvojan täytyy käsin hyväksyä uusi tilisi ennen kuin pääset palstalle. Saat sähköpostin, kun tilisi on hyväksytty." post_action_types: off_topic: title: 'Asiaankuulumaton' + description: 'Tämä viesti ei ole relevantti tässä ketjussa, jonka määrittelee ketjun otsikko ja aloitusviesti, ja pitäisi todennäköisesti siirtää toiselle alueelle.' long_form: 'liputti tämän asiaankuulumattomaksi' spam: title: 'Roskaposti' description: 'Tämä viesti on mainos. Se ei ole hyödyllinen tai relevantti tässä ketjussa, vaan luonteeltaan mainostamista.' long_form: 'liputti tämän roskapostiksi' + email_title: '"%{title}" liputettiin roskapostiksi' + email_body: "%{link}\n\n%{message}" inappropriate: title: 'Asiaton' description: 'Tämän viestin sisältö on loukkaava, herjaava tai ristiriidassa palstan sääntöjen kanssa.' long_form: 'liputti tämän asiattomaksi' notify_user: + title: 'Lähetä yksityisviesti käyttäjälle @{{username}}' description: 'Tämä viesti sisältää jotain, mistä haluan keskustellä tämän henkilön kanssa suoraan ja yksityisesti. Tämä ei aiheuta liputusta.' + long_form: 'lähetä käyttäjälle yksityisviesti' email_title: 'Viestisi ketjussa "%{title}"' email_body: "%{link}\n\n%{message}" notify_moderators: + title: "Jotain muuta" + description: 'Valvojan tulee huomioida tämä viesti muusta, kuin yllä listatusta syystä.' + long_form: 'liputit tämän valvojille tiedoksi' email_title: 'Viesti ketjussa "%{title}" kaipaa valvojan huomiota' email_body: "%{link}\n\n%{message}" bookmark: @@ -409,7 +454,9 @@ fi: description: 'Tämän ketjun sisältö on loukkaava, herjaava tai ristiriidassa palstan sääntöjen kanssa.' long_form: 'liputti tämän asiattomaksi' notify_moderators: + title: "Jotain muuta" description: 'Valvojan tulee huomioida tämä ketju palstan sääntöjen, palveluehtojen, tai muun syyn vuoksi.' + long_form: 'liputit tämän valvojille tiedoksi' email_title: 'Viestiketju"%{title}" kaipaa valvojan huomiota' email_body: "%{link}\n\n%{message}" flagging: @@ -510,6 +557,46 @@ fi: title: "Parhaat viitatut ketjut" xaxis: "Ketju" num_clicks: "Klikkausta" + page_view_anon_reqs: + title: "Anonyymejä" + xaxis: "Päivä" + yaxis: "Anonyymeien sivun katseluita" + page_view_logged_in_reqs: + title: "Kirjautuneita" + xaxis: "Päivä" + yaxis: "Kirjautuneiden sivunkatseluita" + page_view_crawler_reqs: + title: "Hakurobotteja" + xaxis: "Päivä" + yaxis: "Hakurobottien sivunkatseluita" + page_view_total_reqs: + title: "Yhteensä" + xaxis: "Päivä" + yaxis: "Katseluita yhteensä" + http_background_reqs: + title: "Taustajärjestelmä" + xaxis: "Päivä" + yaxis: "Pyyntöjä sivun päivittämiseen ja seuraamiseen" + http_2xx_reqs: + title: "Status 2xx (OK)" + xaxis: "Päivä" + yaxis: "Onnistuneita pyyntöjä (Status 2xx)" + http_3xx_reqs: + title: "HTTP 3xx (Uudelleenohjaus)" + xaxis: "Päivä" + yaxis: "Uudelleenohjauksia (Status 3xx)" + http_4xx_reqs: + title: "HTTP 4xx (Asiakasvirhe)" + xaxis: "Päivä" + yaxis: "Asiakasvirheitä (Status 4xx)" + http_5xx_reqs: + title: "HTTP 5xx (Palvelinvirhe)" + xaxis: "Päivä" + yaxis: "Palvelinvirheitä (Status 5xx)" + http_total_reqs: + title: "Yhteensä" + xaxis: "Päivä" + yaxis: "Pyyntöjä yhteensä" dashboard: rails_env_warning: "Palvelintasi ajetaan %{env} moodissa." ruby_version_warning: "Käytät Rubyn versiota 2.0.0 jonka kanssa on tunnettuja ongelmia. Päivitä patch level 247 tai uudempaan." @@ -528,15 +615,14 @@ fi: s3_backup_config_warning: 'Palvelin on konfiguroitu lataamaan varmuuskopiot s3:een, mutta vähintään yksi arvoista s3_access_key_id, s3_secret_access_key tai s3_backup_bucket ei ole asetettu. Päivitä arvot sivuston asetuksissa.Voit lukea lisätietoja oppaasta "How to set up image uploads to S3?".' image_magick_warning: 'Palvelin on konfiguroitu luomaan esikatselukuvia suurista kuvista, mutta ImageMagickia ei ole asennettu. Asenna ImageMagick paketinhallinnasta tai lataamalla uusin versio.' failing_emails_warning: 'Sähköpostitehtävä on epäonnistunut %{num_failed_jobs} kertaa. Tarkista config/discourse.conf tiedostosta, että sähköpostiasetukset ovat kunnossa. Tarkastele epäonnistuneita tehtäviä Sidekiqissa.' - default_logo_warning: "Et ole asettanut sivustolle logoja. Päivitä logo_url, logo_small_url, ja favicon_url sivuston asetuksissa." - contact_email_missing: "Et ole asettanut sivustolle sähköpostiosoitetta. Päivitä arvo contact_email sivuston asetuksissa." - contact_email_invalid: "Sivuston sähköpostiosoite ei kelpaa. Päivitä arvo contact_email sivuston asetuksissa." - title_nag: "Keskustelupalstan otsikko on yhä oletusarvossa. Lisää palstalle nimi sivuston asetuksissa." - site_description_missing: "Site_description astus on tyhjä. Kirjoita palstalle lyhyt kuvaus Sivuston asetuksissa." + default_logo_warning: "Aseta sivustolle logot. Päivitä logo_url, logo_small_url, ja favicon_url sivuston asetuksissa." + contact_email_missing: "Aseta yhteystietoihin sähköpostiosoite, josta sinut saa tavoitettua sivustoon liittyvissä kiireellisissä asioissa. Voit syöttää sen sivuston asetuksissa." + contact_email_invalid: "Sivuston sähköpostiosoite ei kelpaa. Muokkaa sitä sivuston asetuksissa." + title_nag: "Aseta keskustelupalstalle nimi. Voita muokata sitä sivuston asetuksissa." + site_description_missing: "Aseta yhden lauseen mittainen kuvaus palstasta, joka näkyy hakutuloksissa. Muokkaa asetusta site_description sivuston asetuksissa." consumer_email_warning: "Sivusto on konfiguroitu käyttämään Gmailia (tai muuta yksityishenkilöille suunnattua sähköpostipalvelua) sähköpostin lähettämiseen. Gmail rajoittaa lähetettävien sähköpostien määrää. Harkitse sähköpostipalveluntarjoajaa, kuten mandrill.com varmistaaksesi sähköpostin lähetyksen." - access_password_removal: "Sivustosi käytti access_password asetusta, joka on poistettu. Login_required ja must_approve_users asetukset on otettu käyttöön, joita suositellaan käytettävän sen sijaan. Voit muuttaa asetuksia sivuston asetuksissa. Muista käydä hyväksymässä odottavat käyttäjätilit. (Tämä viesti katoaa 2 päivän kuluttua)" - site_contact_username_warning: "site_contact_username asetus on tyhjä. Päivitä arvo Sivuston asetuksissa. Aseta siihen ylläpitäjän käyttäjänimi, joka on lähettäjänä järjestelmän viesteissä." - notification_email_warning: "notification_email -arvoa ei ole asetettu. Päivitä arvo Sivuston asetuksissa." + site_contact_username_warning: "Aseta käyttäjänimi, jonka nimissä palsta automaattiset yksityisviestit lähetetään. Muokkaa asetusta site_contact sivuston asetuksissa." + notification_email_warning: "Palstan sähköpostien lähettäjäosoite ei ole kelvollinen, josta johtuen sähköpostit eivät mene perille luotettavasti. Aseta toimiva sähköpostiosoite kohtaan notification_email sivuston asetuksissa." content_types: education_new_reply: title: "Uuden käyttäjän opastus: Ensimmäinen vastaus" @@ -567,6 +653,7 @@ fi: description: "HTML joka lisätään jokaisen sivun ylälaitaan (headerin jälkeen, ennen navigaatiota tai ketjun otsikkoa)." bottom: title: "Sivun alaosa" + description: "HTML, joka lisätään ennen elementtiä." site_settings: censored_words: "Sanat, jotka korvataan automaattisesti merkeillä ■■■■" delete_old_hidden_posts: "Poista automaattisesti kaikki yli 30 päivää piilotettuna olleet viestit." @@ -584,9 +671,9 @@ fi: allow_duplicate_topic_titles: "Salli ketjun luominen identtisellä otsikolla." unique_posts_mins: "Kuinka monta minuuttia, kunnes käyttäjä voi luoda uudestaan saman sisältöisen viestin" educate_until_posts: "Näytä uuden käyttäjän ohje, kun käyttäjä alkaa kirjoittamaan ensimmäistä (n) viestiään viestikenttään." - title: "Palstan lyhyt otsikko, käytetään title tagissa." - site_description: "Kuvaile sivustoa yhdellä lauseella, käytetään meta description tagissa." - contact_email: "Sähköpostiosoite, josta sivuston henkilökunnan saa tavoitettua. Tärkeät tiedotteet kriittisistä päivityksistä lähetetään tähän osoitteeseen." + title: "Palstan nimi, käytetään title tagissa." + site_description: "Kuvaile sivustoa yhdellä lauseella, jota käytetään meta description tagissa." + contact_email: "Sähköpostiosoite, josta sivuston henkilökunnan saa tavoitettua. Tänne lähetetään sivuston kriittiset ilmoitukset, kuten käsittelemättömät liput ja näytetään /about sivun yhteystietona." queue_jobs: "VAIN KEHITTÄJILLE! VAROITUS! Oletusarvona tehtävät asetetaan sidekiq jonoon. Jos poistetaan käytöstä, sivusto ei enää toimi." crawl_images: "Lataa linkatut kuvat kuvan dimensioiden määrittamiseksi." download_remote_images_to_local: "Muunna linkatut kuvat liitetiedostoiksi lataamalla ne; tämä estää kuvien rikkoontumisen vanhentuneiden linkkien vuoksi." @@ -606,11 +693,11 @@ fi: post_excerpt_maxlength: "Viestin katkelman merkkien maksimimäärä." post_onebox_maxlength: "Discourse-viestin Onebox-esikatselun merkkien maksimimäärä." onebox_domains_whitelist: "Lista verkkotunnuksista, joilta sallitaan onebox-esikatselut; näiden tunnusten pitäisi tukea OpenGraphia tai oEmbedia. Testaa ne osoitteessa http://iframely.com/debug" - logo_url: "Logo palstan vasemmassa yläkulmassa, esim: http://esimerkki.fi/logo.png" + logo_url: "Logo sivun vasemmassa ylälaidassa. Jos jätetään tyhjäksi, näytetään sen tilalla palstan nimi." digest_logo_url: "Vaihtoehtoinen logo, jota käytetään tiivistelmäsähköpostin yläreunassa. Jos asetus jätetään tyhjäksi 'logo_url' käytetään tämän sijaan. esim: http://esimerkki.fi/logo.png" - logo_small_url: "Pieni logo, joka näytetään vasemmassa yläkulmassa vieritettäessä viestiketjun sivua alaspäin, esim: http://esimerkki.fi/logo-pieni.png" + logo_small_url: "Logo sivun vasemmassa ylälaidassa, kun sivua on vieritetty alas. Jos jätetään tyhjäksi, näytetään koti-merkki." favicon_url: "Palstan favicon, katso http://fi.wikipedia.org/wiki/Favicon" - mobile_logo_url: "Mobiiliteeman vasemmassa yläkulmassa näytettävä logo. Jos jätät tyhjäksi, `logo_url` käytetään tämän sijaan. esim: http://esimerkki.fi/logo.png" + mobile_logo_url: "Logo mobiilisivun vasemmassa ylälaidassa. Jos jätetään tyhjäksi, näytetään sen tilalla palstan nimi." apple_touch_icon_url: "Applen laitteiden käyttämä ikoni, Suositeltu koko on 144px kertaa 144px." notification_email: "Sähköpostiosoite, josta kaikki tärkeät järjestelmän lähettämät sähköpostiviestit lähetetään. Verkkotunnuksen SPF, DKIM ja reverse PTR tietueiden täytyy olla kunnossa, jotta sähköpostit menevät perille." email_custom_headers: "Pystyviivalla eroteltu lista mukautetuista sähköpostin tunnisteista" @@ -649,14 +736,12 @@ fi: enable_noscript_support: "Ota käyttöön noscript-tagi hakukoneiden webcrawlereille" allow_moderators_to_create_categories: "Salli valvojien luoda uusia alueita" cors_origins: "Salli lähteet CORS-pyynnöille (cross-origin request). Jokaisen lähteen pitää sisältää http:// tai https://. DISCOURSE_ENABLE_CORS asetus pitää olla valittuna ottaaksesi CORSin käyttöön." - top_menu: "Mitkä painikkeet näytetään kotisivun navigointipalkissa, ja missä järjestyksessä. Esimerkiksi latest|new|unread|starred|categories|top|read|posted" + top_menu: "Mitkä painikkeet näytetään kotisivun navigointipalkissa, ja missä järjestyksessä. Esimerkiksi latest|new|unread|categories|top|read|posted|bookmarks" post_menu: "Mitkä painikkeet näytetään viestin valikossa, ja missä järjestyksessä. Esimerkiksi like|edit|flag|delete|share|bookmark|reply" post_menu_hidden_items: "Piilotettavat painikkeet viestin valikosta, kunnes '...' klikataan." share_links: "Mitkä painikkeet näytetään Jaa-valikossa ja missä järjestyksessä." track_external_right_clicks: "Seuraa pois sivustolta vieviä linkkejä, jotka avataan hiiren oikealla näppäimellä (esim. avaa uudessa välilehdessä) oletuksena poistettu käytöstä, koska tämä kirjoittaa URL:n uudelleen" - topics_per_page: "Kuinka monta ketjua ladataan avattaessa ketjulista ja kerrallaan lisää vieritettäessä sivua alaspäin" - posts_chunksize: "Kuinka monta viestiä ladataa avattaessa viestiketju ja kerrallaan lisää vieritetäessä sivua alaspäin" - site_contact_username: "Kaikki automaattiset yksityisviestit lähetetään tämän käyttäjän nimissä; jos jätetään tyhjäksi oletuksena on System-käyttäjä." + site_contact_username: "Henkilökuntaan kuuluvan käyttäjä, jonka nimissä kaikki automaattiset yksityisviestit lähetetään. Jos jätetään tyhjäksi, oletuksena on System-käyttäjä." send_welcome_message: "Lähetä kaikille uusille käyttäjille yksityinen tervetuliaisviesti, jossa on pikakäyttöopas." suppress_reply_directly_below: "Älä näytä vastausten lukumäärää viestissä, jos ainoa vastaus on seuraavassa viestissä." suppress_reply_directly_above: "Älä näytä vastauksena-painiketta viestin yläreunassa, jos viestissä on vastattu vain edelliseen viestiin." @@ -674,6 +759,7 @@ fi: email_domains_blacklist: "Lista sähköposti-verkkotunnuksista, joista käyttäjät eivät voi luoda tiliä. Esimerkiksi mailinator.com trashmail.net" email_domains_whitelist: "Lista sähköposti-verkkotunnuksista, joista käyttäjien pitää luoda tilinsä. VAROITUS: Käyttäjiä, joiden sähköpostiosoite on muusta verkkotunnuksesta ei sallita!" forgot_password_strict: "Älä paljasta tilin olemassaoloa unohtuneen salasanan kyselyssä." + log_out_strict: "Kun kirjaudutaan ulos, kirjaa käyttäjä ulos kaikilta laitteilta" version_checks: "Pingaa Discourse Hubia päivityksistä ja näytä viesti /admin hallintapaneelissa kun uusi versio on saatavilla" new_version_emails: "Lähetä sähköposti contact_email osoitteeseen kun uusi versio Discoursesta on saatavilla." port: "VAIN KEHITTÄJILLE! VAROITUS! Käytä tätä HTTP porttia portin 80 sijaan. Jätä tyhjäksi jos haluat käyttää oletusporttia 80." @@ -686,9 +772,10 @@ fi: max_username_length: "Käyttäjänimen enimmäispituus merkeissä. VAROITUS: KÄYTTÄJÄT JOIDEN KÄYTTÄJÄTUNNUS ON PIDEMPI KUIN TÄMÄ EIVÄT VOI ENÄÄ KIRJAUTUA SISÄÄN MUUTOKSEN JÄLKEEN." min_password_length: "Salasanan vähimmäispituus." block_common_passwords: "Älä salli salasanoja, jotka ovat 10 000 yleisimmän salasanan joukossa." - enable_sso: "Ota käyttöön single sign on ulkopuolisen sivuston kautta (Huom: poistaa kutsut käytöstä)" + enable_sso: "Ota käyttöön single sign on ulkopuolisen sivuston kautta (HUOM: voi estää kaikkia kirjautumasta sisään, jos asetukset eivät ole kunnossa ja poistaa kutsut käytöstä)" + enable_sso_provider: "Ota käyttöön Discourse SSO protokolla /session/sso_provider päätepisteessä, vaatii asetuksen sso_secret asettamista." sso_url: "single sign on endpointin URL" - sso_secret: "Salausavain, jolla salataan/puretaan SSO tiedot, varmista että siinä on vähintään 10 merkkiä" + sso_secret: "Salausavain, jolla todennetaan SSO tiedot, varmista että se on vähintään 10 merkkiä pitkä" sso_overrides_email: "Ohittaa paikallisen sähköpostiosoitteen SSO:n kautta saatavalla ulkopuolisella sähköpostillosoitteella (VAROITUS: eroavuuksia saattaa syntyä johtuen paikallisten sähköpostien normalisoinnista)" sso_overrides_username: "Ohittaa paikallisen käyttäjänimen SSO:n kautta saatavalla ulkopuolisella käyttäjänimellä (VAROITUS: eroavuuksia saattaa syntyä johtuen eroavuuksista nimien pituuksissa/vaatimuksissa)" sso_overrides_name: "Ohittaa paikallisen nimen SSO:n kautta saatavalla ulkopuolisella nimellä (VAROITUS: eroavuuksia saattaa syntyä johtuen paikallisten nimien normalisoinnista)" @@ -725,17 +812,18 @@ fi: max_flags_per_day: "Liputusten päivittäinen maksimäärä per käyttäjä." max_bookmarks_per_day: "Kirjanmerkkien päivittäinen maksimäärä per käyttäjä." max_edits_per_day: "Muokkausten päivittäinen maksimäärä per käyttäjä." - max_stars_per_day: "Ketjujen maksimimäärä, jotka yksittäinen käyttäjä voi merkitä tähdellä päivässä." max_topics_per_day: "Luotujen ketjujen päivittäinen maksimäärä per käyttäjä." max_private_messages_per_day: "Yksityisviestien päivittäinen maksimäärä per käyttäjä." + max_invites_per_day: "Maksimimäärä kutsuja, jonka käyttäjä voi lähettää päivässä." suggested_topics: "Suositeltujen ketjujen määrä ketjun alaosassa." limit_suggested_to_category: "Näytä suositeltuja ketjuja vain nykyiseltä alueelta." clean_up_uploads: "Poista orpoutuneet liitetiedostot, joita ei käytetä viesteissä, laittoman hostauksen estämiseksi. VAROITUS: kannattaa varmuuskopioida /uploads kansio ennen tämän asetuksen ottamista käyttöön." clean_orphan_uploads_grace_period_hours: "Varoaika (tunteina) kunnes orpoutuneet liitetiedostot poistetaan" purge_deleted_uploads_grace_period_days: "Varoaika (päivinä) kunnes poistettu liitetiedosto tuhotaan." - purge_inactive_users_grace_period_days: "Varoaika (päivinä) kunnes aktivoimaton käyttäjätili tuhotaan." + purge_unactivated_users_grace_period_days: "Varoaika (päivinä) kunnes aktivoimaton käyttäjätili tuhotaan." enable_s3_uploads: "Lataa liitetiedostot Amazon S3:een. Tärkeää: edellyttää toimivat S3 kirjautumistiedot (access key id ja secret access key)." s3_use_iam_profile: 'Käytä AWS EC2 IAM roolia avainten hakuun. HUOM: käyttöönotto korvaa "s3 access key id" ja "s3 secret access key" asetukset.' + s3_upload_bucket: "Amazon S3 bucket, jonne lataukset sijoitetaan. VAROITUS: täytyy olla pienillä kirjaimilla, ei pisteitä, ei alaviivoja." s3_access_key_id: "Amazon S3 access key id, jota käytetään kuvien sijoittamisessa." s3_secret_access_key: "Amazon S3 secret access key, jota käytetään kuvien sijoittamisessa." s3_region: "Amazon S3 region, jota käytetään kuvien sijoittamisessa." @@ -782,6 +870,7 @@ fi: min_title_similar_length: "Ketjun otsikon minimipituus, kunnes sitä verrataan muihin samankaltaisiin ketjuihin." min_body_similar_length: "Viestin minimipituus, kunnes sitä verratataan muihin samankaltaisiin ketjuihin." category_colors: "Lista alueiden sallituista väriarvoista, heksadesimaaleina." + category_style: "Aluemerkin tyyli." max_image_size_kb: "Liitetyn kuvan suurin sallittu koko kilotavuissa. Tämä pitää asettaa myös nginxin (client_max_body_size) / apachen tai proxyn asetuksista." max_attachment_size_kb: "Liitetyn tiedoston suurin sallittu koko kilotavuissa. Tämä pitää asettaa myös nginxin (client_max_body_size) / apachen tai proxyn asetuksista." authorized_extensions: "Liitetiedostojen sallitut tiedostopäätteet (käytä '*' salliaksesi kaikki tiedostotyypit)" @@ -806,12 +895,17 @@ fi: white_listed_spam_host_domains: "Lista verkkotunnuksista, joita ei oteta huomioon roskapostin tunnistamisessa. Uusilla käyttäjillä ei ole rajoituksia linkkaamisessa näihin tunnuksiin." staff_like_weight: "Kuinka suuri ylimääräinen arvo on henkilökunnan tykkäyksillä." levenshtein_distance_spammer_emails: "Verrattaessa sähköpostiosoitteita tunnettuihin roskapostittajiin, näin monen merkin ero saa vielä aikaan löydöksen." + max_new_accounts_per_registration_ip: "Jos samasta IP osoitteesta on jo (n) luottamustason 0 käyttäjätiliä (eikä yhtään henkilökunnan tai vähintään LT2), lakkaa hyväksymästä uusia rekisteröitymisiä tästä IP:stä." + min_ban_entries_for_roll_up: "Kun Kääri-painiketta painetaan, luodaan IP-porttikielloista aliverkon kattavia kieltoja jos kieltoja on asettu vähintään (N) määrä." + max_age_unmatched_emails: "Poista osumattomat seulotut sähköpostiosoitteet (N) päivän jälkeen." + max_age_unmatched_ips: "Poista osumattomat seulotut IP-osoitteet (N) päivän jälkeen." + num_flaggers_to_close_topic: "Minimimäärä uniikkeja liputtajia, joka vaaditaan ketjun automaattiseksi keskeyttämiseksi." + num_flags_to_close_topic: "Minimimäärä aktiivisia lippuja, joka vaaditaan ketjun automaattiseksi keskeyttämiseksi." reply_by_email_enabled: "Ota käyttöön vastaukset sähköpostin avulla." reply_by_email_address: "Saapuvan sähköpostin sapluuna sähköpostivastauksiin, esimerkiksi: %{reply_key}@reply.example.com tai replies+%{reply_key}@example.com" disable_emails: "Estä Discoursea lähettämästä mitään sähköpostia" strip_images_from_short_emails: "Poista kuvat sähköposteista, joiden koko on alle 2800 tavua" short_email_length: "Lyhyen sähköpostin pituus tavuissa" - enable_email_names: "Salli koko nimen käyttäminen sähköposteissa. Ota pois käytöstä piilottaaksesi koko nimen sähköposteissa." pop3_polling_enabled: "Pollaa sähköpostivastaukset POP3:lla." pop3_polling_ssl: "Käytä SSL-salausta yhdistettäessä POP3-palvelimeen. (Suositellaan)" pop3_polling_period_mins: "Tiheys minuuteissa kuinka usein POP3 tililtä tarkastetaan uudet sähköpostit. HUOM: vaatii uudelleenkäynnistyksen." @@ -832,10 +926,13 @@ fi: logout_redirect: "Sivu, jonne selain ohjataan uloskirjautumisen jälkeen, esim: (http://sivusto.com/logout)" allow_uploaded_avatars: "Salli käyttäjien ladata oma avatar." allow_animated_avatars: "Salli käyttäjien valita animoituja gif-avatareja. VAROITUS: aja avatars:refresh rake task tämän asetuksen muuttamisen jälkeen." + allow_animated_thumbnails: "Luo animoidut esikatselukuvat animoiduista gif-tiedostoista." automatically_download_gravatars: "Lataa käyttäjille Gravatarit automaattisesti tilin luonnin ja sähköpostin vaihdon yhteydessä." digest_topics: "Sähköpostitiivistelmässä näytettävien ketjujen maksimimäärä." digest_min_excerpt_length: "Viestin katkelman vähimmäispituus sähköpostitiivistelmässä, merkeissä" default_digest_email_frequency: "Kuinka usein käyttäjän saavat sähköpostitiivistelmän oletuksena. He voivat muuttaa asetusta omista asetuksistaan." + suppress_digest_email_after_days: "Supista tiivistelmäsähköpostit käyttäjltä, joita ei ole nähty (n) päivän aikana." + disable_digest_emails: "Ota tiivistelmäsähköpostit pois käytöstä kaikilta käyttäjiltä." default_external_links_in_new_tab: "Avaa ulkoiset linkit uudessa välilehdessä. Käyttäjät voivat muuttaa tämän asetuksistaan." detect_custom_avatars: "Tarkistetaanko, ovatko käyttäjät ladanneet oman avatarin." max_daily_gravatar_crawls: "Korkeintaan kuinka monta kertaa Discourse tarkistaa avatarit Gravatarista päivässä" @@ -847,7 +944,7 @@ fi: suppress_uncategorized_badge: "Älä näytä alueettomille ketjuille tunnusta ketjujen listauksissa." global_notice: "Näytä KIIREELLISESTÄ HÄTÄTAPAUKSESTA kertova banneri kaikilla sivuilla kaikille käyttäjille, vaihda tyhjäksi piilottaaksesi sen (HTML sallittu)." disable_edit_notifications: "Poista muokkausilmoitukset system-käyttäjältä, kun 'download_remote_images_to_local' on asetettu." - enable_names: "Salli käyttäjän pitkän nimen näyttäminen. Ota pois käytöstä piilottaaksesi pitkän nimen." + enable_names: "Näytä käyttäjän koko nimi profiilissa, käyttäjäkortissa ja sähköposteissa. Poista käytöstä piilottaaksesi koko nimen kaikkialla." display_name_on_posts: "Näytä käyttäjän pitkä nimi viesteissä @nimen lisäksi." invites_per_page: "Oletuksena montako kutsua näytetään käyttäjäsivulla." short_progress_text_threshold: "Kuinka monen viestin jälkeen ketjun edistyspalkissa näytetään vain nykyisen viestin numero. Jos muutat palkin leveyttä, voit joutua muuttamaan tätä arvoa." @@ -857,8 +954,8 @@ fi: embeddable_host: "Host, joka voi upottaa kommentteja tältä Discourse-palstalta." feed_polling_enabled: "VAIN UPOTUS: Upotetaanko RSS/ATOM syöte viesteinä." feed_polling_url: "VAIN UPOTUS: RSS/ATOM syötteen URL." - embed_by_username: "Sen käyttjän Discourse käyttäjänimi, joka luo upotetut ketjut." - embed_username_key_from_feed: "Avain, jolla erotetaan discoure käyttäjänimi syötteestä." + embed_by_username: "Sen käyttäjän Discourse käyttäjänimi, joka luo upotetut ketjut." + embed_username_key_from_feed: "Avain, jolla erotetaan Discourse-käyttäjänimi syötteestä." embed_truncate: "Typistä upotetut viestit." embed_category: "Alue upotettujen ketjuille." embed_post_limit: "Upotettavien viestien maksimimäärä." @@ -869,6 +966,8 @@ fi: show_create_topics_notice: "Jos palstalla on vähemmän kuin 5 julkista ketjua, huomauta ylläpitäjiä ketjujen luonnista." vacuum_db_days: "Aja VACUUM FULL ANALYZE vapauttaaksesi tilaa tietokantaan migraatioiden jälkeen (aseta 0 ottaaksesi pois käytöstä)" prevent_anons_from_downloading_files: "Estä kirjautumattomia käyttäjiä lataamasta liitetiedostoja. VAROITUS: tämä estää viestin liitettyjen muiden tiedostojen, kuin kuvien käyttämisen sivustolla." + enable_emoji: "Ota emoji käyttöön" + emoji_set: "Minkälaisa emojia haluaisit käyttää?" errors: invalid_email: "Sähköpostiosoite ei kelpaa." invalid_username: "Tällä nimellä ei löydy käyttäjää." @@ -881,6 +980,7 @@ fi: invalid_string_min_max: "Merkkien lukumäärän täytyy olla välillä %{min} - %{max}." invalid_string_min: "Täytyy olla vähintään %{min} merkkiä." invalid_string_max: "Ei saa olla yli %{max} merkkiä." + invalid_reply_by_email_address: "Arvon täytyy sisältää '%{reply_key}' ja olla eri, kuin ilmoitusten sähköpostiosoite." notification_types: mentioned: "%{display_username} viittasi sinuun viestissä %{link}" liked: "%{display_username} tykkäsi viestistäsi %{link}" @@ -900,6 +1000,11 @@ fi: category: 'Alueet' topic: 'Tulokset' user: 'Käyttäjät' + sso: + not_found: "Käyttäjätilin hakeminen tai luominen ei onnistunut, ota yhteys järjestelmävalvojaan." + account_not_approved: "Tili odottaa hyväksyntää, saat sähköpostiin ilmoituksen kun hyväksyntä on valmis" + unknown_error: "Virhe tietojen päivittämisessä, ota yhteyttä järjestelmävalvojaan" + timeout_expired: "Kirjautuminen on vanhentunut, ole hyvä ja kirjaudu uudestaan" original_poster: "Alkuperäinen kirjoittaja" most_posts: "Eniten viestejä" most_recent_poster: "Uusin kirjoittaja" @@ -916,6 +1021,10 @@ fi: other: "Siirsin %{count} viestiä ketjuun: %{topic_link}" change_owner: post_revision_text: "Omistus vaihdettu käyttäjältä %{old_user} käyttäjälle %{new_user}" + emoji: + errors: + name_already_exists: "Pahoittelut, nimi '%{name}' on jo käytössä toisella emojilla." + error_while_storing_emoji: "Pahoittelut, tämän emojin tallentamisessa tapahtui virhe." topic_statuses: archived_enabled: "Tämä ketju on arkistoitu. Se on jäädytetty eikä sitä voi muuttaa millään tavalla." archived_disabled: "Ketjun arkistointi on poistettu, se ei ole enää jäädytettynä ja sitä voi taas muuttaa." @@ -960,7 +1069,7 @@ fi: errors: "%{errors}" not_available: "Ei saatavissa. Kokeile %{suggestion}?" something_already_taken: "Jotain meni pieleen. Ehkäpä tämä käyttäjänimi tai sähköpostiosoite on jo rekisteröity. Kokeile salasana unohtui -linkkiä." - omniauth_error: "Pahoittelut, %{strategy} tilisi tunnistautumisessa tapahtui virhe. Ehkäpä et hyväksynyt valtuutusta?" + omniauth_error: "Pahoittelut, tilisi tunnistautumisessa tapahtui virhe. Ehkäpä et hyväksynyt valtuutusta?" omniauth_error_unknown: "Jotain meni pieleen kirjautumisesti käsittelyssä, ole hyvä ja yritä uudelleen." new_registrations_disabled: "Uusien tilien luonti ei ole tällä hetkellä sallittu." password_too_long: "Salasanan enimmäispituus on 200 merkkiä." @@ -1055,16 +1164,11 @@ fi: [2]: http://mxtoolbox.com/ReverseLookup.aspx [3]: http://www.dkim.org/ [4]: http://whatismyipaddress.com/blacklist-check - [5]: %{base_url}/unsubscribe [7]: http://dkimcore.org/tools/dkimrecordcheck.html [8]: http://www.openspf.org/SPF_Record_Syntax [md]: http://mandrill.com [mg]: http://www.mailgun.com/ [mj]: http://www.mailjet.com/pricing - - ---- - - Jokaisen lähettämäsi sähköpostin lopussa pitäisi olla listaltapoistumislinkki, joten luodaan sellainen tähänkin. Tämän sähköpostin lähetti Yritys, Tie 1, 12345 Kaupunki, Suomi. Jos et halua vastaanottaa näitä sähköposteja jatkossa, [klikkaa poistuaksesi listalta][5]. new_version_mailer: subject_template: "[%{site_name}] Uusi Discourse versio, päivitys saatavilla" text_body_template: |+ @@ -1106,23 +1210,39 @@ fi: other: "Nämä liput annettiin yli %{count} tuntia sitten." please_review: "Ole hyvä ja tarkasta ne." post_number: "viesti" + how_to_disable: 'Voit muuttaa tämän sähköpostimuistutuksen tiheyttä tai ottaa sen pois käytöstä muuttamalla "notify about flags after"-asetusta.' subject_template: one: "Yksi liputus odottaa käsittelyä" other: "%{count} liputusta odottaa käsittelyä" flag_reasons: off_topic: "Viestisi liputettiin **asiaan kuulumattomaksi**: se ei kuulu tähän ketjuun, jonka aiheen määrittelee aloitusviesti ja otsikko." inappropriate: "Viestisi liputettiin **asiattomaksi**: se on loukkaava, herjaava tai rikkoo [palstan sääntöjä](/guidelines)." - spam: "Viestisi liputettiin **roskapostiksi**: siinä on ilmoitus, joka ei ole hyödyllinen tai relevantti tässä ketjussa, vaan luonteeltaan mainostamista." - notify_moderators: "Viestisi liputettiin **valvojalle tiedoksi**: siinä on jotain, johon valvojan pitäisi puuttua." + spam: "Viestisi liputettiin **roskapostiksi**: siinä on ilmoitus, joka on luonteeltaan mainostamista sen sijaan, että olisi hyödyllinen tai relevantti tässä keskustelussa." + notify_moderators: "Viestisi liputettiin **valvojalle tiedoksi**: siinä on jotain, johon henkilökunnan pitäisi puuttua." flags_dispositions: agreed: "Kiitos kun toit asian tietoomme. Olemme samaa mieltä ongelmasta ja selvitämme sitä." agreed_and_deleted: "Kiitos kun toit asian tietoomme. Olemme samaa mieltä ongelmasta ja olemme poistaneet kyseisen viestin." disagreed: "Kiitos kun toit asian tietoomme. Selvitämme asiaa." deferred: "Kiitos kun toit asian tietoomme. Selvitämme asiaa." deferred_and_deleted: "Kiitos kun toit asian tietoomme. Olemme poistaneet kyseisen viestin." + temporarily_closed_due_to_flags: "Tämä ketju on väliaikaisesti suljettu suuren lippumäärän vuoksi" system_messages: post_hidden: subject_template: "Viesti on piilotettu liputuksen johdosta" + text_body_template: | + Hei, + + Tämä on automaattinen viesti sivustolta %{site_name}, koska viestisi on piilotettu. + + %{base_url}%{url} + + %{flag_reason} + + Usea käyttäjä on liputtanut tämän viestien ennen sen piilottamista, joten harkitse miten voisit muokata viestiäsi tämän palautteen pohjalta. **Voit muokata viestiäsi %{edit_delay} minuutin kuluttua, jolloin se tuodaan taas näkyviin.** Tämä kasvattaa luottamustasoasi. + + Kuitenkin, jos viesti piilotettiin toista kertaa, se pysyy piilotettuna kunnes henkilökunta tutkii tilanteen – jonka jälkeen voi seurata muita seuraamuksia, mukaan lukien tilisi hyllyttäminen. + + Saadaksesi lisätietoja, lue [yhteisön säännöt](%{base_url}/guidelines). usage_tips: text_body_template: | Tässä yksityisviestissä on muutama pikaohje, joiden avulla pääset alkuun: @@ -1185,7 +1305,7 @@ fi: - Voit vaihtaa yksittäisen viestiketjun ilmoitusasetuksia ketjun alalaidassa olevalla painikkeella (asetuksen voi muuttaa myös aluekohtaisesti). Vaihtaaksesi asetuksia siitä, kuinka seuraat ketjuja tai mitkä ketjut tulkitaan uusiksi, katso [käyttäjäasetukset](/my/preferences). + Voit vaihtaa yksittäisen viestiketjun ilmoitusasetuksia ketjun alalaidassa olevalla painikkeella (asetuksen voi muuttaa myös aluekohtaisesti). Vaihtaaksesi asetuksia siitä, kuinka seuraat ketjuja tai mitkä ketjut tulkitaan uusiksi, katso [käyttäjäasetukset](%{base_url}/my/preferences). ## Miksi en voi tehdä jotain? @@ -1200,16 +1320,18 @@ fi: Me uskomme [sivistyneeseen kanssakäymiseen](%{base_url}/guidelines). Toivottavasti viihdyt! + + (Jos haluat keskustella yksityisesti [henkilökunnan jäsenten](%{base_url}/about) kanssa, vastaa vain tähän viestiin) welcome_invite: subject_template: "Tervetuloa sivustolle %{site_name}!" text_body_template: | Kiitos kutsun hyväksymisestä sivustolle %{site_name} -- tervetuloa! - Olemme luoneet sinulle automaattisesti käyttäjänimen: **%{username}**, mutta voit vaihtaa sen koska hyvänsä [käyttäjäprofiilistasi][prefs]. + Olemme luoneet sinulle uuden käyttäjätunnuksen: **%{username}** ja olet nyt kirjautuneena sisään. Voit vaihtaa nimeäsi [käyttäjäprofiilistasi][prefs]. - Kirjautuaksesi sisään uudestaan, joko: + Kirjautuaksesi uudestaan sisään myöhemmin: - 1. Kirjaudu sisään millä metodilla haluat -- kunhan se on yhdistetty **samaan sähköpostiosoitteeseen,** kuin jonne vastaanotit alkuperäisen kutsun. Muuten emme voi tietää, että se todella olet sinä! + 1. Käytä aina **samaa sähköpostiosoitetta, jonne vastaanotit alkuperäisen kutsun**. Muuten emme voi tietää, että olet sinä! 2. Luo salasana [käyttäjäprofiilissasi][prefs], ja käytä sitä kirjautumiseen. @@ -1219,6 +1341,8 @@ fi: Toivottavasti viihdyt! + (Jos haluat keskustella yksityisesti [henkilökunnan jäsenten](%{base_url}/about) kanssa, vastaa vain tähän viestiin) + [prefs]: %{user_preferences_url} backup_succeeded: subject_template: "Varmuuskopiointi suoritettu onnistuneesti" @@ -1260,16 +1384,16 @@ fi: %{logs} ``` csv_export_succeeded: - subject_template: "Datan vieminen suoritettu onnistuneesti" + subject_template: "Datan vieminen on tehty" text_body_template: | Datan vienti onnistui. - Lataa CSV-tiedosto: %{file_name} + %{file_name} (%{file_size}) - Linkki CSV-tiedostoon vanhenee 48 tunnin kuluttua. + Linkki tiedostoon on voimassa 48 tuntia. csv_export_failed: - subject_template: "Vienti epäonnistui" - text_body_template: "Vienti on epäonnistunut. Tarkista loki-tiedostot." + subject_template: "Datan vienti epäonnistui" + text_body_template: "Pahoittelemme, mutta datan vientisi epäonnistui. Tarkasta lokit tai ota yhteyttä henkilökuntaan." email_reject_trust_level: subject_template: "Sähköpostiongelma -- Riittämätön luottamustaso" text_body_template: | @@ -1463,13 +1587,17 @@ fi: --- %{respond_instructions} digest: + why: "Lyhyt tiivistelmä siitä mitä on tapahtunut sivustolla %{site_link} viimeisimmän vierailusi jälkeen %{last_seen_at}." subject_template: "[%{site_name}] Tiivistelmä ajalta %{date}" new_activity: "Uusi toiminta omissa ketjuissasi ja viesteissäsi:" top_topics: "Suosittuja viestejä" other_new_topics: "Suosittuja ketjuja" + unsubscribe: "Tämä tiivistelmä lähetetään sivustolta %{site_link}, kun emme ole nähneet sinua vähään aikaan. Jos et halua vastaanottaa näitä viestejä, %{unsubscribe_link}." click_here: "klikkaa tästä" from: "%{site_name} tiivistelmä" read_more: "Lue lisää" + more_topics: "Muita uusia ketjuja on luotu %{new_topics_since_seen}." + more_topics_category: "Lisää uusia ketjuja:" posts: one: "1 viesti" other: "%{count} viestiä" @@ -1521,6 +1649,8 @@ fi: Me uskomme [sivistyneeseen kanssakäyntiin](%{base_url}/guidelines). Toivottavasti viihdyt! + + (Jos haluat keskustella yksityisesti [henkilökunnan jäsenten](%{base_url}/about) kanssa, vastaa vain tähän viestiin) signup: subject_template: "[%{site_name}] Vahvista uusi tilisi" text_body_template: | @@ -1551,9 +1681,9 @@ fi: pasted_image_filename: "Liitetty kuva" store_failure: "Latauksen #%{upload_id} käyttäjälle #%{user_id} tallentaminen epäonnistui." attachments: - too_large: "Pahoittelut, tiedosto jonka latausta yritit on liian suuri ( suurin tiedostokoko on %{max_size_kb}%kb)." + too_large: "Pahoittelut, tiedosto jonka latausta yritit on liian suuri ( suurin tiedostokoko on %{max_size_kb}KB)." images: - too_large: "Pahoittelut, kuva jonka yritit ladata on liian suuri (suurin sallittu kuvakoko on {%max_size_kb}%kb), pienennä kuvaa ja yritä uudestaan." + too_large: "Pahoittelut, kuva jonka yritit ladata on liian suuri (suurin sallittu kuvakoko on %{max_size_kb}KB), pienennä kuvaa ja yritä uudestaan." fetch_failure: "Pahoittelut, kuvan hakemisessa tapahtui virhe." unknown_image_type: "Pahoittelut, mutta tiedosto jota yrität ladata ei vaikuta olevan kuva." size_not_found: "Pahoittelut, mutta emme pystyneet selvittämään kuvan kokoa. Ehkä kuvatiedosto on vahingoittunut?" @@ -1579,6 +1709,9 @@ fi: guidelines: "Ohjeet" privacy: "Yksityisyys" edit_this_page: "Muokkaa tätä sivua" + csv_export: + boolean_yes: "Kyllä" + boolean_no: "Ei" static_topic_first_reply: | Muokkaa ketjun aloitusviestiä muokataksesi sivun %{page_name} sisältöä. guidelines_topic: @@ -1587,3 +1720,26 @@ fi: title: "Käyttöehdot" privacy_topic: title: "Rekisteriseloste" + static: + search_help: | +

Vinkkejä

+

+

    +
  • Otsikon vastaavuutta korostetaan, joten jos olet epävarma, etsi otsikkoa
  • +
  • Uniikit, harvinaiset sanat tuovat aina parhaan lopputuloksen
  • +
  • Rajoita hakusi tietylle alueelle, tiettyyn käyttäjään tai ketjuun aina kun mahdollista.
  • +
+

+

Komennot:

+

+ + + + + + +
order:viewsorder:latest
status:openstatus:closedstatus:archivedstatus:norepliesstatus:singleuser
category:foouser:foo
in:likesin:postedin:watchingin:trackingin:private
in:bookmarks
+

+

+ sateenkaari category:puistot status:open order:latest etsii ketjuja, joissa mainitaan sana "sateenkaari" alueella "puistot" joita ei ole suljettu tai arkistoitu, järjestettynä viimeisen viestin mukaan. +

diff --git a/config/locales/server.fr.yml b/config/locales/server.fr.yml index ba1617cf3b..ad5b06b426 100644 --- a/config/locales/server.fr.yml +++ b/config/locales/server.fr.yml @@ -6,6 +6,9 @@ # https://www.transifex.com/projects/p/discourse-org/ fr: + stringex: + characters: + number: "-" i18n: transliterate: rule: @@ -17,11 +20,6 @@ fr: short_date_no_year: "D MMM" short_date: "D MMM, YYYY" long_date: "D MMMM YYYY H:mm" - time: - formats: - short: "%d-%m-%Y" - short_no_year: "%B %-d" - date_only: "%b %-d, %Y" title: "Discourse" topics: "Sujets" posts: "messages" @@ -30,13 +28,50 @@ fr: log_in: "Se connecter" via: "%{username} via %{site_name}" is_reserved: "est réservé" - purge_reason: "Supprimer automatiquement car devenue trop ancien et non verifier" + purge_reason: "Supprimé automatiquement comme compte abandonné, non activé" disable_remote_images_download_reason: "Le téléchargement des images externes a été désactivé faute de place suffisante sur le disque." errors: + format: '%{attribute} %{message}' messages: too_long_validation: "est limité à %{max} caractères maximum; il y en a %{length}." invalid_boolean: "Vrai/Faux invalide." taken: "a déjà été pris" + accepted: doit être accepté + blank: ne peut être vide + present: doit être vide + confirmation: "ne correspond pas %{attribute}" + empty: ne peut être vide + equal_to: doit être égal à %{count} + even: doit être encore + exclusion: est réservé + greater_than: doit être supérieure à %{count} + greater_than_or_equal_to: doit être supérieur ou égal à %{count} + inclusion: n'est pas inclus dans la liste + invalid: est invalide + less_than: doit être inférieure à %{count} + less_than_or_equal_to: doit être inférieur ou égal à %{count} + not_a_number: n'est pas un nombre + not_an_integer: doit être un entier + odd: doit être impair + record_invalid: 'Validation échouée: %{errors}' + restrict_dependent_destroy: + one: "Impossible de supprimer l'enregistrement car certains %{record} existent et en dépendent" + many: "Impossible de supprimer l'enregistrement car un %{record} existe et en dépend" + too_long: + one: c'est trop long (le maximum est de 1 caractère) + other: c'est trop long (le maximum est de %{count} caractères) + too_short: + one: c'est trop court (le minimum est de 1 caractère) + other: c'est trop court (le minimum est de %{count} caractères) + wrong_length: + one: c'est la mauvaise longueur (devrait être de 1 caractère) + other: c'est la mauvaise longueur (devrait être de %{count} caractères) + other_than: "doit être différent de %{count}" + template: + body: 'Des problèmes ont été rencontrés sur les champs suivants : ' + header: + one: Une erreur empêche ce %{model} d'être sauvegardé + other: '%{count} erreurs empêchent ce %{model} d''être sauvegardé' embed: load_from_remote: "Il y a eu une erreur lors du chargement de ce message." bulk_invite: @@ -104,6 +139,7 @@ fr: latest: "Sujets récents" hot: "Sujets populaires" too_late_to_edit: "Ce message a été créé il y a trop longtemps. Il ne peut plus être modifié ou supprimé." + excerpt_image: "image" groups: errors: can_not_modify_automatic: "Vous ne pouvez pas modifier un groupe automatique" @@ -188,8 +224,8 @@ fr: base: warning_requires_pm: "Vous ne pouvez joindre d'avertissements qu'aux messages privés." too_many_users: "Vous ne pouvez envoyer d'avertissement qu'à un utilisateur à la fois." - archetype: cant_send_pm: "Désolé, vous ne pouvez pas envoyer de message privé à cet utilisateur." + no_user_selected: "Vous devez choisir un utilisateur valide." user: attributes: password: @@ -256,7 +292,7 @@ fr: regular: title: "membre" leader: - title: "régulier" + title: "habitué" elder: title: "meneur" change_failed_explanation: "Vous avez essayé de rétrograder %{user_name} au niveau '%{new_trust_level}'. Cependant son niveau de confiance est déjà '%{current_trust_level}'. %{user_name} restera au niveau '%{current_trust_level}' - Si vous souhaitez rétrograder un utilisateur vous devez verrouiller le niveau de confiance au préalable" @@ -356,7 +392,7 @@ fr: change_email: confirmed: "Votre adresse de courriel a été mise à jour." please_continue: "Continuer vers %{site_name}" - error: "Il y a eu une erreur lors du changement de votre adresse de courriel. Elle est peut-être déjà utilisée ?" + error: "Il y a eu une erreur lors de la modification de votre adresse de courriel. Elle est peut-être déjà utilisée ?" activation: action: "Activer votre compte" already_done: "Désolé, ce lien de confirmation n'est plus valide. Votre compte est peut-être déjà activé ?" @@ -373,20 +409,26 @@ fr: title: 'Spam' description: 'Ce message est une publicité. Elle n''est pas utile ou pertinente dans ce sujet, mais est uniquement de nature promotionnelle.' long_form: 'signalé comme spam' + email_title: '"%{title}" a été signalé comme spam' + email_body: "%{link}\n\n%{message}" inappropriate: title: 'Inapproprié' description: 'Ce message contient du contenu qu''une personne raisonnable jugerait offensant, abusif ou en violation du règlement de notre communauté.' long_form: 'signalé comme inapproprié' notify_user: + title: 'Contacter @{{username}} en privé' description: 'Ce message contient quelque chose sur lequel je souhaite discuter en privé avec cet utilisateur. N''envoi pas de signalement.' + long_form: 'utilisateur contacté en privé' email_title: 'Votre message sur "%{title}"' email_body: "%{link}\n\n%{message}" notify_moderators: + title: "Autre chose" description: 'Ce message nécessite l''attention d''un modérateur pour une autre raison que celles listées ci-dessus.' + long_form: 'signalé pour modération' email_title: 'Un message dans "%{title}" requière l''attention d''un modérateur' email_body: "%{link}\n\n%{message}" bookmark: - title: 'Signets' + title: 'Signet' description: 'Ajouter aux signets' long_form: 'ajouté aux signets' like: @@ -407,7 +449,9 @@ fr: description: 'Ce message contient du contenu qu''une personne raisonnable jugerait offensant, abusif ou en violation du règlement de notre communauté.' long_form: 'signalé comme inapproprié' notify_moderators: + title: "Autre chose" description: 'Ce message requiert l''attention de la modération d''après le règlement de la communauté, TOS, ou pour une autre raison non listée ici.' + long_form: 'signalé pour modération' email_title: 'Ce sujet "%{title}" requière l''attention d''un modérateur' email_body: "%{link}\n\n%{message}" flagging: @@ -415,7 +459,7 @@ fr: user_must_edit: '

Ce message a été signalé par la communauté et temporairement masqué.

' archetypes: regular: - title: "Sujet régulier" + title: "Sujet normal" banner: message: make: "Ce sujet est maintenant un gros titre. Il sera affiché en haut de chaque page jusqu’à qu'il soit ignoré par un utilisateur." @@ -508,6 +552,45 @@ fr: title: "Sujets les plus référencés" xaxis: "Sujet" num_clicks: "Clics" + page_view_anon_reqs: + title: "Anonyme" + xaxis: "Jour" + yaxis: "Pages vues par les utilisateurs non connectés" + page_view_logged_in_reqs: + title: "Connecté" + xaxis: "Jour" + yaxis: "Pages vues par les utilisateurs connectés" + page_view_crawler_reqs: + title: "Crawlers Web" + xaxis: "Jour" + yaxis: "Pages vues par les Crawler Web" + page_view_total_reqs: + title: "Total" + xaxis: "Jour" + yaxis: "Nombre de pages vues" + http_background_reqs: + title: "Arrière plan" + xaxis: "Jour" + http_2xx_reqs: + title: "Statut 2xx (OK)" + xaxis: "Jour" + yaxis: "Requêtes traitées avec succès (Statut 2xx)" + http_3xx_reqs: + title: "HTTP 3xx (Redirection)" + xaxis: "Jour" + yaxis: "Requêtes de redirection (Statut 3xx)" + http_4xx_reqs: + title: "HTTP 4xx (Erreur du client)" + xaxis: "Jour" + yaxis: "Erreurs du client (Statut 4xx)" + http_5xx_reqs: + title: "HTTP 5xx (Erreur du serveur)" + xaxis: "Jour" + yaxis: "Erreurs du serveur (Statut 5xx)" + http_total_reqs: + title: "Total" + xaxis: "Jour" + yaxis: "Total des requêtes" dashboard: rails_env_warning: "Votre serveur fonctionne dans l'environnement de %{env}." ruby_version_warning: "Vous exécutez une version de Ruby 2.0.0 qui est connu pour avoir des problèmes. Migrez vers le patch 247 ou supérieur." @@ -526,15 +609,14 @@ fr: s3_backup_config_warning: 'Le serveur est configuré pour envoyer les sauvegardes sur S3, mais l''un des paramètres suivants n''est pas renseigné: s3_access_key_id, s3_secret_access_key or s3_backup_bucket. Allez dans les Paramètres et mettez les à jour. Voir "Comment mettre en place une sauvegarde sur S3?" pour en savoir plus.' image_magick_warning: 'Le serveur est configuré pour créer des aperçus des grandes images, mais ImageMagick n''est pas installé. Installez ImageMagick en utilisant votre gestionnaire de paquets favori ou bien allez ici pour télécharger la dernière version.' failing_emails_warning: 'Il y a %{num_failed_jobs} tâches d''envois de courriel en erreur. Vérifiez votre fichier config/discourse.conf et assurez-vous de la conformité des paramètres du serveur de courriel. Regarder les processus en échec dans Sidekiq.' - default_logo_warning: "Vous n'avez pas changé le logo de votre site. Merci de mettre à jour les paramètres suivants : logo_url, logo_small_url et favicon_url." - contact_email_missing: "Vous n'avez pas renseigné de courriel de contact pour votre site. Merci de mettre à jour le paramètre : contact_email." - contact_email_invalid: "Le courriel de contact du site est invalide. Merci de mettre à jour le paramètre : contact_email." - title_nag: "Le titre de votre site est toujours à sa valeur par défaut. Merci de le modifier dans les paramètres." - site_description_missing: "Le champ site_description est vide. Ecrivez une bref description de ce forum dans les paramètres." + default_logo_warning: "Modifier le logo de votre site. Mettez à jour les paramètres du site suivant : logo_url, logo_small_url et favicon_url." + contact_email_missing: "Saisissez l'adresse courriel de contact pour votre site web afin qu'on puisse vous contacter pour des problèmes urgents sur votre site. Mettez-le à jour dans les Paramètres du site." + contact_email_invalid: "L'adresse courriel de contact du site est invalide. Mettez-le à jour dans les paramètre du site." + title_nag: "Saisissez le nom de votre site. Modifier le titre sous Paramètres du site." + site_description_missing: "Saisissez une phrase courte qui décrira votre site et apparaîtra dans les moteurs de recherche. Mettez site_description à jour dans les Paramètres du site." consumer_email_warning: "Votre site est configuré pour envoyer les courriels en utilisant Gmail (ou un autre site de courriels pour utilisateur standard). Gmail limite le nombre d'emails que vous pouvez envoyer. Nous vous conseillons d'utiliser un autre service d'envoi de courriels afin d'assurer une meilleure délivrabilité." - access_password_removal: "Votre site utilisait le paramètre access_password, qui a été supprimé. Les paramètres login_required et must_approve_users settings ont été activé et doivent être utilisé en lieu et place. Vous pouvez les changer dans les Paramètres. Assurez-vous d'approuver les utilisateur dans les liste des Utilisateurs en Attente. (Ce message disparait au bout de 2 jours.)" - site_contact_username_warning: "Le contact du site (site_contact_username) n'est pas renseigné. Merci de mettre à jour dans les paramètres. Saisissez le pseudo d'un utilisateur admin qui doit être l'émetteur des messages système." - notification_email_warning: "Le courriel de notification (notification_email) n'est pas renseigné. Merci de mettre à jour dans les paramètres." + site_contact_username_warning: "Saisissez le pseudo d'un responsable sympathique à partir de laquele seront envoyés les messages privés importants. Mettez à jour site_contact_username dans les Paramètres du site." + notification_email_warning: "Les courriels de notification ne serot pas envoyés depuis une adresse de courriel valide sur votre domaine ; l'envoie des courriels sera aléatoire et peu fiable. Veuillez saisir une adresse de courriel locale dans notification_email dans les Paramètres du site." content_types: education_new_reply: title: "Education du nouvel utlisateur : premières réponses" @@ -583,12 +665,9 @@ fr: allow_duplicate_topic_titles: "Autoriser la création de sujet avec le même titre." unique_posts_mins: "Combien de temps avant qu'un utilisateur puisse poster le même contenu à nouveau" educate_until_posts: "Lors de la rédaction des (n) premiers nouveaux sujets de l'utilisateur, afficher le panneau d'aide à la saisie." - title: "Bref titre du site, utilisé dans la balise title." - site_description: "Décrivez ce site en une seule phrase. utilisée dans la balise meta description." - contact_email: "Courriel de la personne responsable techniquement du site. Les notifications importantes de siscourse.org concernant les mises à jour critiques seront envoyées à cette adresse." - company_full_name: "OBSOLÈTE. N'est plus utilisé ou a été supprimé. Le nom complet de la société qui gère ce site, utilisé dans les documents légaux, tels que /tos et /privacy" - company_short_name: "OBSOLÈTE. N'est plus utilisé ou a été supprimé. Le nom de la société qui gère ce site, utilisé dans les documents légaux, tels que /tos et /privacy" - company_domain: "OBSOLÈTE. N'est plus utilisé ou a été supprimé. Le nom de domaine de la société qui gère ce site, utilisé dans les documents légaux, tels que /tos et /privacy" + title: "Le nom du site, utilisé dans la balise title." + site_description: "Décrivez ce site en une seule phrase, utilisée dans le tag meta description." + contact_email: "Adresse de courriel du contact clef pour ce site. Utilisée que pour des notifications critiques tel que des signalements en suspens, et aussi sur la formulaire de contact sur la page 'a propos'." queue_jobs: "SEULEMENT POUR LES DÉVELOPPEURS ! ATTENTION ! Par défaut, empiler les travaux dans sidekiq. Si désactivé, votre site sera cassé." crawl_images: "Récupère les images provenant de sources tierces pour y insérer les dimensions corrects (hauteur et largeur)" download_remote_images_to_local: "Transformer les images distantes en images locales en les téléchargeant; cela permet d'éviter les liens morts." @@ -608,11 +687,11 @@ fr: post_excerpt_maxlength: "Longueur maximale d'un extrait / résumé de message." post_onebox_maxlength: "Longueur maximale d'un message emboîté en nombre de caractères." onebox_domains_whitelist: "Liste des noms de domaines autorisés pour les 'onebox'. Ces domaines doivent supporter OpenGraph or oEmbed. Testez les sur http://iframely.com/debug" - logo_url: "L'image de votre logo en haut à gauche du site, ex : http://example.com/logo.png" + logo_url: "L'image logo en haut à gauche de votre site ; si vide, le titre du site sera affiché." digest_logo_url: "Le logo alternatif qui sera utilisé dans les courriels de votre site. Si non renseigné, alors `logo_url` sera utilisé, par exemple : http://example.com/logo.png" - logo_small_url: "La version minifiée du logo de votre site lorsque vous faites défilez vers le bas ex : http://example.com/logo-small.png" + logo_small_url: "La petite image logo affiché en haut à gauche du site quand la page est défilée vers le bas. Si vide, une glyphe 'maison' sera affichée." favicon_url: "Le favicon de votre site, voir http://fr.wikipedia.org/wiki/Favicon" - mobile_logo_url: "L'image du logo qui sera en position fixe en haut à gauche sur votre site movile. Si non renseigné, alors `logo_url` sera utilisé, par exemple : http://example.com/uploads/default/logo.png" + mobile_logo_url: "L'image logo avec position fixe en haut à gauche de votre site en mode mobile. Si vide, le titre du site sera affiché." apple_touch_icon_url: "Icône utilisée pour les appareils d'Apple. Taille recommandée 144 px par 144 px." notification_email: "L'adresse de courriel dans le champs De qui sera utilisée pour envoyer les courriels systèmes essentiels. Le nom de domaine spécifié doit avoir les informations SPF, DKIM et PTR inversé renseignés correctement pour que le courriel arrive à destination." email_custom_headers: "Une liste délimité par des (|) pipes d'entêtes de courriel" @@ -651,14 +730,12 @@ fr: enable_noscript_support: "Activer le support des moteurs de recherches standards via le tag noscript" allow_moderators_to_create_categories: "Autoriser les modérateurs à créer de nouvelles catégories" cors_origins: "Requêtes cross-origin (CORS) autorisées. Chaque origine doit inclure http:// ou https://. la variable d'environnement DISCOURSE_ENABLE_CORS doit être renseignée à true pour activer CORS." - top_menu: "Déterminer les éléments qui apparaissent dans la navigation de la page d'accueil, et dans quel ordre. Exemple latest|new|unread|starred|categories|top|read|posted" + top_menu: "Déterminer les éléments qui apparaissent dans la navigation de la page d'accueil, et dans quel ordre. Exemple latest|new|unread|categories|top|read|posted|bookmarks" post_menu: "L'ordre des éléments dans le menu de rédaction. Exemple like|edit|flag|delete|share|bookmark|reply" post_menu_hidden_items: "Les éléments du menu qui seront cachés par défaut jusqu’à extension du menu." share_links: "Choix des éléments qui doivent apparaître dans la fenêtre de partage, et leur ordre." track_external_right_clicks: "Suivi des clics sur les liens externes (ex: ouverture dans un nouvel onglet) désactivé par défaut car nécessite une ré-écriture de toutes les urls" - topics_per_page: "Combien de sujets seront chargés par défaut sur la liste des sujets et lors du défilement vers le bas pour charger des sujets supplémentaires" - posts_chunksize: "Combien de messages seront chargés par défaut sur un sujet, et quand défiler vers le bas pour charger des messages supplémentaires." - site_contact_username: "Tous les messages privés automatisés le seront depuis cet utilisateur; si non renseigné, le compte par défaut System sera utilisé." + site_contact_username: "Un pseudo de responsable à partir de laquelle seront envoyés les message privés. Si laissé vide, le compte système sera utilisé." send_welcome_message: "Envoyer à tous les nouveaux utilisateurs un message privé avec un guide de démarrage rapide." suppress_reply_directly_below: "Ne pas afficher le panneau extensible des réponses d'un message quand la seule réponse est juste en dessous ce dernier." suppress_reply_directly_above: "Ne pas afficher 'en réponse à' sur un message quand la seule réponse est juste en dessus de ce dernier." @@ -668,7 +745,7 @@ fr: topics_per_period_in_top_summary: "Nombre de meilleurs sujets affichés dans le résumé par défaut des meilleurs sujets." topics_per_period_in_top_page: "Nombre de meilleurs sujets affichés lorsqu'on sélectionne \"Voir plus\" des meilleurs sujets." redirect_users_to_top_page: "Rediriger automatiquement les nouveaux utilisateurs et les longues absences sur la page Top." - show_email_on_profile: "Afficher l'adresse du courriel de l'utilisateur sur leur page utilisateur (seulement visible pour l'utilisatuer et les équipes techniques)" + show_email_on_profile: "Afficher l'adresse du courriel de l'utilisateur sur leur page utilisateur (seulement visible pour l'utilisateur et les équipes techniques)" email_token_valid_hours: "Les jetons (tokens) de Mot de passe oublié / Activation de comptes sont valides (n) jours." email_token_grace_period_hours: "Les jetons (tokens) de Mot de passe oublié / Activation de comptes sont encore valides pour une période de grâce de (n) heures après leur expiration." enable_badges: "Activer le système de badges" @@ -676,6 +753,7 @@ fr: email_domains_blacklist: "Liste des domaines de courriel qui ne sont pas autorisé lors de la création de compte. Exemple: mailinator.com|trashmail.net" email_domains_whitelist: "Liste des domaines de courriel avec lesquelles les utilisateurs DOIVENT s'enregistrer. ATTENTION : les utilisateurs ayant une adresse de courriel sur un autre domaine ne pourrons pas s'enregistrer." forgot_password_strict: "Ne pas tenir informé de l'existance d'un compte utilisateur quand un utilisateur utilise le formulaire d'oubli de mot de passe." + log_out_strict: "Lors de la déconnexion, déconnecter TOUTES les sessions pour l'utilisateur sur tous les appareils" version_checks: "Ping les serveurs de Discourse afin d'obtenir les mises à jours et affiche les nouveaux messages d'information dans le tableau de bord /admin" new_version_emails: "Envoyer un courriel à contact_email quand une nouvelle version de Discourse est disponible." port: "SEULEMENT POUR LES DÉVELOPPEURS ! ATTENTION ! Utiliser ce port HTTP plutôt que celui par défaut 80. Laissez vide par défaut 80." @@ -688,9 +766,10 @@ fr: max_username_length: "Longueur maximum des pseudos en caractères. ATTENTION : TOUS LES UTILISATEURS QUI DÉPASSE CETTE LIMITE NE POURRONS PLUS ACCÉDER AU SITE. " min_password_length: "Longueur minimale du mot de passe." block_common_passwords: "Ne pas autoriser les mots de passe qui font parti des 10 000 les plus utilisés." - enable_sso: "Activer l'authentification unique via un site externe (Note: désactive les invitations)" + enable_sso: "Activer l'authentication unique via un site externe (AVERTISSEMENT : si activé, peut empêcher toute connexion en cas de mauvaise configuration ; désactive aussi les invitations)" + enable_sso_provider: "Implémenter le procotole Discourse SSO à /session/sso_provider, requiert sso_secret" sso_url: "URL de l'authentification unique" - sso_secret: "Chaîne de caractères secrète utilisée pour crypter/décrypter les informations SSO, assurez-vous qu'elle est de 10 caractères ou plus" + sso_secret: "Chaîne de caractères secrète utilisée pour authentifier les informations SSO par cryptographie, assurez-vous qu'elle est de 10 caractères ou plus" sso_overrides_email: "Surcharger les adresses de courriel locales avec les adresses de courriel externes d'un SSO (ATTENTION: Des écarts peuvent se produire due aux règles locales sur les adresses de courriel)" sso_overrides_username: "Surcharger les pseudos avec les pseudos du SSO (ATTENTION: Des écarts peuvent se produire due aux règles locales sur les pseudos)" sso_overrides_name: "Surcharger les noms locaux avec les noms externes du SSO (ATTENTION: Des écarts peuvent se produire due aux règles locales sur les noms)" @@ -725,19 +804,20 @@ fr: rate_limit_new_user_create_post: "Après avoir posté un message, les nouveaux utilisateurs doivent attendre (n) secondes avant de pouvoir en poster un nouveau." max_likes_per_day: "Nombre maximum de J'aime par utilisateur chaque jour." max_flags_per_day: "Nombre maximum de signalement par utilisateur chaque jour." - max_bookmarks_per_day: "Nombre maximum de signets par utilisateur chaque jour." + max_bookmarks_per_day: "Nombre maximum de signets par utilisateur et par jour." max_edits_per_day: "Nombre maximum de modifications par utilisateur chaque jour." - max_stars_per_day: "Nombre maximum de favoris par utilisateur chaque jour." max_topics_per_day: "Nombre maximum de sujet qu'utilisateur peut créer par jour." max_private_messages_per_day: "Nombre maximum de message privé qu'utilisateur peut créer par jour." + max_invites_per_day: "Nombre maximum d'invitations qu'un utilisateur peut envoyer par jour." suggested_topics: "Nombre de sujets suggérés affichés en bas d'un sujet." limit_suggested_to_category: "Afficher uniquement les sujets de la catégorie courante dans les sujets similaires." clean_up_uploads: "Retirer les fichiers téléchargés orphelins pour prévenir les hébergements illégaux. ATTENTION: vous devriez faire une sauvegarde de votre répertoire /uploads avant d'activer ce paramètre." clean_orphan_uploads_grace_period_hours: "La période de grâce (en heures) avant qu'un fichier envoyé orphelin soit retiré." purge_deleted_uploads_grace_period_days: "La période de grâce (en jours) avant qu'un fichier envoyé et supprimé soit effacé." - purge_inactive_users_grace_period_days: "Période de grâce (en jours) avant qu'un utilisateur qui n'a pas activé son compte soit supprimé." + purge_unactivated_users_grace_period_days: "Période de grâce (en jours) avant qu'un utilisateur qui n'a pas activé son compte soit supprimé." enable_s3_uploads: "Placez les fichiers envoyés sur un stockage Amazon S3. IMPORTANT: nécessite un accès valide à S3 (l'identifiant et la clé secrète)." s3_use_iam_profile: 'Utiliser un role AWS EC2 IAM pour la récupération des clés. NOTE: si activé, surcharge les paramètres "s3 access key id" et "s3 secret access key".' + s3_upload_bucket: "Le nom du bucket Amazon S3 qui contiendra les fichiers téléchargés. ATTENTION : doit être en minuscule, sans points et sans caractères de soulignement." s3_access_key_id: "L' access key Amazon S3 qui sera utilisée pour uploader les images." s3_secret_access_key: "La clé secrète Amazon S3 qui va être utilisée pour uploader des images." s3_region: "Le nom de la région Amazon S3 qui va être utilisée pour uploader des images." @@ -784,6 +864,7 @@ fr: min_title_similar_length: "La taille minimale d'un titre avant que l'on vérifie l'existence de sujets identiques." min_body_similar_length: "La taille minimale d'un message avant que l'on vérifie l'existence de sujets identiques." category_colors: "Une liste de couleurs en hexadécimale autorisées pour les catégories." + category_style: "Style visuel pour les badges de catégorie." max_image_size_kb: "La taille maximale des images en Ko. Doit être configuré dans nginx (client_max_body_size) / apache ou proxy aussi." max_attachment_size_kb: "La taille maximale des fichiers envoyés en Ko. Doit être configurer dans nginx (client_max_body_size) / apache ou proxy aussi." authorized_extensions: "Une liste d'extensions de fichier autorisés pour les envois sur le serveur (mettre '*' pour autoriser tout les types)" @@ -808,12 +889,17 @@ fr: white_listed_spam_host_domains: "Une liste des domaines exclus des hôtes testés comme spam. Les nouveaux utilisateurs ne seront jamais restreint dans la création de message contenant des liens vers ses domaines. " staff_like_weight: "Quel poids supplémentaire donner aux J'aime de l'équipe." levenshtein_distance_spammer_emails: "Lorsque des courriels correspondent à des spammer, la différence du nombre de caractère permettra toujours une correspondance floue." + max_new_accounts_per_registration_ip: "S'il y a déjà (n) Niveau de confiance 0 comptes à partir de cette adresse IP ( et aucun n'est un membre du personnel ou au NC2 ou ultérieure), ne plus accepter de nouvelles inscriptions de cette IP." + min_ban_entries_for_roll_up: "En cliquant sur le bouton Consolider, une liste d'au moins (N) adresses interdites sera remplacée par une plage de sous réseau." + max_age_unmatched_emails: "Effacer les adresses de courriel sous surveillance sans correspondance après (N) jours" + max_age_unmatched_ips: "Effacer les adresses IP sous surveillenace sans correspondance après (N) jours" + num_flaggers_to_close_topic: "Nombre minimum de signalements uniques requis pour automatiquement suspendre un sujet pour intervention" + num_flags_to_close_topic: "Nombre minimum de signalements uniques requis pour automatiquement suspendre un sujet pour intervention" reply_by_email_enabled: "Activer les réponses aux sujets via courriel." reply_by_email_address: "Modèle pour la réponse par courriel entrant; exemple : %{reply_key}@reply.example.com ou replies+%{reply_key}@example.com" disable_emails: "Désactiver l'envoi de les courriels depuis Discourse." strip_images_from_short_emails: "Retirer les images des courriels dont la taille est inférieur à 2800 Octets" short_email_length: "Taille des courriels courts en Octets" - enable_email_names: "Autoriser l'affichage des noms complets des utilisateurs dans les courriels. Décochez-le pour cacher les noms complets dans les courriels." pop3_polling_enabled: "Utiliser POP3 pour les réponses via courriel." pop3_polling_ssl: "Utiliser SSL pour les connections au serveur POP3. (Recommandé)" pop3_polling_period_mins: "La période en minute entre chaque vérification du compte POP3 des courriels.\nNote: nécessite un redémarrage de la machine." @@ -829,15 +915,18 @@ fr: relative_date_duration: "Nombre de jours après la création d'un message à partir desquels les dates seront affichées en absolu (20 Fév) plutôt qu'en relatif (7j)" delete_user_max_post_age: "Interdire la suppression des utilisateurs dont le premier message est date de plus de (n) jours." delete_all_posts_max: "Le nombre maximum de messages qui peuvent être supprimés en une seule fois avec le bouton Supprimer tous les messages. Si un utilisateur a plus que ce nombre de messages, ses messages ne pourront pas être supprimés en une seule fois et l'utilisateur ne pourra pas être supprimé." - username_change_period: "Le nombre de jours après l'enregistrement pour pouvoir changer de pseudo (0 pour empêcher le changement de pseudo)." - email_editable: "Autoriser les utilisateurs à changer leur adresse de courriel après l'inscription." + username_change_period: "Le nombre de jours après l'enregistrement pour pouvoir modifier le pseudo (0 pour empêcher la modification de pseudo)." + email_editable: "Autoriser les utilisateurs à modifier leur adresse de courriel après l'inscription." logout_redirect: "Adresse de redirection après déconnexion, ex: http://monsite.com/logout" allow_uploaded_avatars: "Autoriser les utilisateurs à envoyer leurs propres avatars." allow_animated_avatars: "Autoriser les utilisateurs à utiliser des avatars ens gif animés. ATTENTION: il est hautement recommandé d'exécuter la tâche rake avatars:refresh après avoir changé ce paramètre." - automatically_download_gravatars: "Télécharger les gravatars pour les utilisateurs lors de la création de compte ou du changement de courriel." + allow_animated_thumbnails: "Créer des aperçus animés pour les gifs animés." + automatically_download_gravatars: "Télécharger les gravatars pour les utilisateurs lors de la création de compte ou de la modification de courriel." digest_topics: "Nombre maximum de sujets à afficher dans le courriel de résumé." digest_min_excerpt_length: "Taille minimum du résumé des messages dans les courriels, en caractères." - default_digest_email_frequency: "A quelle fréquence les utilisateurs reçoivent-ils les courriels par défaut. Ils peuvent changer ce paramétrage dans leur préférences." + default_digest_email_frequency: "A quelle fréquence les utilisateurs reçoivent-ils les courriels par défaut. Ils peuvent modifier ce paramétrage dans leur préférences." + suppress_digest_email_after_days: "Ne pas envoyer de résumés courriel aux utilisateurs qui n'ont pas visité le site depuis (n) jours." + disable_digest_emails: "Désactiver les résumés par courriels pour tous les utilisateurs." default_external_links_in_new_tab: "Les liens externes s'ouvrent dans un nouvel onglet. Les utilisateurs peuvent modifier ceci dans leurs préférences." detect_custom_avatars: "Vérifier ou pas que les utilisateurs ont envoyés des avatars personnalisés." max_daily_gravatar_crawls: "Nombre maximum de fois que Discourse vérifiera Gravatar pour des avatars personnalisés en une journée." @@ -849,10 +938,10 @@ fr: suppress_uncategorized_badge: "Ne pas afficher le badge pour les sujets non catégorisés dans les listes des sujets." global_notice: "Affiche un bandeau global URGENT pour tout les utilisateurs du site, laissez vide pour le cacher (HTML autorisé)." disable_edit_notifications: "Désactiver les notifications de modifications par l'utilisateur système lorsque l'option 'download_remote_images_to_local' est activée." - enable_names: "Autoriser l'affichage des noms complets des utilisateurs. Désactiver pour cacher les noms complets." + enable_names: "Autoriser l'affichage des noms complets des utilisateurs dans leur profil, sur leur carte d'utilisateur et dans les courriels. Décocher pour cacher les noms complets partout." display_name_on_posts: "Afficher le nom complet de l'utilisateur dans ses messages en plus de son @pseudo." invites_per_page: "Afficher les invitations sur la page de l'utilisateur." - short_progress_text_threshold: "Si le nombre de messages dans un sujet dépasse ce nombre, la barre de progression affichera uniquement le numéro de message actuel. Si vous modifiez la largeur de la barre de progression, vous devez peut être changer cette valeur." + short_progress_text_threshold: "Si le nombre de messages dans un sujet dépasse ce nombre, la barre de progression affichera uniquement le numéro de message actuel. Si vous modifiez la largeur de la barre de progression, vous devrez peut-être modifier cette valeur." default_code_lang: "Coloration syntaxique par défaut appliquée à la syntaxe des langages de programmation des blocs de code GitHub (lang-auto, Ruby, Python, etc)" warn_reviving_old_topic_age: "Lorsque quelqu'un commence à répondre à un sujet dont la dernière réponse est vielle de plusieurs jours, un avertissement sera affiché. Désactiver la fonctionnalité en indiquant: 0." autohighlight_all_code: "Forcer la mise en évidence de tout les textes dans les balises code, même si ils ne correspondent à aucun langage de programmation." @@ -871,6 +960,8 @@ fr: show_create_topics_notice: "Si le site contient moins de 5 sujets publics, afficher un message pour demander aux administrateurs de créer d'autres sujets." vacuum_db_days: "Exécuter VACUUM FULL ANALYZE pour récupérer de l'espace dans la base de donnée après une migration (0 pour désactivé) " prevent_anons_from_downloading_files: "Refuser le téléchargement de pièces jointes aux utilisateurs anonymes. ATTENTION: cela empêchera de fonctionner les ressources envoyées en pièce jointe qui ne sont pas des images." + enable_emoji: "Activer les emojis" + emoji_set: "Comment aimeriez-vous vos emoji ?" errors: invalid_email: "Adresse de courriel invalide." invalid_username: "Il n'y a pas d'utilisateur ayant ce pseudo." @@ -883,6 +974,7 @@ fr: invalid_string_min_max: "Doit être compris(e) entre %{min} et %{max} caractères." invalid_string_min: "Doit être d'au moins %{count} caractères." invalid_string_max: "Ne doit pas être supérieur à %{max} caractères." + invalid_reply_by_email_address: "La valeur doit contenir '%{reply_key}' et être différente de la notification email." notification_types: mentioned: "%{display_username} vous a mentionné dans %{link}" liked: "%{display_username} a aimé votre message dans %{link}" @@ -902,6 +994,11 @@ fr: category: 'Catégories' topic: 'Résultats' user: 'Utilisateurs' + sso: + not_found: "Impossible de rechercher ou de créer un compte, contacter l'administrateur du site" + account_not_approved: "Le compte est en attente d'approbation. Vous recevrez un avis par courriel une fois approuvé." + unknown_error: "Erreur de mise à jour des informations, contacter l'administrateur du site" + timeout_expired: "La connexion au compte a été interrompue, veuillez vous connecter à nouveau" original_poster: "Créateur du sujet" most_posts: "Le plus de messages" most_recent_poster: "Auteur le plus récent" @@ -918,6 +1015,10 @@ fr: other: "J'ai déplacé %{count} messages dans le sujet : %{topic_link}" change_owner: post_revision_text: "Auteur du message modifié de %{old_user} vers %{new_user}" + emoji: + errors: + name_already_exists: "Désolé, le nom '%{name}' est déjà utilisé par un autre emoji." + error_while_storing_emoji: "Désolé, il y a eu une erreur lors de l'enregistrement de l'emoji." topic_statuses: archived_enabled: "Ce sujet est maintenant archivé. Il est gelé et ne peut plus être modifié d'aucune façon." archived_disabled: "Ce sujet est maintenant dé-archivé. Il n'est plus gelé, et peut être modifié." @@ -925,27 +1026,30 @@ fr: closed_disabled: "Ce sujet est maintenant ouvert. Les nouvelles réponses sont autorisées." autoclosed_enabled_days: one: "Cette discussion a été automatiquement fermée après un jour. Aucune réponse n'est permise dorénavant." - other: "Cette discussion a été automatiquement fermée après %{count} jours. Aucune réponse n'est permise dorénavant." + other: "Ce sujet a été automatiquement fermé après %{count} jours. Aucune réponse n'est permise dorénavant." autoclosed_enabled_hours: one: "Cette discussion a été automatiquement fermée après une heure. Aucune réponse n'est permise dorénavant." - other: "Cette discussion a été automatiquement fermée après %{count} heures. Aucune réponse n'est permise dorénavant." + other: "Ce sujet a été automatiquement fermé après %{count} heures. Aucune réponse n'est permise dorénavant." autoclosed_enabled_minutes: one: "Cette discussion a été automatiquement fermée après une minute. Aucune réponse n'est permise dorénavant." - other: "Cette discussion a été automatiquement fermée après %{count} minutes. Aucune réponse n'est permise dorénavant." + other: "Ce sujet a été automatiquement fermé après %{count} minutes. Aucune réponse n'est permise dorénavant." autoclosed_enabled_lastpost_days: one: "Cette discussion a été automatiquement fermée après un jour. Aucune réponse n'est permise dorénavant." - other: "Cette discussion a été automatiquement fermée après %{count} jours. Aucune réponse n'est permise dorénavant." + other: "Ce sujet a été automatiquement fermé après %{count} jours. Aucune réponse n'est permise dorénavant." + autoclosed_enabled_lastpost_hours: + one: "Cette discussion a été automatiquement fermée après 1 heure suivant le dernier commentaire. Aucune réponse n'est permise dorénavant." + other: "Ce sujet a été automatiquement fermé après %{count} heures suivant le dernier commentaire. Aucune réponse n'est permise dorénavant." autoclosed_enabled_lastpost_minutes: one: "Cette discussion a été automatiquement fermée une minute après le dernier message. Aucune réponse n'est permise dorénavant." - other: "Cette discussion a été automatiquement fermée %{count} minutes après le dernier message. Aucune réponse n'est permise dorénavant." + other: "Ce sujet a été automatiquement fermé %{count} minutes après le dernier message. Aucune réponse n'est permise dorénavant." autoclosed_disabled: "Ce sujet est maintenant ouvert. Les nouvelles réponses sont autorisées." - autoclosed_disabled_lastpost: "Cette discussion est maintenant ouverte. Vous pouvez y participer." + autoclosed_disabled_lastpost: "Ce sujet est maintenant ouvert. Vous pouvez y participer." pinned_enabled: "Ce sujet est maintenant épinglé. Il apparaîtra en haut de sa catégorie jusqu'à ce qu'il soit désépinglé par un modérateur, ou individuellement par les utilisateurs eux-mêmes." pinned_disabled: "Ce sujet est maintenant dé-épinglé. Il n'apparaîtra plus en haut de sa categorie." pinned_globally_enabled: "Ce sujet est maintenant épinglé pour tous. Il apparaîtra en haut de sa catégorie et de chaque liste de sujets jusqu'à ce qu'il soit désépinglé par un modérateur, ou individuellement par les utilisateurs eux-mêmes." pinned_globally_disabled: "Ce sujet est maintenant désépinglé. Il n'apparaîtra plus en haut de sa catégorie." - visible_enabled: "Cette discussion est maintenant visible. Elle sera affichée dans la liste des discussions." - visible_disabled: "Cette discussion est maintenant invisible. Elle ne sera plus affichée dans la liste des discussions. Le seul moyen d'y accéder est par lien direct." + visible_enabled: "Ce sujet est maintenant visible. Il sera affiché dans la liste des sujets." + visible_disabled: "Ce sujet est maintenant invisible. Il ne sera plus affiché dans la liste des sujets. Le seul moyen d'y accéder est par lien direct." login: not_approved: "Votre compte n'a pas encore été approuvé. Vous serez notifié par courriel quand cela sera fait." incorrect_username_email_or_password: "pseudo, adresse de courriel ou mot de passe incorrect" @@ -959,7 +1063,7 @@ fr: errors: "%{errors}" not_available: "Pas disponible. Essayez %{suggestion} ?" something_already_taken: "Quelque chose c'est mal passé. Peut-être que votre pseudo ou votre adresse de courriel est déjà enregistré ? Essayez le lien : j'ai oublié mon mot de passe." - omniauth_error: "Désolé, il y a eu une erreur lors de l'autorisation de votre compte %{strategy}. Vous n'avez peut-être pas approuvé l'autorisation ?" + omniauth_error: "Désolé, une erreur est survenue lors de l'autorisation de votre compte. Vous n'avez peut-être pas approuvé l'autorisation ?" omniauth_error_unknown: "Quelque chose s'est mal passé lors de votre connexion, merci de réessayer." new_registrations_disabled: "La création de nouveau compte n'est pas autorisé pour le moment." password_too_long: "Les mots de passe sont limités à 200 caractères." @@ -1014,9 +1118,9 @@ fr: invite_password_instructions: subject_template: "Renseignez le mot de passe pour votre compte utilisateur %{site_name} " text_body_template: | - Meric d'avoir accepter votre invitation pour %{site_name} -- bienvenue ! + Merci d'avoir accepté notre invitation pour %{site_name} -- bienvenue ! - Pour vous reconnecter, cliquez sur le liens suivant et choisissez un mot de passe: + Pour vous reconnecter, cliquez sur le lien suivant et choisissez un mot de passe: %{base_url}/users/password-reset/%{email_token} test_mailer: subject_template: "[%{site_name}] Test de délivrabilité d'un courriel" @@ -1031,15 +1135,15 @@ fr: - Sachez comment consulter le format brut d'un courriel dans votre client de courriel, pour vous permettre de consulter les entêtes pour obtenirs des indices importants. Dans Gmail, c'est l'option "show original" dans le menu contextuel situé en haut à droite de chaque courriel. - - **IMPORTANT:** Votre fournisseur internet a-t-il renseigné un enregistrement de DNS inversé pour associer le nom de domaine et l'addresse IP utilisée pour envoyer vos courriels ? [Testez l'enregistrement inversé de DNS de type PTR][2] ici. Si votre fournisseur d'accès internet n'a pas renseigné de manière correcte l'enregistrement de DNS inversé, il est très peu probable que vos courriels soient réceptionnés. + - **IMPORTANT:** Does your ISP have a reverse DNS record entered to associate the domain names and IP addresses you send mail from? [Test your Reverse PTR record][2] here. If your ISP does not enter the proper reverse DNS pointer record, it's very unlikely any of your email will be delivered. - l'enregistrement [SPF record][8] de votre nom de domaine est-il correcte ? [Testez votre enregistrement SPF][1] ici Veuillez noter que le type officiel d'enregistrment est TXT pour l'enregistrement SPF. - l'enregistrement [DKIM record][3] de votre nom de domaine est-il correcte ? Ce point particulier augmentera de manière significative la bonne distribution de vos courriels. [Testez votre enregistrement DKIM][7] ici. - - Si vous utisez votre propre serveur de courriels, vérifiez pour être certain que les adresses IP de votre serveur de courriel ne sont dans [aucune listes noires][4]. Vérifiez également que le courriel est envoyé en utilisant des noms de machines complets qui se traduisent en DNS dans les messages de type HELO. Si ce n'est pas le cas, votre courriel sera rejeté par les services de messageries. + - If you run your own mail server, check to make sure the IPs of your mail server are [not on any email blacklists][4]. Also verify that it is definitely sending a fully-qualified hostname that resolves in DNS in its HELO message. If not, this will cause your email to be rejected by many mail services. - (La manière la plus *facile* est de créer un compte gratuit sur [Mandrill][md] ou [Mailgun][mg] ou [Mailjet][mj], qui ont des options gratuites généreuses et qui seront parfaite pour de petites communautés. Vous devrez quand même configurer les entrées SPF et DKIM dans votre DNS!) + (La mannière la plus *facile* est de créer un compte gratuit sur [Mandrill][md] ou [Mailgun][mg] ou [Mailjet][mj], qui ont des options gratuites généreuses et qui seront parfaite pour de petites communautés. Vous devrez quand même configurer les entrées SPF et DKIM dans votre DNS!) Nous espérons que vous avez bien reçu ce courriel de test de distribution ! @@ -1052,16 +1156,11 @@ fr: [2]: http://mxtoolbox.com/ReverseLookup.aspx [3]: http://www.dkim.org/ [4]: http://whatismyipaddress.com/blacklist-check - [5]: %{base_url}/unsubscribe [7]: http://dkimcore.org/tools/dkimrecordcheck.html [8]: http://www.openspf.org/SPF_Record_Syntax [md]: http://mandrill.com [mg]: http://www.mailgun.com/ [mj]: http://www.mailjet.com/pricing - - ---- - - Il doit y avoir un pied de page pour le désabonnement sur chaque courriel que vous envoyez, en voici un exemple. Ce courriel a été envoyé par Name of Company, 55 Main Street, Anytown, USA 12345. Si vous ne souhaitez plus recevoir de nouveaux courriels, [cliquez ici pour le désabonnement][5]. new_version_mailer: subject_template: "[%{site_name}] Nouvelle version de Discourse, mise à jour disponible" text_body_template: | @@ -1099,44 +1198,46 @@ fr: flags_reminder: flags_were_submitted: one: "Il y a un signalement qui a été soumis il y a plus de %{count} heures." - other: "Il y a des signalements qui ont été soumis il y a plus de %{count} heures." + other: "Ces signalements ont été soumis il y a plus de %{count} heures." please_review: "Veuillez examiner cela." post_number: "message" + how_to_disable: 'Vous pouvez désactiver ou modifier la fréquence de ce courriel de rappel via le réglage "m''informer sur les signalements".' subject_template: one: "un signalement en attente de traitement" other: "%{count} signalements en attente de traitement." flag_reasons: - off_topic: "Votre message a été signalé comme étant **hors-sujet** : la communauté pense qu'il ne correspond pas au sujet, tel que le titre et le premier message le définisse." - inappropriate: "Votre message a été signalé comme étant **inapproprié** : la communauté pense qu'il est offensant, abusif ou en violation du [règlement de la communauté](/guidelines)." - spam: "Votre message a été signalé comme étant du **spam** : La communauté pense que c'est une publicité, non utile ou pertinente dans ce sujet, mais de nature promotionnelle." - notify_moderators: "Votre message a été signalé **aux modérateur**: la communauté pense que ce message requiert l'intervention d'un modérateur." + off_topic: "Votre message a été signalé comme étant **hors sujet**: la communauté considère qu'il ne correspond au sujet en discussion, qui est fixé par son titre et son premier message." + inappropriate: "Votre message a été signalé comme étant **inapproprié**: la communauté considère que son contenu est offensif, abusif ou enfreint notre [règlement interne](/guidelines)." + spam: "Votre message a été signalé comme étant du **spam**: la communauté considère qu'il s'agit d'une publicité, ou du contenu trop promotionnel au lieu d'être utile et à propos du sujet en discussion." + notify_moderators: "Votre message a été **signalé aux modérateurs**: la communauté considère que quelque chose requiert une intervention d'un des responsables." flags_dispositions: agreed: "Merci de nous en informer. Nous sommes en accord avec votre signalement et nous travaillons à sa résolution." agreed_and_deleted: "Merci de nous en informer. Nous sommes en accord avec votre signalement et avons supprimé le message." disagreed: "Merci de nous en informer. Nous travaillons à sa résolution." deferred: "Merci de nous en informer. Nous travaillons à sa résolution." deferred_and_deleted: "Merci de nous en informer. Nous avons supprimé le message." + temporarily_closed_due_to_flags: "Ce sujet est temporairement clos dû à un trop grand nombre de signalements" system_messages: post_hidden: subject_template: "Message caché suite à un signalement par la communauté" - usage_tips: - text_body_template: "Ce message privé contient quelques astuces pour vous aider à démarrer rapidement.\n\n## Continuez de descendre\n\nIl n'y a pas de bouton Page Suivante ni de numéro de page – pour en lire plus, **il vous suffit de descendre !**\n\nLorsque de nouvelles réponses arrivent, elles apparaissent automatiquement. \n\n## Où suis-je ?\n\n- Pour la recherche, votre page d'utilisateur, ou le menu, utiliser les **boutons icônes en haut à droite**.\n\n- Dans la liste des sujets, le titre vous emmènera toujours vers le prochain message non lu. Utiliser les colonnes Activité ou Messages pour allez au premier ou au dernier message. \n\n- Lorsque vous lisez un sujet, retournez en haut de celui ↑ en cliquant sur le titre. Cliquez sur la barre de progression en bas à droite pour avoir une navigation complète, ou utilisez les touches Home et Fin. \n\n \n\n## Comment je réponds ?\n\n- Pour répondre au sujet dans sa globalité, utilisez le bouton \"Répondre\" tout en bas de la page.\n\n- Pour répondre à un message spécifique, utilisez le bouton \"Répondre\" sur le message.\n\n - Si vous voulez continuer le sujet dans une section différente, mais garder le lien entre votre sujet et le message qui vous l'a inspiré, utilisez la fonction Répondre en créant un nouveau sujet à droite de chaque message.\n\nPour citer quelqu'un dans votre message, sélectionnez le texte que vous voulez citer et appuyez sur un des boutons Répondre.\n\n\n\nPour mentionner le pseudo d'un utilisateur, commencez à taper `@` et une liste d'auto-complétion apparaîtra.\n\n\n\nConcernant les [icones Emoji](http://www.emoji.codes/), commencez par écrire `:` ou le traditionnel smiley `:)` :smile: \n\n## Que puis-je faire d'autre ?\n\nÀ la fin de chaque message il y a un ensemble de boutons pour les différentes actions possibles.\n\n\n\nPour faire savoir à quelqu'un que vous avez apprécié son message, cliquez sur le bouton *j'aime** en bas du message. Si vous voyez un problème avec un message, n'hésitez pas à cliquer sur le bouton **signaler**\ - \ pour avertir en privé l'auteur ou les modérateurs du contenu de ce message.\n\nVous pouvez aussi **partager** un liens vers un message ou l'ajouter à vos **signets** pour le retrouver sur votre page d'utilisateur. \n\n## Qui me parle ?\n\nQuand quelqu'un répond à votre message, vous cite, ou mentionne votre `@pseudo`, un nombre apparaitra immédiatement en haut à droite de la page. Utilisez-le pour consulter vos **notifications**.\n\n\n\nNe soyez pas inquiêt de manquer des notifications – \nLes réponses directes (et les messages privés) vous seront envoyés par courriel si vous n'êtes pas présent sur le site lorsqu'ils sont envoyés. \n\n## Quand est-ce qu'une conversation est nouvelle ?\n\nPar défaut, toutes les conversations de moins de deux jours sont considérées comme nouvelles, et chaque sujets auxquels vous avez participé (répondus, créés ou lu pendant un long moment) seront automatiquements suivis. \n\nVous verrez le badge bleu de nouveauté et le nombre de nouveaux messages au côté de ces discussions:\n\n\n\nVous pouvez modifier l'état du suivi des notifications d'une discussion en utilisant les boutons en bas de la discussion\ - \ (vous pouvez également le modiifer au niveau des catégories).\nPour changer la façon dont vous suivez les discussions, ou pour définir de nouveaux suivis, consultez [vos préférences utilisateurs](/my/preferences). \n\n## Quelles sont les choses que je ne peux pas faire ?\n\nLes nouveaux utilisateurs sont limités pour des raisons de sécurité. Plus vous participerez, plus vous gagnerez la confiance de la communauté et les restrictions disparaîtrons. A plus haut [niveau de confiance](https://meta.discourse.org/t/what-do-user-trust-levels-do/4924), vous gagnerez de nouvelles compétences pour nous aider à gérer la communauté. \n" + text_body_template: | + Bonjour, + + Ceci est un message automatique de %{site_name} pour vous informer que votre message a été caché. + + %{base_url}%{url} + + %{flag_reason} + + Plusieurs membres ont signalé ce message avant qu'il ne soit caché, donc veuillez prendre en compte leurs remarques pour revoir votre message. **Vous pouvez modifier le message après %{edit_delay} minutes, et il sera automatiquement ré-affiché.** Ceci augmentera votre niveau de confiance sur le forum. + + Cependant, si le message est signalé par la communauté une seconde fois, il restera marqué jusqu’à l'intervention d'un modérateur – et ils pourront prendre d'autres mesures y compris l'éventuelle suspension de votre compte. + + Pour plus d'informations, merci de vous en référer aux [règles de la communauté](%{base_url}/guidelines). welcome_user: subject_template: "Bienvenue sur %{site_name} !" - text_body_template: | - Merci d'avoir rejoint %{site_name}, et bienvenue ! - - %{new_user_tips} - - Nous croyons au [comportement communautaire civilisé](%{base_url}/guidelines) en tous temps. - - Amusez-vous bien ! welcome_invite: subject_template: "Bienvenue sur %{site_name} !" - text_body_template: "Merci d'avoir accepté votre invitation sur %{site_name}, et bienvenue !\n\nNous vous avons automatiquement généré un pseudo : **%{username}**, mais vous pouvez le changer quand vous voulez en allant sur [votre profil][prefs].\n\nPour vous reconnecter, vous pouvez :\n\n1. Utiliser votre identifiant préféré -- mais votre **adresse de courriel** devra être la même que celle de l'invitation que vous avez reçue. Autrement, nous ne pourrons pas vous reconnaître.\n\n2. Créer un mot de passe unique sur votre [page de profil utilisateur][prefs], et vous connecter avec. \n\n%{new_user_tips}\n\nNous croyons au [comportement communautaire civilisé](%{base_url}/guidelines) en tous temps.\n\nAmusez-vous bien !\n\n[prefs] : %{user_preferences_url}\n" backup_succeeded: subject_template: "Sauvegarde terminée avec succès" text_body_template: "La sauvegarde a été un succès.\nVisitez la section [Administration > sauvegardes](/admin/backups) pour télécharger votre dernière sauvegarde." @@ -1177,16 +1278,16 @@ fr: %{logs} ``` csv_export_succeeded: - subject_template: "Données exportées avec succès." + subject_template: "Exportation des données complétée" text_body_template: | - Export des données réussi. + L'exportation de vos données a réussi! :dvd: - Télécharger le fichier CSV: %{file_name} + %{file_name} (%{file_size}) - Le lien de téléchargement du CSV expire après 48h. + Le lien de téléchargement ci-dessus expire après 48h. csv_export_failed: - subject_template: "Export échoué" - text_body_template: "L'export a échoué. Veuillez consultez les logs." + subject_template: "Échec de l'exportation des données " + text_body_template: "Nous sommes désolés, l'exportation de vos données a échoué. Veuillez consultez les logs ou contactez un modérateur." email_reject_trust_level: subject_template: "Problème de courriel - Niveau de confiance insuffisant " text_body_template: | @@ -1240,20 +1341,19 @@ fr: text_body_template: | Nous sommes désolé, mais l'envoi de votre courriel à destination de {destination} (intitulé %{former_title}) n'a pas fonctionné. - Aucune adresse de destination n'est reconnue. Veuillez vérifier que l'adresse du site est bien renseignée dans le champs To: (et non dans les champs C:: ou Bcc:), et que vous envoyez bien - à l'adresse de courriel fournie par l'équipe. + Aucune adresse de destination n'est reconnue. Veuillez vérifier que l'adresse du site est bien renseignée dans le champs To: (et non dans les champs C:: ou Bcc:), et que vous envoyez bien à l'adresse de courriel fournie par l'équipe. email_reject_topic_not_found: subject_template: "Erreur de courriel -- Le sujet est introuvable" text_body_template: | - Nous sommes désolé, mais votre email à destination de {destination} (intitulé %{former_title}) n'a pas fonctionné. + Nous sommes désolé, mais votre courriel à destination de {destination} (intitulé %{former_title}) n'a pas fonctionné. - Le sujet est introuvable. Si vous pouvez que c'est une erreur, contacter l’équipe du site. + Le sujet est introuvable et a peut-être été supprimé. Si vous considérez que c'est une erreur, contacter un responsable. email_reject_topic_closed: subject_template: "Erreur de courriel -- Le sujet est fermé" text_body_template: | - Nous sommes désolé, mais votre email à destination de {destination} (intitulé %{former_title}) n'a pas fonctionné. + Nous sommes désolé, mais votre courriel à destination de {destination} (intitulé %{former_title}) n'a pas fonctionné. - Le sujet est fermé. Si vous pouvez que c'est une erreur, contacter l’équipe du site. + Le sujet est fermé. Si vous considérez que c'est une erreur, contacter un responsable. email_error_notification: subject_template: "Problème de courriel - Erreur d'authentification POP3" text_body_template: | @@ -1381,13 +1481,17 @@ fr: --- %{respond_instructions} digest: + why: "Voici un bref résumé de ce qu'il s'est passé sur %{site_link} depuis votre dernière visite le %{last_seen_at}" subject_template: "[%{site_name}] Résumé du %{date}" new_activity: "Nouvelles activités sur vos sujets et messages :" top_topics: "Messages populaires" other_new_topics: "Sujets populaires" + unsubscribe: "Ce courriel résumé vous est transmis par %{site_link} car nous ne vous avons pas vu depuis quelques temps. Pour vous désabonner %{unsubscribe_link}." click_here: "cliquez ici" from: "Résumé de %{site_name}" read_more: "Lire la suite" + more_topics: "Il y a eu %{new_topics_since_seen} nouveaux sujets." + more_topics_category: "Plus de nouveaux sujets :" posts: one: "1 message" other: "%{count} messages" @@ -1418,20 +1522,6 @@ fr: %{base_url}/users/authorize-email/%{email_token} signup_after_approval: subject_template: "Votre compte a été approuvé sur %{site_name} !" - text_body_template: | - Bienvenue sur %{site_name} ! - - Un membre de l'équipe a approuvé votre compte sur %{site_name}. - - Cliquez sur le lien suivant pour confirmer et activer votre nouveau compte : %{base_url}/users/activate-account/%{email_token} - - Si le lien ci-dessus n'est pas cliquable, essayez de le copier et coller dans la barre d'adresse de votre navigateur web. - - %{new_user_tips} - - Nous croyons au [comportement communautaire civilisé](%{base_url}/guidelines) en tous temps. - - Amusez-vous bien ! signup: subject_template: "[%{site_name}] Activer votre nouveau compte" text_body_template: | @@ -1462,9 +1552,9 @@ fr: pasted_image_filename: "Image collée" store_failure: "Erreur lors du stockage de #%{upload_id} pour l'utilisateur #%{user_id}." attachments: - too_large: "Désolé, le fichier que vous essayez d'envoyer est trop gros (la taillle maximale est %{max_size_kb}% ko)." + too_large: "Désolé, le fichier que vous essayez d'envoyer est trop gros (la taillle maximale est %{max_size_kb}Ko)." images: - too_large: "Désolé, l'image que vous essayez d'envoyer est trop grande (taille maximum de %{max_size_kb}% ko), merci de la redimensionner et de réessayer." + too_large: "Désolé, l'image que vous essayez d'envoyer est trop grande (taille maximum de %{max_size_kb}Ko), merci de le redimensionner et de réessayer." fetch_failure: "Désolé, il y a eu une erreur lors de la récupération de l'image." unknown_image_type: "Désolé, mais le fichier que vous avez essayé d'envoyer ne semble pas être une image." size_not_found: "Désolé, mais nous n'avons pas pu déterminer la taille de votre image. Peut-être est-elle corrompue ?" @@ -1490,34 +1580,37 @@ fr: guidelines: "Règlement" privacy: "Protection des données" edit_this_page: "Modifier cette page" + csv_export: + boolean_yes: "Oui" + boolean_no: "Non" static_topic_first_reply: | Modifier le premier message de ce sujet pour changer le contenu de la page %{page_name}. guidelines_topic: title: "FAQ/Règlement" - body: | + body: |+ - ## [C'est un endroit civilisé pour un discussion publique](#civilized) + ## [Ceci est un endroit civilisé, merci de le respecter](#civilized) - Merci de traiter ce forum de discussion avec le même respect que vous pourriez avoir dans un parc public. Nous, aussi, sommes une ressource partagé par la communauté — un endroit pour partager des compétences, savoir et intérêts au travers de conversation. + Merci de traiter ce forum de discussion avec le même respect que vous pourriez avoir dans un parc public. Nous, aussi, sommes une ressource partagée par la communauté — un endroit pour partager des compétences, savoirs et intérêts au travers de conversations. - Il ne s'agit pas de règles absolues, uniquement des aides au bon sens de notre communauté. Suivez ces instructions pour garder cet endroit propre et bien éclairé pour le débat public civilisé. + Il ne s'agit pas de règles absolues, uniquement des aides au bon sens de notre communauté. Suivez ces instructions pour garder cet endroit propre et bien éclairé pour un débat public civilisé. - ## [Améliorer la discussion](#improve) + ## [Améliorez la discussion](#improve) Aidez nous à faire de cette endroit un endroit idéal pour la discussion en améliorant constamment le débat d'une certaine façon, même minime. Si vous n'êtes pas sûr que votre message soit utile à la discussion ou pourrait nuire à son utilité, pensez à ce que vous voulez dire et essayer de nouveau plus tard. - Les sujets abordés ici sont importantes pour nous, et nous voulons que vous agissez comme si ils comptent pour vous, aussi. Soyez respectueux des sujets et des gens d'en discuter, même si vous êtes en désaccord avec une partie de ce qui est dit. + Les sujets abordés ici sont importants pour nous, et nous voulons que vous agissiez comme s'ils comptaient également pour vous. Soyez respectueux des sujets et des autres utilisateurs, même si vous êtes en désaccord avec une partie de ce qui est dit. - Une façon d'améliorer la discussion est de découvrir celles qui sont déjà en cours. S'il vous plaît passer un peu de temps à parcourir les sujets ici avant de répondre ou démarrer votre propre discussion, et vous aurez une meilleure chance de rencontrer d'autres personnes qui partagent vos intérêts. + Une façon d'améliorer la discussion est de découvrir celles qui sont déjà en cours. Essayez de passez un peu de temps à parcourir les sujets avant de répondre ou démarrer votre propre discussion. De fait, vous aurez de plus grandes chances de rencontrer d'autres personnes qui partagent vos intérêts. - ## [Être agréable, même si vous n'êtes pas d'accord](#agreeable) + ## [Soyez agréable, même si vous n'êtes pas d'accord](#agreeable) - Vous pouvez répondre à quelque chose en étant en désaccord avec elle. C'est très bien. Mais, n'oubliez pas de _critiquer les idées, pas les gens_. S'il vous plaît éviter : + La critique peut-être constructive. Vous pouvez répondre à une personne en étant en désaccord avec elle. C'est très bien. Mais n'oubliez pas de *critiquer les idées, pas les gens*. À évitez: * Injures. * Attaques ad hominem. @@ -1530,21 +1623,21 @@ fr: ## [Votre participation compte](#participate) - Les conversations que nous avons ici donnent le ton pour tout le monde. Aidez-nous à influencer l'avenir de cette communauté en choisissant de s'engager dans des discussions qui font de ce forum un lieu intéressant - et en évitant celles qui n'en ont pas. + Les conversations que nous avons ici donnent le ton pour tout le monde. Aidez-nous à influencer l'avenir de cette communauté en choisissant de vous engager dans des discussions qui font de ce forum un lieu intéressant, et en évitant celles qui n'en ont pas. - %{company_name} fournit des outils qui permettent à la communauté d'identifier collectivement le meilleur (et le pire) des contributions: favoris, signets, des goûts, des signalements, des réponses, des vérifications, et ainsi de suite. Utilisez ces outils pour améliorer votre propre expérience, et celle de tous les autres aussi. + Discourse fournit des outils qui permettent à la communauté d'identifier collectivement le meilleur (et le pire) des contributions: favoris, signets, des *J'aime*, des signalements, des réponses, des vérifications, et ainsi de suite. Utilisez ces outils pour améliorer votre propre expérience, et celle de tous les autres aussi. Essayons de laisser notre parc mieux que nous l'avons trouvé. - ## [Si vous voyez un problème, Signaler le](#flag-problems) + ## [Si vous voyez un problème, signaler le](#flag-problems) - Modérateurs ont une rôle spécial; ils sont responsables de ce forum. Mais si vous aussi. Avec votre aide, les modérateurs peuvent être des facilitateurs de la communauté, et pas seulement les concierges ou les policiers. + Les modérateurs ont une rôle spécial; ils sont responsables de ce forum. Mais vous aussi. Avec votre aide, les modérateurs peuvent être des facilitateurs de la vie de la communauté, et pas seulement des concierges ou des policiers. - Quand vous voyez un mauvais comportement, ne répondez pas. Votre reconnaissance encourage le mauvais comportement, il consomme votre énergie et fait perdre du temps à chacun. -_signalez_ !. Si _suffisamment de signalements s'accumulent, des mesures seront prises, automatiquement ou par l'intervention d'un modérateur_. + Quand vous voyez un mauvais comportement, ne répondez pas. Votre reconnaissance encourage le mauvais comportement, il consomme votre énergie et fait perdre du temps à chacun. *Signalez le*. Si suffisamment de signalements s'accumulent, des mesures seront prises, automatiquement ou par l'intervention d'un modérateur. - Afin de maintenir notre communauté, les modérateurs se réservent le droit de supprimer tout contenu et n'importe quel compte d'utilisateur pour une raison quelconque, et ce à n'importe quel moment. Les modérateurs ne disposent pas d'un aperçu de nouveaux messages avant leur publication, les modérateurs et opérateurs du site n'assument aucune responsabilité pour tout contenu affiché par la communauté. + Afin de maintenir notre communauté, les modérateurs se réservent le droit de supprimer tout contenu et n'importe quel compte d'utilisateur pour une raison quelconque, et ce à n'importe quel moment. Les modérateurs ne disposent pas d'un aperçu des nouveaux messages avant leur publication, les modérateurs et opérateurs du site n'assument aucune responsabilité pour tout contenu affiché par la communauté. @@ -1552,12 +1645,12 @@ fr: Rien ne sabote une conversation saine mieux que la grossièreté : - * Soyez civilisé. Ne postez pas ce qu'une personne raisonnable jugerait offensive, discours violent ou haineux. - * Gardez-le forum propre. Ne publiez rien obscène ou sexuellement explicite. - * Se respecter mutuellement. Ne pas harceler ou chagriner quelqu'un, usurper l'identité ou exposer leurs informations privées de quelqu'un. - * Respectez notre forum. Ne postez pas de spam ou tout autre forme de vandalisme sur le forum. + * Soyez civilisé. Ne postez pas ce qu'une personne raisonnable jugerait offensif, ni de discours violent ou haineux. + * Gardez-le forum propre. Ne publiez rien d'obscène ou sexuellement explicite. + * Se respecter mutuellement est primordial. Ne pas harceler ou chagriner quelqu'un, ni usurper l'identité de quelqu'un ou exposer des informations privées le concernant. + * Respecter notre forum. Ne postez pas de spam ou tout autre forme de vandalisme. - Ceux-ci ne sont pas des clauses concrètes avec des définitions précises - éviter même de prendre l'apparence de l'une de ces choses. _Si vous n'êtes pas sûr, demandez-vous comment vous vous sentiriez si votre message était sélectionné sur la première page du journal Le Monde_. + Il ne s'agit pas ici de choses concrètes avec des définitions précises - évitez *même d'avoir l'air* d'agir de la sorte. Si vous avec un doute, demandez-vous comment vous vous sentiriez si votre post était publié sur la première page du Monde. Il s'agit d'un forum public, et les moteurs de recherche indexent ces discussions. Utilisez un langage, des liens et des images sans danger pour votre famille et vos amis. @@ -1565,27 +1658,34 @@ fr: ## [Gardez cet endroit propre](#keep-tidy) - Faites l'effort de mettre les choses au bon endroit, afin que nous puissions passer plus de temps à discuter et moins à nettoyer. Ainsi : + Faites l'effort de mettre les choses au bon endroit, afin que nous puissions passer plus de temps à discuter et moins à ranger. Ainsi : - * Ne commencer pas un sujet dans la mauvaise catégorie. - * Ne posez pas la même chose dans plusieurs discussions - * Ne postez pas de contenu sans réponse. + * Ne commencez pas un sujet dans la mauvaise catégorie. + * Ne postez pas le même contenu dans plusieurs discussions. + * Ne postez pas de message sans contenu. * Ne pas détourner un sujet en le changeant en cours de route. - * Ne signez vos messages - chaque poste dispose d'informations sur le profil qui s'y rattachent. + * Ne signez pas vos messages — chaque post dispose d'informations sur le profil qui s'y rattachent. - Plutôt que de poster "+1" ou "d'accord", utilisez le bouton 'J'aime'. Plutôt que de prendre une discussion existante dans une direction radicalement différente, utilisez Répondre en créant un nouveau sujet. + Plutôt que de poster "+1", "lol", "mdr" ou "d'accord", utilisez le bouton 'J'aime'. Plutôt que de faire prendre à une discussion existante une direction radicalement différente, utilisez "Répondre en créant un sujet lié". - ## [Poster seulement vos propres informations](#stealing) + ## [Postez seulement vos propres trucs](#stealing) Vous ne pouvez pas poster n'importe quel contenu numérique s'il appartient à quelqu'un d'autre sans son autorisation. Vous ne pouvez pas poster des descriptions, des liens ou des méthodes pour voler la propriété intellectuelle de quelqu'un (logiciels, vidéo, audio, images), ou pour transgresser toute autre loi. + + + ## [Powered by You](#power) + + Ce site est géré par votre [équipe de responsables](/about) ainsi que *vous*, la communauté. Si vous avez d'autres questions sur comment les choses devraient fonctionner ici, créez un nouveau sujet dans [la catégorie meta](/c/meta) et discutons-en! S'il y un problème critique ou urgent qui ne peut pas être résolu par un sujet meta ou un signalement, contactez nous [ici](/about). + - ## [Conditions générales d'utilisation](#tos) + ## [Lisez les conditions générales d'utilisation](#tos) + + Oui, le jargon juridique est ennuyeux, mais nous devons nous protéger – et par extension, vous et vos données – contre des gens hostiles. Nous avons des [Conditions générales d'utilisation](/tos) décrivant votre (et notre) comportement, et les droits liés au contenu, la confidentialité et les lois. Pour utiliser ce service, vous devez accepter de respecter nos [CGU](/tos). - Oui, le jargon juridique est ennuyeux, mais nous devons nous protéger - et par extension, vous et vos données - contre des gens hostiles. Nous avons des [Conditions générales d'utilisation](/tos) décrivant votre (et notre) comportement et les droits liés au contenu, la confidentialité et les lois. Pour utiliser ce service, vous devez accepter de respecter nos [CGU](/tos). tos_topic: title: "Conditions générales d'utilisation" body: | @@ -1769,25 +1869,3 @@ fr: \ ?](#cookies)\n\nOui. Les cookies sont de petits fichiers qu'un site ou son prestataire de services transfert sur le disque dur de votre ordinateur via votre navigateur Web (si vous permettez). Ces cookies permettent au site de reconnaître votre navigateur et, si vous avez un compte enregistré, l'associer à votre compte enregistré.\n\nNous utilisons des cookies pour comprendre et sauvegarder vos préférences pour de futures visites et compiler des données globales sur le trafic du site et les interactions avec le site afin que nous puissions offrir de meilleures expériences de navigation et de meilleurs outils à l'avenir. Nous pouvons conclure des contrats avec des fournisseurs de services tiers pour nous aider à mieux comprendre les visiteurs du site. Ces fournisseurs de services ne sont pas autorisés à utiliser les renseignements recueillis en notre nom, sauf pour nous aider à mener et à améliorer notre entreprise.\n\n\n\n## [Divulguons-nous des informations à des tiers?](#disclose)\n\nNous ne vendons pas, ni échangeons, ou même transférerons vos renseignements personnelles à des tiers. Ceci n'inclut pas les tiers de confiance qui nous aident à exploiter notre site, la conduite de nos affaires, ou vous servir, tant que ces parties conviennent à garder ces informations\ \ confidentiels. Nous pouvons également divulguer vos informations lorsque nous croyons nécessaire de se conformer à la loi, appliquer nos politiques de site, ou la nôtre ou d'autres droits, la propriété ou la sécurité. Toutefois, des informations non personnelles des visiteurs peuvent être fournis à d'autres parties pour le marketing, la publicité, ou d'autres utilisations.\n\n\n\n## [Liens vers des tiers](#third-party)\n\nParfois, à notre discrétion, nous pouvons inclure ou offrir des produits ou services de tiers sur notre site. Ces sites tiers ont des politiques de confidentialité distinctes et indépendantes. Nous n'avons donc aucune responsabilité pour le contenu et les activités de ces sites. Néanmoins, nous cherchons à protéger l'intégrité de notre site et sommes disposés à accueillir tous les commentaires au sujet de ces sites. \n\n\n\n## [Protection des mineurs](#coppa)\n\nNotre site, produits et services sont dirigés vers les personnes qui ont au moins 13 ans ou plus. Si ce serveur est aux Etats-Unis, et que vous êtes sous l'âge de 13 ans, conformément aux exigences de la COPPA ([Children's Online Privacy Protection Act](http://fr.wikipedia.org/wiki/Children%27s_Online_Privacy_Protection_Act)), n'utiliser pas ce site. ## [Politique de confidentialité en ligne](#online) Cette politique de confidentialité en ligne s'applique uniquement aux informations collectées sur notre site et non aux informations recueillies hors connexion. ## [Votre consentement](#consent) En utilisant notre site, vous consentez à la Politique de confidentialité de notre site web.\n\n\n\n## [Changements à notre politique de confidentialité](#changes)\n\nSi nous décidons de changer notre politique de confidentialité, nous afficherons ces modifications sur cette page.\n\nCe document est soumis à la licence creative commons CC-BY-SA.\n" - static: - search_help: | -

Astuces

-

-

    -
  • Les résultats dont le titre correspond à la recherche sont retournés en priorité. Donc, en cas de doutes, recherchez dans les titres
  • -
  • Pour obtenir de meilleurs résultats, utilisez toujours des mots rares ou peu fréquents
  • -
  • Dès que possible, limitez vos recherches à une catégorie en particulier, à un utilisateur ou à un sujet
  • -
-

-

Paramètres

-

- - - - - -
order:viewsorder:latest
status:openstatus:closedstatus:archivedstatus:norepliesstatus:singleuser
category:foouser:foo
in:likesin:postedin:watchingin:tracking
-

-

- rainbows category:parks status:open order:latest recherchera les sujets qui contiennent le mot "rainbows", qui sont dans la catégorie "parks" et qui ne sont ni fermés ni archivé. Les résultats seront triés par la date du dernier message publié. -

diff --git a/config/locales/server.he.yml b/config/locales/server.he.yml index 40cc520015..f9fb8deca4 100644 --- a/config/locales/server.he.yml +++ b/config/locales/server.he.yml @@ -6,6 +6,9 @@ # https://www.transifex.com/projects/p/discourse-org/ he: + stringex: + characters: + number: "-" i18n: transliterate: rule: @@ -17,11 +20,6 @@ he: short_date_no_year: "D MMM" short_date: "D MMM, YYYY" long_date: "MMMM D, YYYY h:mma" - time: - formats: - short: "%m-%d-%Y" - short_no_year: "%B %-d" - date_only: "%b %-d, %Y" title: "Discourse" topics: "נושאים" posts: "הודעות" @@ -30,13 +28,13 @@ he: log_in: "התחברות" via: "%{username} עם %{site_name}" is_reserved: "שמור" - purge_reason: "נמחק באופן אוטומטי מכיוון שהיה ישן וללא שינוי" + purge_reason: "נמחק באופן אוטומטי כחשבון נטוש ולא פעיל" disable_remote_images_download_reason: "הורדת תמונות מרחוק נחסמה בשל היעדר מספיק שטח אכסון פנוי." errors: + format: '%{attribute} %{message}' messages: too_long_validation: "מוגבל לאורך של %{max} תוים: את/ה הקשת %{length}." invalid_boolean: "בוליאני לא חוקי." - taken: "כבר נשלח" embed: load_from_remote: "ארעה שגיאה בטעינת ההודעה הזו." bulk_invite: @@ -184,8 +182,8 @@ he: base: warning_requires_pm: "את/ה יכולים להוסיף אזהרות רק למסרים פרטיים." too_many_users: "את/ה יכולים לשלוח אזהרות רק למשתמש/ת אחד בכל פעם." - archetype: - cant_send_pm: "סליחה, אינך יכול לשלוח הודעה פרטית למשתמש הזה." + cant_send_pm: "מצטערים, אינכם יכולים לשלוח הודעה פרטית למשתמש זה." + no_user_selected: "עליכם לבחור משתמש/ת חוקיים." user: attributes: password: @@ -355,12 +353,14 @@ he: activation: action: "הפעל/י את חשבונך" already_done: "סליחה, כתובת אישור החשבון הזו אינה זמינה יותר. אולי החשבון שלך כבר פעיל?" + please_continue: "חשבונך החדש אושר; הנכם מועברים לעמוד הבית." continue_button: "המשך ל-%{site_name}" welcome_to: "ברוכים הבאים ל-%{site_name}!" approval_required: "מנהל צריך לאשר את החשבון שלך ידנית לפני שתוכל להיכנס לפורום הזה. יישלח אליך דואר אלקטרוני כשהחשבון שלך יאושר!" post_action_types: off_topic: title: 'אוף-טופיק' + description: 'פרסום זה אינו רלוונטי לדיון הנוכחי כפי שהוגדר בכותרת ובפרסום הראשון, ולכן כנראה שעליו לעבור למקום אחר.' long_form: 'דוגלל כאוף-טופיק' spam: title: 'ספאם' @@ -371,10 +371,15 @@ he: description: 'פרסום זה מכיל תוכן שאדם סביר היה רואה כפוגעני, מתעלל או הפרה של כללי הקהילה .' long_form: 'דוגלל כלא ראוי' notify_user: + title: 'מסר פרטי @{{username}}' description: 'פרסום זה מכיל משהו שארצה לשוחח עליו עם האדם הזה באופן ישיר ופרטי. אין צורך בסימון.' + long_form: 'הועבר מסר פרטי למשתמש/ת' email_title: 'הפרסום שלךב"%{title}"' email_body: "%{link}\n\n%{message}" notify_moderators: + title: "משהו אחר" + description: 'פרסום זה דורש את תשומת ליבו של מנחה מסיבה שאינה מצויינת למעלה.' + long_form: 'זה סומן לתשומת הלב של מנחה' email_title: 'הודעה ב-"%{title}" דורשת תשומת לב של מנהל' email_body: "%{link}\n\n%{message}" bookmark: @@ -399,7 +404,9 @@ he: description: 'נושא זה מכיל תוכן שהאדם הסביר היה מחשיב פוגעני, מתעלל או הפרה של כללי ההתנהלות בקהילה שלנו .' long_form: 'דוגלל כלא ראוי' notify_moderators: + title: "משהו אחר" description: 'נושא זה דורש התייחסות של מנחה (moderator) כללי, על פי הכללים המנחים , TOS, או סיבה אחרת שאינה מופיעה כאן.' + long_form: 'זה סומן לתשומת הלב של מנחה' email_title: 'הנושא "%{title}" דורש תשומת לב של מנהל' email_body: "%{link}\n\n%{message}" flagging: @@ -518,15 +525,7 @@ he: s3_backup_config_warning: 'The server is configured to upload backups to s3, but at least one the following setting is not set: s3_access_key_id, s3_secret_access_key or s3_backup_bucket. Go to the Site Settings and update the settings. See "How to set up image uploads to S3?" to learn more.' image_magick_warning: 'The server is configured to create thumbnails of large images, but ImageMagick is not installed. Install ImageMagick using your favorite package manager or download the latest release.' failing_emails_warning: 'There are %{num_failed_jobs} email jobs that failed. Check your config/discourse.conf file and ensure that the mail server settings are correct. See the failed jobs in Sidekiq.' - default_logo_warning: "You haven't customized the logo images for your site. Update logo_url, logo_small_url, and favicon_url in the Site Settings." - contact_email_missing: "You haven't provided a contact email for your site. Please update contact_email in the Site Settings." - contact_email_invalid: "The site contact email is invalid. Please update contact_email in the Site Settings." - title_nag: "The title Site Setting is still set to the default value. Please update it with your site's title in the Site Settings." - site_description_missing: "The site_description setting is blank. Write a brief description of this forum in the Site Settings." consumer_email_warning: "Your site is configured to use Gmail (or another consumer email service) to send email. Gmail limits how many emails you can send. Consider using an email service provider like mandrill.com to ensure email deliverability." - access_password_removal: "Your site was using the access_password setting, which has been removed. The login_required and must_approve_users settings have been enabled, which should be used instead. You can change them in the Site Settings. Be sure to approve users in the Pending Users list. (This message will go away after 2 days.)" - site_contact_username_warning: "The site_contact_username setting is blank. Please update it in the Site Settings. Set it to the username of an admin user who should be the sender of system messages." - notification_email_warning: "The notification_email setting is blank. Please update it in the Site Settings." content_types: education_new_reply: title: "New User Education: First Replies" @@ -557,6 +556,7 @@ he: description: "HTML that will be added at the top of every page (after the header, before the navigation or the topic title)." bottom: title: "Bottom of the pages" + description: "קוד HTML שיתווסף לפני התגית ." site_settings: censored_words: "מלים שיוחלפו באופן אוטומטי ב- ■■■■" delete_old_hidden_posts: "מחיקת אוטומטית של פרסומים מוסתרים שנותרים מוסתרים במשך יותר מ-30 יום." @@ -574,12 +574,6 @@ he: allow_duplicate_topic_titles: "אשר/י נושאים עם כותרות זהות או משוכפלות." unique_posts_mins: "How many minutes before a user can make a post with the same content again" educate_until_posts: "כאשר המשתמש/ת מתחילים להקיש את (n) ההודעות הראשונות שלהם, הציגו פאנל הנחיה למשתמש באזור חיבור ההודעות." - title: "כותרת קצרה של אתר זה, תשמש בתיוג הכותרת (title tag)." - site_description: "תארו אתר זה בעזרת משפט אחד, ישמש בתגית התיאור (meta description tag)." - contact_email: "כתובת הדוא\"ל של דמות מפתח באתר. הודעות חשובות מטעם discourse.org בנוגע לעדכונים חשובים ישלחו לכתובת זו." - company_full_name: "מיושן. אינו בשימוש יותר ויוסר. השם המלא של החברה שמריצה אתר זה, משמש במסמכים משפטיים, כמו /tos ו-/privacy" - company_short_name: "מיושן. אינו בשימוש יותר ויוסר. השם הקצר של החברה שמריצה אתר זה, משמש במסמכים משפטיים כמו /tos ו-/privacy" - company_domain: "מיושן. אינו בשימוש יותר ויוסר. שם המתחם שבבעלות החברה שמריצה אתר זה, משמש במסמכים משפטיים כמו /tos ו- /privacy" queue_jobs: "DEVELOPER ONLY! WARNING! By default, queue jobs in sidekiq. If disabled, your site will be broken." crawl_images: "אחזור תמונות מכתובת URL רחוקה כדי להכניס את מימדי האורך והרוחב הנכונים." download_remote_images_to_local: "המרת תמונות מרחוק לתמונות מקומיות באמצעות הורדות; למניעת שגיאות של תמונות חסרות" @@ -599,11 +593,8 @@ he: post_excerpt_maxlength: "אורך מקסימלי של פרסום קטע / סיכום." post_onebox_maxlength: "מספר תוים מקסימאלי מותר כאורך פרסום Discourse אחד בקופסא (oneboxed Discourse post)." onebox_domains_whitelist: "רשימת מתחמים (דומיינים) מותרים לאריזה (oneboxing); על דומיינים אלה לתמוך ב-OpenGraph או ב-oEmbed. בדקו אותם ב-http://iframely.com/debug." - logo_url: "סמליל (לוגו) שיופיע בפינה שמאלית העליונה של האתר שלך. לדוגמא: http://example.com/logo.png" digest_logo_url: "הסמליל (לוגו) האלטרנטיבי שיופיע בראש מייל התקציר של האתר שלך. אם תשאירו משבצת זו ריק יופיע שם 'logo_url' במקום. לדוגמא: http://example.com/logo.png" - logo_small_url: "סמליל (לוגו) שיופיע בפינה השמאלית העליונה של האתר שלך, נראה כאשר גוללים למטה. לדוגמא: http://example.com/logo-small.png" favicon_url: "A favicon for your site, see http://en.wikipedia.org/wiki/Favicon" - mobile_logo_url: "המיקום הקבוע של תמונת הסמליל (לוגו) המשתמשת בפינה השמאלית העליונה של האתר שלכם המותאם למכשירים ניידים. אם תשאירו משבצת זו ריקה, 'logo_url' יופיע במקום. לדוגמא: http://example.com/uploads/default/logo.png" apple_touch_icon_url: "Icon used for Apple touch devices. Recommended size is 144px by 144px." notification_email: "הטופס: נעשה שימוש בכתובת הדוא\"ל כאשר שולחים את כל הודעות המערכת הנדרשות. כדי שהודעות הדוא\"ל יגיעו, המתחם (domain) המצויין כאן חייב לכלול SPF, DKIM ורשומות reverse PTR מוגדרים כהלכה." email_custom_headers: "A pipe-delimited list of custom email headers" @@ -642,14 +633,10 @@ he: enable_noscript_support: "Enable standard webcrawler search engine support via the noscript tag" allow_moderators_to_create_categories: "Allow moderators to create new categories" cors_origins: "מקורות שאפשר לבצע להם בקשות קרוס-דומיין (Cross origin requests). כל מקור צריך לכלול את התחילית http:// או https:// . משתנה הסביבה DISCOURCE_ENABLE_CORS חייב להיות true כדי לאפשר CORS." - top_menu: "Determine which items appear in the homepage navigation, and in what order. Example latest|new|unread|starred|categories|top|read|posted" post_menu: "Determine which items appear on the post menu, and in what order. Example like|edit|flag|delete|share|bookmark|reply" post_menu_hidden_items: "פריטי התפריט להסתרה כברירת מחדל בתפריט הפרסום, אלא אם כן נלחץ לחצן ההרחבה." share_links: "החלט אילו פריטים יופיעו בתיבת השיתוף, ובאיזה סדר." track_external_right_clicks: "Track external links that are right clicked (eg: open in new tab) disabled by default because it rewrites URLs" - topics_per_page: "כמה נושאים נטענים כברירת מחדל ברשימת הנושאים, ומתי גלילה למטה טוענת נושאים נוספים" - posts_chunksize: "כמה פרסומים לטעון כברירת מחדל תחת נושא מסוים, וכאשר יש גלילה למטה ייטענו פרסומים נוספים." - site_contact_username: "כל המסרים האישיים האוטומטיים יהיו ממשתמש זה; אם תשאירו ריק, יעשה שימוש בחשבון ברירת המחדל של המערכת." send_welcome_message: "שלחו כל המשתמשים החדשים הודעת \"ברוכים הבאים\" פרטים עם הדרכה ראשונית כיצד להתחיל." suppress_reply_directly_below: "אל תציגו את סך התגובות המצטבר בפרסום כאשר ישנה תגובה ישירה אחת לפרסום זה." suppress_reply_directly_above: "אל תציגו את את אפשרות ההרחבה \"בתגובה ל..\" לפרסום כאשר יש רק תגובה אחת ישירה מעל לפרסום זה." @@ -679,9 +666,8 @@ he: max_username_length: "מספר תווים מקסימלי המותר לשם משתמש/ת. אזהרה: משתמשים קיימים עם שמות הארוכים מערך זה לא יוכלו להתחבר לאתר." min_password_length: "Minimum password length." block_common_passwords: "אל תאפשרו סיסמאות מתוך 10,000 הסיסמאות הנפוצות ביותר." - enable_sso: "אפשרו התחברות יחידה באמצעות אתר חיצוני (הערה: מבטל אפשרות להזמנות)" + enable_sso_provider: "ליישם את פרוטוקול SSO של Discourse בנקודת הסיום /session/sso, זה דורש כיוון של sso_secret." sso_url: "URL of single sign on endpoint" - sso_secret: "Secret string used to encrypt/decrypt SSO information, be sure it is 10 chars or longer" sso_overrides_email: "Overrides local email with external site email from SSO payload (WARNING: discrepancies can occur due to normalization of local emails)" sso_overrides_username: "Overrides local username with external site username from SSO payload (WARNING: discrepancies can occur due to differences in username length/requirements)" sso_overrides_name: "Overrides local name with external site name from SSO payload (WARNING: discrepancies can occur due to normalization of local names)" @@ -718,7 +704,6 @@ he: max_flags_per_day: "מספר מקסימלי של סימונים.מותרים למשתמש/ת ביום." max_bookmarks_per_day: "מספר מקסימלי של סימניות למשתמש/ת ביום." max_edits_per_day: "מספר עריכות מקסימלי מותר למשתמש ליום." - max_stars_per_day: "מספר נושאים מקסימלי שיותר למשתמש/ת לסמן (בכוכב) ביום." max_topics_per_day: "מספר מקסימלי של נושאים שמשתמש/ת יכולים ליצור ביום." max_private_messages_per_day: "מספר מקסימלי של מסרים פרטיים שמשתמש/ת יכולים ליצור ביום." suggested_topics: "מספר הנושאים המוצעים שיופיעו בתחתית הנושא המוצג." @@ -726,9 +711,10 @@ he: clean_up_uploads: "הסירו העלאות יתומות וללא הפניה כדי למנוע אירוח בלתי חוקי של חומר. אזהרה: אתם עלולי לרצות לגבות את תיקיית ה-/uploads שלכם לפנו שתאפשרו הגדרה זו." clean_orphan_uploads_grace_period_hours: "Grace period (in hours) before an orphan upload is removed." purge_deleted_uploads_grace_period_days: "Grace period (in days) before a deleted upload is erased." - purge_inactive_users_grace_period_days: "תקופת המתנה (בימים) לפני שמשתמש שלא הפעיל את החשבון שלו יימחק." + purge_unactivated_users_grace_period_days: "תקופת חסד (בימים) לפני שמשתמש/ת שלא הפעילו את חשבונם ימחקו." enable_s3_uploads: "אחסן העלאות (uploads) על תשתית של Amazon S3. חשוב: מצריך מפתח גישה + מפתח גישה סודי שיהיו חוקיים." s3_use_iam_profile: 'השתמש ב-AWS EC2 IAM role על מנת לאחזר מפתחות. שימו לב: איפשור של זה ידרוס את ההגדרות "S3 access key id" וכן את "s3 secret access key".' + s3_upload_bucket: "השם של Amazon S3 bucket אליו יועלו קבצים. אזהרה: שם זה חייב להיות באותיות קטנות, ללא נקודות וללא קווים תחתונים." s3_access_key_id: "מפתח הגישה (access key id) של Amazon S3 שישמש להעלאת התמונות." s3_secret_access_key: "מפתח הגישה הסודי (secret access key) של Amazon S3 שישמש להעלאת תמונות." s3_region: "שם האזור (region name) של Amazon S3 שישמש להעלאת תמונות." @@ -799,12 +785,17 @@ he: white_listed_spam_host_domains: "רשימה של מתחמים (domain) שיוחרגו מבדיקת הספאם. משתמשים חדשים לעולם לא יוגבלו ביצירת פרסומים חדשים עם קישורים למתחמים אלו." staff_like_weight: "כמה משקל עודף יש להעניק ללייקים של הצוות." levenshtein_distance_spammer_emails: "כאשר מתאימים דוא\"ל של ספאמרים, מספר ההבדלים בתווים שעדיין מאפשרים התאמה מטושטשת." + max_new_accounts_per_registration_ip: "אם ישנם כבר (n) חשבונות עם רמת אמון 0 מכתובת IP זו (ואף אחד מהם אינו חבר צוות, או בעל/ת רמת אמון 2 ומעלה), הפסיקו קצת הרשמות מכתובת IP זו." + min_ban_entries_for_roll_up: "בעת לחיצה על לחצן הגלילה למעלה, ייוצר איסור כניסת משנה (subnet ben entry) חדשה אם יש לפחות (N) ערכים." + max_age_unmatched_emails: "מחק ערכי דוא\"ל לא תואמים שמוצגים לאחר (N) ימים." + max_age_unmatched_ips: "מחק ערכי IP לא תואמים שמוצגים לאחר (N) ימים." + num_flaggers_to_close_topic: "מספר מינימלי של מסמנים שונים שנדרש כדי להשהות באופן אוטומטי אפשות להתערב בנושא" + num_flags_to_close_topic: "מספר מינימלי של סימונים פעילים שנדרש כדי להשהות באופן אוטומטי את היכולת להתערב בנושא" reply_by_email_enabled: "אפשרו תגובה לנושאים באמצעות הדוא\"ל." reply_by_email_address: "Template for reply by email incoming email address, for example: %{reply_key}@reply.example.com or replies+%{reply_key}@example.com" disable_emails: "מנעו מ-Discourse ממשלו דוא\"ל כלשהו." strip_images_from_short_emails: "הסרת תמונות מדוא\"ל בגודל של פחות מ-2800 בייטים" short_email_length: "אורכו של דוא\"ל קצר בבייטים (Bytes)" - enable_email_names: "אפשרו הצגת שם מלא של משתמש בדוא\"ל. נטרלו אפשרות זו כדי להסתיר את השם המלא בדוא\"ל." pop3_polling_enabled: "משיכה דרך POP3 לתגובות דוא\"ל." pop3_polling_ssl: "שימוש ב-SSL בעת חיבור לשרת POP3. (מומלץ)" pop3_polling_period_mins: "משך הזמן בדקות בין בדיקת הדוא\"ל בחשבון ה-POP3. הערה: דורש אתחול." @@ -825,6 +816,7 @@ he: logout_redirect: "מיקום להכוונת הדפדפן לאחר ההתנתקות לדוגמא: (http://somesite.com/logout)" allow_uploaded_avatars: "אפשרו למשתמשים להעלות אווטרים מותאמים אישית." allow_animated_avatars: "אפשרו למשתמשים להשתמש באווטרים מסוג GIF אנימציה. אזהרה: הריצו את האווטרים: רעננו לאחר שינוי הגדרה זו." + allow_animated_thumbnails: "יצירת תמונות אנימציה מוקטנות קטנות של קבצי אנימציית gif." automatically_download_gravatars: "הורדת גראווטרים למשתמשים בעת יצירת החשבון או שינוי כתובת הדוא\"ל." digest_topics: "מספר הנושאים המקסימלי להצגה במייל סיכום." digest_min_excerpt_length: "מספר התווים המינימלי למובאות מתוך הפרסום במייל הסיכום." @@ -840,7 +832,6 @@ he: suppress_uncategorized_badge: "אל תציגו את התג (badge) לנושאים נטולי קטגוריה ברשימת הנושאים." global_notice: "הציגו הודעת אזהרה דחופה כללית לכל המבקרים, החליפו לריק כדי להסתיר אותה (ניתן להשתמש ב-HTML)." disable_edit_notifications: "ביטול התראות עריכה על ידי משתמש המערכת כאשר 'download_remote_images_to_local' פעיל." - enable_names: "אפשרו הצגת שמות מלאים של משתמשים. בטלו אפשרות זו כדי להסתיר את השמות המלאים." display_name_on_posts: "הצגת שמם המלא של משתמשים בפרסומיהם, בנוסף ל@שם_המשתמש שלהם." invites_per_page: "ברירת המחדל, הזמנות המוצגות בעמוד המשתמש." short_progress_text_threshold: "After the number of posts in a topic goes above this number, the progress bar will only show the current post number. If you change the progress bar's width, you may need to change this value." @@ -862,6 +853,8 @@ he: show_create_topics_notice: "אם לאתר פחות מ-5 נושאים פומביים, הציגו הודעה המבקשת מן המנהלים/מנהלות ליצור עוד נושאים." vacuum_db_days: "הריצו VACUUM FULL ANALYZE כדי להשיב שטח בסיס הנתונים אחרי העברות (כוונו ל-0 כדי לנטרל)" prevent_anons_from_downloading_files: "מונע ממשתמשים אנונימיים להוריד צרופות (attachments). אזהרה: דבר זה ימנע מכל משאב שאינו תמונה ופורסם כצרופה לעבוד." + enable_emoji: "אפשרו emoji" + emoji_set: "איך אתם אוהבים את ה-emoji שלכם?" errors: invalid_email: "כתובת דוא\"ל לא קיימת." invalid_username: "אין משתמש/ת עם שם משתמש כזה." @@ -893,6 +886,11 @@ he: category: 'קטגוריות' topic: 'תוצאות' user: 'משתמשים' + sso: + not_found: "לא ניתן לאתר או ליצור חשבון, צרו קשר עם אדמין." + account_not_approved: "החשבון ממתין לאישור, תקבל/י הודעת דוא\"ל ברגע שיאושר." + unknown_error: "שגיאה בעדכון המידע, צרו קשר עם האדמין" + timeout_expired: "תם זמן החיבור לחשבון, אנא נסו להתחבר שוב" original_poster: "מפרסם מקורים" most_posts: "מירב ההודעות" most_recent_poster: "המפרסם האחרון" @@ -909,6 +907,10 @@ he: other: "העברתי %{count} הודעות לנושא קיים: %{topic_link}" change_owner: post_revision_text: "בעלות הועברה מהמשתמש %{old_user} אל %{new_user}" + emoji: + errors: + name_already_exists: "מצטערים, השם '%{name}' כבר תפוס על ידי emoji אחר." + error_while_storing_emoji: "מצטער, היתה שגיאה בזמן שמירת ה-emoji." topic_statuses: archived_enabled: "הנושא הזה נכנס לארכיון. הוא קפוא ולא ניתן לשינוי בכל דרך." archived_disabled: "הנושא הזה הוצא מהארכיון. הוא כבר לא קפוא, וניתן לשינוי." @@ -927,8 +929,8 @@ he: one: "נושא זה ננעל אוטומטית לאחר יום אחד מהתגובה האחרונה. תגובות חדשות לא מתקבלות." other: "נושא זה ננעל אוטומטית לאחר %{count} ימים מהתגובה האחרונה. תגובות חדשות לא מתקבלות." autoclosed_enabled_lastpost_hours: - one: "נושא זה ננעל אוטומטית לאחר שעה מהתגובה האחרונה. תגובות חדשות לא מתקבלות." - other: "נושא זה ננעל אוטומטית לאחר %{count} שעות מהתגובה האחרונה. תגובות חדשות לא מתקבלות." + one: "נושא זה נסגר שעה לאחר התגובה האחרונה. תגובות נוספות אינן מותרות יותר." + other: "נושא זה נסגר אוטומטית %{count} שעות לאחר התגובה האחרונה. תגובות נוספות אינן מותרות יותר." autoclosed_enabled_lastpost_minutes: one: "נושא זה ננעל אוטומטית לאחר דקה מהתגובה האחרונה. תגובות חדשות לא מתקבלות." other: "נושא זה ננעל אוטומטית לאחר %{count} דקות מהתגובה האחרונה. תגובות חדשות לא מתקבלות." @@ -953,7 +955,6 @@ he: errors: "%{errors}" not_available: "לא זמין. נסה %{suggestion}?" something_already_taken: "משהו השתבש, אולי שם המשתמש או כתובת הדאור האלקטרוני כבר בשימוש. נסה את קישור שכחתי סיסמה." - omniauth_error: "סליחה, הייתה שגיאה באישור חשבון ה-%{strategy} שלך. אולי לא אישרת לנו לחבר אותך דרכו?" omniauth_error_unknown: "משהו השתבש במהלך עיבוד ההתחברות שלך, אנא נסה שנית." new_registrations_disabled: "הוספת חשבונות חדשים אינה מותרת בעת זו." password_too_long: "סיסמאות מוגבלות ל-200 תווים." @@ -1031,39 +1032,61 @@ he: - ביקור ב[meta.discourse.org](http://meta.discourse.org) לחדשות, דיונים ותמיכה ב-Discourse. new_version_mailer_with_notes: subject_template: "[%{site_name}] update available" + text_body_template: | + גרסה חדש של [Discourse](http://www.discourse.org) זמינה. + + הגרסה שלכם: %{installed_version} + הגרסה החדשה: **%{new_version}** + + ייתכן ותרצו: + + - לראות מה חדש ב- [GitHub changelog](https://github.com/discourse/discourse/commits/master). + + - לעדכן באמצעות ביקור ב- [%{base_url}/admin/upgrade](%{base_url}/admin/upgrade), ולחיצה על כפתור ה-"Upgrade". + + - ביקור ב-[meta.discourse.org](http://meta.discourse.org) לחדשות, דיונים ותמיכה ב-Discourse. + + ### פרטי הגרסה המשוחררת + + %{notes} flags_reminder: flags_were_submitted: one: "סימונים אלו נשלחו לפני כשעה." other: "סימונים אלה נשלחו לפני %{count} שעות." please_review: "Please review them." post_number: "post" + how_to_disable: 'תוכל/י לבטל או לשנות את תדירות תזכורת דוא"ל באמצעות ההגדרה "הודעה על סימונים אחרי...".' subject_template: one: "סימון אחד ממתין לטיפול" other: "%{count} סימונים ממתינים לטיפול" - flag_reasons: - off_topic: "הפרסום שלך סומן כ**מחוץ לנושא**: הקהילה מאמינה שהוא אינו תואם לנושא, כפי שהוא מוגדר כרגע בכותרת ובפרסום הראשון שבו." - inappropriate: "הפרסום שלך סומן כ**בלתי ראוי**: הקהילה חושבת שהוא מעליב, פוגעני או מפר את [הכללים המנחים](/guidelines)." - spam: "הפרסום שלך סומן כ**ספאם**: הקהילה חושבת שהוא פרסום, לא שימושי או לא רלוונטי לנושא, אבל מקדם מכירות בטבעו." - notify_moderators: "הפרסום שלך סומן **לתשומת ליבם של מנחים**: הקהילה חושבת שמשהו בפרסום הזה דורש התייחסות של מנחה." flags_dispositions: agreed: "תודה שעדכנת אותנו. אנחנו מסכימים שיש כאן בעיה ואנחנו מנסים לבדוק את העניין." agreed_and_deleted: "תודה שעדכנת אותנו. אנחנו מסכימים שישנה בעיה והסרנו את הפרסום." disagreed: "תודה שעדכנת אותנו. אנחנו בודקים את זה." deferred: "תודה שעדכנתםאותנו. אנחנו בודקים את העניין." deferred_and_deleted: "תודה שעדכנתם אותנו. הסרנו את הפרסום." + temporarily_closed_due_to_flags: "נושא זה סגור באופן זמני בעקבות מספר גדול של סימונים מצד הקהילה" system_messages: post_hidden: subject_template: "הודעה הוסתרה בעקבות סימון" + text_body_template: | + שלום, + + זוהי הודעה אוטומטית מ%{site_name} שנועדה לידע אותך שפרסום שלך הוסתר. + + {base_url}%{url} + + %{flag_reason} + + כמה חברים בקהילה סימנו את הפרסום הזה לפני שהוא הוסתר, לכן אנא שקלו כיצד תוכלו לשנות את הפרסום בצורה שתתמודד עם המשוב שלהם. + ** תוכלו לערוך את הפרסום המדובר לאחר %{edit_delay} דקות, והוא יוצג מחדש באופן אוטומטי.** + פעולה זו תעלה את רמות האמון שלך במערכת. + + בכל אופן, אם פרסום זה יוסתר על ידי הקהילה בפעם השניה, הוא ישאר במצב זה עד שיגיע לטיפול חבר/ת צוות - ופעולות נוספות עשויות להינקט, כולל אפשרות תיאורטית להשהיית חשבון המשתמש שלך בפורום. + + להדרכה נוספת אנא פנו למסמך ה[קווים המנחים של הקהילה שלנו](%{base_url}/guidelines). welcome_user: subject_template: "ברוכים הבאים ל %{site_name}!" - text_body_template: | - תודה שהצטרפת ל%{site_name}, וברוכ/ה הבא/ה! - - %{new_user_tips} - - אנו מאמינים ב[התנהגות קהילתית מתורבתת](%{base_url}/guidelines) בכל עת. - - תהנו מהביקור! welcome_invite: subject_template: "Welcome to %{site_name}!" backup_succeeded: @@ -1106,15 +1129,10 @@ he: %{logs} ``` csv_export_succeeded: - subject_template: "יצוא המידע הושלם בהצלחה" - text_body_template: | - ייצוא המידע הסתיים בהצלחה. - הורדה של קובץ ה-CSV: %{file_name} - - הקישור להודת קובץ ה-CSV יפוג לאחר 48 שעות + subject_template: "יצוא הנתונים הושלם" csv_export_failed: - subject_template: "הייצוא נכשל" - text_body_template: "תהליך הייצוא נכשל. בדוק את הלוגים." + subject_template: "ייצוא הנתונים נכשל" + text_body_template: "צר לנו, אך ייצוא הנתונים שלכם נכשל. אנא בדקו את רישומי המערכת או צרו קשר עם איש/אשת צוות." email_reject_trust_level: subject_template: "בעיית דוא\"ל - היעדר רמת אמון מספקת" text_body_template: | @@ -1306,13 +1324,17 @@ he: --- %{respond_instructions} digest: + why: "סיכום קצר של %{site_link} מאז ביקורך האחרון ב-%{last_seen_at}" subject_template: "[%{site_name}] Forum Activity for %{date}" new_activity: "New activity on your topics and posts:" top_topics: "Recent posts the community enjoyed:" other_new_topics: "Other New Topics:" + unsubscribe: "תקציר זה נשלח אליך מ-%{site_link} מכיוון שלא ראינו אותך מזה זמן מה. קישור לביטול ההרשמה %{unsubscribe_link}." click_here: "click here" from: "%{site_name} digest" read_more: "Read More" + more_topics: "ישנם %{new_topics_since_seen} נושאים חדשים נוספים." + more_topics_category: "עוד נושאים חדשים:" posts: one: "1 post" other: "%{count} posts" @@ -1349,20 +1371,6 @@ he: %{base_url}/users/authorize-email/%{email_token} signup_after_approval: subject_template: "You've been approved on %{site_name}!" - text_body_template: | - ברוכים הבאים ל%{site_name}! - - מישהו מהצוות שלנו אישר את החשבון שלך ב%{site_name}. - - הקישו על הקישור הזה כדי להפעיל את החשבון החדש: %{base_url}/users/activate-account/%{email_token} - - אם הקישור אינו בר-הקלקה, נסו להעתיק ולהדביק אותו אל שורת הכתובות בדפדפן שלכם. - - %{new_user_tips} - - אנו מאמינים ב[התנהגות קהילתית מתורבתת](%{base_url}/guidelines) בכל עת. - - מקווים שתהנו מהביקור! signup: subject_template: "[%{site_name}] Activate your new account" text_body_template: | @@ -1392,10 +1400,7 @@ he: unauthorized: "Sorry, the file you are trying to upload is not authorized (authorized extensions: %{authorized_extensions})." pasted_image_filename: "Pasted image" store_failure: "Failed to store upload #%{upload_id} for user #%{user_id}." - attachments: - too_large: "Sorry, the file you are trying to upload is too big (maximum size is %{max_size_kb}%kb)." images: - too_large: "Sorry, the image you are trying to upload is too big (maximum size is %{max_size_kb}%kb), please resize it and try again." fetch_failure: "Sorry, there has been an error while fetching the image." unknown_image_type: "Sorry, but the file you tried to upload doesn't appear to be an image." size_not_found: "Sorry, but we couldn't determine the size of the image. Maybe your image is corrupted?" @@ -1421,14 +1426,13 @@ he: guidelines: "קווים מנחים" privacy: "פרטיות" edit_this_page: "עריכת עמוד זה" + csv_export: + boolean_yes: "כן" + boolean_no: "לא" static_topic_first_reply: | ערכו את הפרסום הראשון בנושא זהכדי לשנות את התוכנים של העמוד %{page_name}. guidelines_topic: title: "שאלות נפוצות / כללים מנחים" - body: "\n\n## [זהו מקום מתורבת לדיון ציבורי](#civilized)\n\nאנא התייחסו לפורום הדיון ביחס של כבוד, בדומה לאופן בו הייתם נוהגים בפארק ציבורי. גם אנחנו משאב קהילתי משותף — מקום כדי לחלוק יכולות, ידע ותחומי עניין תוך כדי שיחה מתפתחת.\n\nאלו אינם חוקים קשים ומהירים, אלא כלי עזר שנועדו לסייע להכרעות אנושיות במסגרת הקהילות שלנו. השתמשו בכללים המנחים הללו כדי לשמוש על הזירה הזו כמקום נקי, מואר ומאפשר שיח ציבורי מתורבת.\n\n\n## [שפרו את הדיון](#improve)\n\nסייעו לנו להפוך פורום זה למקום מעולה לדיון באמצעות עבודה מתמדת על שיפור הדיון בדרך כל שהיא, גם אם בעזרת תרומה קטנה או צנועה. אם אינם בטוחים אם הפרסום שלכם תורם לשיחה, חשבו שוב מה בעצם ברצונכם לומר ונסו שוב מאוחר יותר.\n\nהנושאים שנדונים כאן חשובים לנו, ואנו רוצים שתנהגו כלפיהם ככאלה שחשובים גם לכם. לכן, נהגו בכבוד לנושאים ולאנשים אשר דנים בהם, גם אם אינכם מסכימים אם חלק מהדברים שנאמרים.\n\nאחת הדרכים לשפר את הדיון היא באמצעות גילוי נושאים שלגביהם כבר מתנהל דיון. אנא השקיעו מזמנים כדי ללגלוש בין הנושאים כאן לפני שתשיבו או תפתחו נושאים חדשים משלכם, וכך גם תגדילו את הסיכויים לפגוש אחרים להכיר אחרים שחולקים אותם תחומי עניין.\n.\n\n\n\n## [נטו להסכמה, גם אם אינם מסכימים](#agreeable)\n\nאתם עשויים לרצות להגיב באי הסכמה למשהו שתראו כאן. זה בסדר, א-ב-ל זכרו _לבקר את הרעיונות, לא את האנשים_. אנא המנעו מ:\n\n* הדבקת כינויים או שמות.\n* מתקפות לגופו של אדם.\n* תגובה לטון הכתיבה בפרסום כלשהוא, במקום לתוכן הממשי שלו.\n* סתירה אינסטנקטיבית.\n\nבמקום כל אלו הגיבו בטיעוני נגד אשר משפרים את השיחה.\n\ - \n\n\n## [ההשתתפות שלכם נחשבת](#participate)\n\nהשיחה שאנחנו מנהלים כאן משפיעה על כל חברי הקהילה. עזרו לנו להשפיע על עתיד הקהילה באמצעות הצטרפות לדיונים אשר יעשו את הפורום הזה מקום מעניין יותר לשהות בו — והתעלמו מכאלו שלא עושים כך.\n\nמערכת Discourse כוללת כלים אשר מאפשרים לקהילה לזהות באופן קולקטיבי את התרומות הטובות ביותר (והגרועות ביותר): פרסומים מועדפים, סימניות, לייקים, סימונים, תגובות, עריכות וכן הלאה. השתמשו בכל הכלים הללו כדי לשפר את החוויה האישית שלכם, וגם את זו של כולם.\n\nבואו ננסה להשאיר את הפארק טוב יותר מכפי שמצאנו אותו.\n\n\n\n## [אם נתקלתם בבעיה, סמנו אותה](#flag-problems)\n\nלמנחים יש סמכות מיוחדת; הם אחראים לפורום הזה. אבל גם את/ה. בעזרתך מנחים יכולים כמדריכים לדיון, ולא רק כשומרים או שוטרים.\n\nכאשר אתם נתקלים בהתנהגות פסולה, אל תגיבו אליה. זה יעודד את ההתנהגות הרעה על ידי מתן הכרה, יגזול מכם אנרגיה ויבזבז זןמ לכולם. _פשוט סמנו אותה_. אם מספיק יועברו מספיק סימונים, תינקט פעולה, באופן אוטומטי או על ידי התערבות של מנחה. \n\nבכדי לתחזק את הקהילה שלנו, שמורה למנחים הזכות להסיר תוכן או חשבונות משתמש מכל סיבה בכל זמן. מנחים לא מאשרים הודעות לפני פרסום בדרך כלשהיא; המנחים ומפעילי האתר אינו נושאים באחריות כלשהיא לתוכן שפורסם על ידי הקהילה.\n\n\n\n\n## [נהגו כאזרחים]](#be-civil)\nאין משהו שפוגע ביכולת לנהל שיחה טובה כמו גסות רוח:\n\n* נהגו כאזרחים טובים. אל תפרסמו שום דבר שאדם סביר היה מחשיב פוגעני, מעליב או טקסט שנאה.\n* כתבו נקי. אל תפרסמו שום דבר מגונה או מיני מפורש.\n* כבדו זה את זו. המנעו מלהטריד\ - \ או לגרום צער לאחרים, מלהתחזות לאחרים או לחשוף את פרטיהם האישיים.\n* כבדו את הפורום שלנו. אל תפרסמו ספאם או כל פעולה ונדליסטית אחרת לפורום.\n\nאלו אינן כללים ברורים עם הגדרות מדוייקות — המנו אפילו מ_חזות_ של משהו מהדברים הללו. אם אינכם בטוחים, שאלו את עצמכם איך הייתם מרגישים לו אחד מהפרסומים שלכם היה מופיע על העמוד הראשון של הניו-יורק טיימס (או עיתון מכובד אחר, לבחירתכם).\n\nזהו פורום פומבי ומנועי חיפוש מאנדקסים דיונים אלו. הקפידו על השפה, הקישורים והתמונות בטוחים ומותאמים למשפחה וחברים.\n\n\n\n## [שמרו על סדר ונקיון](#keep-tidy)\n\nעשו מאמץ למקום דברים במקום הנכון, כך שנוכל להשקיע יותר זמן בדיון ופחות בסידור וניקיון. לכן\n\n* אל תתחילו נושא בקטגוריה שאינה מתאימה.\n* אל תפרסמו במקביל את אותו הדבר בכמה נושאים שונים.\n* אל תפרסמו תגובות ללא תוכן.\n* אל תפגעו בנושא באצמעות שינויו באמצע הדיון.\n* אל תוסיפו חתימה בהודעות שלכם — כל פרטי הפרופיל שלכם יוצמדו לכל פרסום שלכם.\n\nבמקום לפרסם \"1+\" או \"אני מסכים/מסכימה, השתמשו בכפתור הלייק. במקום לקחת נושא בדיון לכיוון שונה בצורה קיצונית, השתמש ב\"תגובה כנושא חדשה\".\n\n\n\n## [פרסמו רק חומר שלכם](#stealing)\n\nאינם יכולים לפרסם שום תוכן דיגיטלי ששייך למישהו אחר ללא רשותם. אינכם יכולים לפרסם תיאור, קישורים או דרכים לגנוב את הקניין הרוחני של מישהו אחר (ובכלל זה תוכנה, וידאו, שמע, תמונות) או כדי לעבור על כל חוק אחר.\n\n\n\n\n## [תנאי השימוש](#tos)\n\nכן, חוק ומשפט יכולים להיות משעממים, אבל עלינו להגן על עצמנו – ובהמשך לכך גם עליך ועל המידע שלך – מחבר'ה לא נחמדים. יש\ - \ לנו [תנאי שימוש] (/tos) אשר מתארים את ההתנהגות והזכויות שלך ושלנו ביחס לתוכן, פרטיות וחוקים. כדי להשתמש בשירות [אתם חייבים להסכים לציית לכללים אלו(/tos).\n" tos_topic: title: "תנאי השימוש" body: "הכללים והתנאים הבאים חלים על כל שימוש באתר %{company_domain} וכל התוכן, השירותים והמוצרים הזמנים בו או דרכו, כולל, ולא רק, תוכנת הפורום %{company_domain, פורום התמיכה של %{company_domain ושירותי האירוח של %{company_domain (\"אירוח\") (להלן, \"האתר\"). האתר הוא בבעלות ומופעל על ידי %{company_full_name} (\"%{company_name}\").. האתר נתון להסכמתך ללא שינוי של כל הכללים והתנאים הכלולים כאן וכל כלל ומדיניות (כולל, וללא מגבלה, [מדיניות הפרטיות] (/privacy)של %{company_domain} ו[כללי ההתנהגות הקהילתיים] (/faq)ונהלים אשר אשויים להתפרסם מעת לעת באתר זה על ידי %{company_name} (להלן, \"ההסכם\").\n\nאנא קראו הסכם זה בזהירות לפני גישה או שימוש באתר. בגישה או שימוש בכל חלק של האתר, הנכם מסכימים להיות מחוייבים לכללים והתנאים שבהסכם זה. אם אינכם מסכימים לכל הכללים והתנאים של הסכם זה, אז אינכם רשאים לגשת לאתר או להשתמש באיזהשהו שירות. אם הכללים והתנאים האלה נחשבים הצעה מטעם %{company_name}, קבלתה מוגבלת לכללים אלה. האתר זמין רק ליחידים בני 13 ומעלה.\n\n\n\n## [1. חשבון %{company_domain} שלך](#1)\n \nאם את/ה יוצרים חשבון באתר, את/ה אחראים לתחזר את האבטחה של החשבון שלך ואתם נושאים באחריות המלאה לכל הפעילויות שיתבצעו תחת החשבון. אתם חייבים ליידע באופן מיידי את %{company_name} על כל שימוש לא מאושר שנעשה בחשבונך או כל פרצה אחרת. %{company_name} לא יהיו אחראיים לפעולה כל שהיא או למחדל שלך, כולל כל סוג של נזק שיגרם כתוצאה מפעיולות כאלה או מחדלים.\n\n\n\n## [2. אחריות התורמים](#2)\n\nאם אתם מפרסמים חומר לאתר, מפרסמים קישורים באתר, או הופכים (או מאפשר לכל צד שלישי אחר להפוך) חומר לזמין\ @@ -1527,5 +1531,3 @@ he: א0 נחליט לשנות את מדיניות הפרטיות שלנו, נפרסם שינויים אלו בעמוד זה. מסמך זה מפורסם תחת רשיון CC-BY-SA. הוא עודכן לאחרונה ב-31 למאי, 2013. - static: - search_help: "

טיפים

\n

\n

    \n
  • כותרות מוצגות על פי סדר עדיפות, כך שכשיש ספק, חפשו: titles
  • \n
  • מלים ייחודיות ולא מקובלות יציגו תמיד את התוצאות הטובות ביותר
  • \n
  • כאשר זה אפשרי, צמצמו את החיפוש שלכם לקטגוריה, משתמש או נושא
  • \n
\n

\n

אפשרויות

\n

\n \n \n \n \n \n
order:viewsorder:latest
status:openstatus:closedstatus:archivedstatus:norepliesstatus:singleuser
category:foouser:foo
in:likesin:postedin:watchingin:tracking
\n

\n

\n rainbows category:parks status:open order:latest \nיחפש נושאים הכוללים את המילה \"rainbows\" בקטגוריה \"parks\" שאינם סגורים או מאורכבים, על פי סדר תאריך הפרסום האחרון.

\n" diff --git a/config/locales/server.id.yml b/config/locales/server.id.yml index 29f1032cd1..28a819cffb 100644 --- a/config/locales/server.id.yml +++ b/config/locales/server.id.yml @@ -10,9 +10,6 @@ id: short_date_no_year: "D MMM" short_date: "D MMM, YYYY" long_date: "MMMM D, YYYY h:mma" - time: - formats: - short: "%d-%m-%Y" title: "Discourse" topics: "Topik" posts: "post" @@ -73,11 +70,6 @@ id: messages: is_invalid: "tidak valid; cobalah dengan sesuatu yang lebih deskriptif" has_already_been_used: "telah digunakan" - models: - topic: - attributes: - archetype: - cant_send_pm: "Maaf, Anda tidak dapat mengirimkan pesan kepada pengguna ini." user_profile: no_info_me: "
kolom Tentang Saya dari profil Anda masih kosong, apakah Anda mau mengisinya?
" no_info_other: "
%{name} belum mengisi kolom Tentang Saya untuk profilnya
" @@ -148,10 +140,7 @@ id: previous_discussion: "Balasan Sebelumnya" posted_by: "Dipost oleh %{username} pada %{post_date}" upload: - attachments: - too_large: "Maaf, berkas yang Anda unggah terlalu besar (ukuran maksimum adalah %{max_size_kb}%kb)." images: - too_large: "Maaf, gambar yang Anda unggah terlalu besar (ukuran maksimum adalah %{max_size_kb}%kb), sesuaikan ukuran gambar Anda dan cobalah lagi." fetch_failure: "Maaf, terjadi kesalahan saat memuat gambar." unknown_image_type: "Maaf, berkas yang Anda unggah bukan gambar" size_not_found: "Maaf, kami tidak dapat mendeteksi ukuran dari gambar. Apa berkas gambar yang Anda unggah rusak?" diff --git a/config/locales/server.it.yml b/config/locales/server.it.yml index d077e183e4..c98a817785 100644 --- a/config/locales/server.it.yml +++ b/config/locales/server.it.yml @@ -20,11 +20,6 @@ it: short_date_no_year: "D MMM" short_date: "D MMM YYYY" long_date: "D MMMM YYYY h:mma" - time: - formats: - short: "%d-%m-%Y" - short_no_year: "%B %-d" - date_only: "%b %-d, %Y" title: "Discourse" topics: "Argomenti" posts: "messaggi" @@ -33,13 +28,50 @@ it: log_in: "Accedi" via: "%{username} via %{site_name}" is_reserved: "è riservato" - purge_reason: "Cancellato automaticamente poiché vecchio e non verificato" + purge_reason: "Cancellato automaticamente come account abbandonato e mai attivato" disable_remote_images_download_reason: "Lo scaricamento delle immagini remote è stato disabilitato perché non c'è abbastanza spazio disco disponibile." errors: + format: '%{attribute} %{message}' messages: too_long_validation: "è limitato a %{max} caratteri; tu ne hai inseriti %{length}." invalid_boolean: "Booleano non valido." - taken: "già preso" + taken: "è già stato usato" + accepted: deve essere accettato + blank: non può essere vuoto + present: deve essere vuoto + confirmation: "non corrisponde a %{attribute}" + empty: non può essere vuoto + equal_to: deve essere uguale a %{count} + even: deve essere pari + exclusion: è riservato + greater_than: deve essere maggiore di %{count} + greater_than_or_equal_to: deve essere maggiore o uguale a %{count} + inclusion: non è incluso nella lista + invalid: non è valido + less_than: deve essere minore di %{count} + less_than_or_equal_to: deve essere minore o uguale a %{count} + not_a_number: non è un numero + not_an_integer: deve essere un intero + odd: deve essere dispari + record_invalid: 'Validazione fallita: %{errors}' + restrict_dependent_destroy: + one: "Impossibile cancellare il record poiché ne esistono %{record} dipendenti" + many: "Impossibile cancellare il record poiché esiste %{record} dipendente" + too_long: + one: è troppo lungo (il massimo è di 1 carattere) + other: è troppo lungo (il massimo è di %{count} caratteri) + too_short: + one: è troppo corto (il minimo è di 1 carattere) + other: è troppo corto (il minimo è di %{count} caratteri) + wrong_length: + one: ha una lunghezza errata (dovrebbe essere di 1 carattere) + other: ha una lunghezza errata (dovrebbe essere di %{count} caratteri) + other_than: "deve essere diverso da %{count}" + template: + body: 'Sono stati riscontrati problemi con i seguenti campi:' + header: + one: 1 errore ha impedito il salvataggio di questo %{model} + other: '%{count} errori hanno impedito il salvataggio di questo %{model}' embed: load_from_remote: "Si è verificato un errore nel caricamento del messaggio." bulk_invite: @@ -107,6 +139,7 @@ it: latest: "Argomenti più recenti" hot: "Argomenti caldi" too_late_to_edit: "Questo messaggio è stato creato troppo tempo fa. Non può più essere modificato né cancellato." + excerpt_image: "immagine" groups: errors: can_not_modify_automatic: "Non puoi modificare un gruppo automatico" @@ -182,8 +215,8 @@ it: base: warning_requires_pm: "Puoi allegare solo avvertimenti ai messaggi privati." too_many_users: "Puoi inviare avvertimenti ad un solo utente per volta." - archetype: - cant_send_pm: "Spriacenti, non puoi mandare un messaggio privato a quell'utente." + cant_send_pm: "Spiacenti, non puoi inviare un messaggio privato a quell'utente." + no_user_selected: "Seleziona un utente valido." user: attributes: password: @@ -262,17 +295,17 @@ it: one: "1m" other: "%{count}m" about_x_hours: - one: "1h" - other: "%{count}h" + one: "1o" + other: "%{count}o" x_days: - one: "1d" - other: "%{count}d" + one: "1g" + other: "%{count}gg" about_x_months: - one: "1mes" - other: "%{count}mes" + one: "1mese" + other: "%{count}mesi" x_months: - one: "1mes" - other: "%{count}mes" + one: "1mese" + other: "%{count}mesi" about_x_years: one: "1a" other: "%{count}a" @@ -347,6 +380,8 @@ it: title: 'Spam' description: 'Questo messaggio è una pubblicità. Non è né utile né rilevante ai fini di questo argomento, ha bensì natura promozionale.' long_form: 'segnalato come spam' + email_title: '"%{title}" è stato segnalato come spam' + email_body: "%{link}\n\n%{message}" inappropriate: title: 'Inappropriato' description: 'Questo messaggio ha dei contenuti che una persona ragionevole considererebbe offensivi, aggressivi o una violazione delle linee guida della comunità.' @@ -488,6 +523,46 @@ it: title: "Migliori Argomenti Referenziati" xaxis: "Argomento" num_clicks: "Click" + page_view_anon_reqs: + title: "Anonimo" + xaxis: "Giorno" + yaxis: "Pagine Viste da utenti anonimi" + page_view_logged_in_reqs: + title: "Collegato" + xaxis: "Giorno" + yaxis: "Pagine Viste da utenti collegati" + page_view_crawler_reqs: + title: "Web Crawler" + xaxis: "Giorno" + yaxis: "Pagine Viste da Web Crawler" + page_view_total_reqs: + title: "Totali" + xaxis: "Giorno" + yaxis: "Pagine Viste Totali" + http_background_reqs: + title: "Background" + xaxis: "Giorno" + yaxis: "Richieste usate per l'aggiornamento diretto e per il tracking" + http_2xx_reqs: + title: "Status 2xx (OK)" + xaxis: "Giorno" + yaxis: "Richieste andate a buon fine (Status 2xx)" + http_3xx_reqs: + title: "HTTP 3xx (Redirect)" + xaxis: "Giorno" + yaxis: "Richieste ridirette (Status 3xx)" + http_4xx_reqs: + title: "HTTP 4xx (Errore Client)" + xaxis: "Giorno" + yaxis: "Errori Client (Status 4xx)" + http_5xx_reqs: + title: "HTTP 5xx (Errore Server)" + xaxis: "Giorno" + yaxis: "Errori Server (Status 5xx)" + http_total_reqs: + title: "Totali" + xaxis: "Giorno" + yaxis: "Richieste totali" dashboard: rails_env_warning: "Il tuo server è in modalità %{env}." ruby_version_warning: "Stai eseguendo la versione 2.0.0 di Ruby che ha problemi noti. Effettua l'upgrade alla patch livello 247 o seguenti." @@ -502,19 +577,18 @@ it: facebook_config_warning: 'Il server è configurato per accettare registrazioni e login con Facebook (enable_facebook_logins), tuttavia i parametri app id e secret non sono stati impostati. Vai alle Impostazioni e aggiorna i campi interessati. Leggi questa guida per saperne di più.' twitter_config_warning: 'Il server è configurato per accettare registrazioni e login con Twitter (enable_twitter_logins), tuttavia i parametri key e secret non sono stati impostati. Vai alle Impostazioni e aggiorna i campi interessati. Leggi questa guida per saperne di più.' github_config_warning: 'Il server è configurato per accettare registrazioni e login con GitHub (enable_github_logins), tuttavia i parametri client id e secret non sono stati impostati. Vai alle Impostazioni e aggiorna i campi interessati. Leggi questa guida per saperne di più.' - s3_config_warning: 'Il server è configurato per caricare file a s3, ma almeno uno dei seguenti parametri non è impostato: s3_access_key_id, s3_secret_access_key or s3_upload_bucket. Vai alle impostazioni del sito e aggiorna i parametri. Leggi anche "How to set up image uploads to S3?" per saperne di più.' - s3_backup_config_warning: 'Il server è configurato per caricare i backup su S3, ma almeno uno dei seguenti campi non è impostato: s3_access_key_id, s3_secret_access_key or s3_backup_bucket. Vai sulle Impostazioni e aggiorna i relativi campi. Guarda "Come impostare il caricamento di immagini su S3?" per informazioni.' - image_magick_warning: 'Il server è configurato per creare thumbnail di immagini di ampie dimensioni, ma ImageMagick non è installato. Installa ImageMagick usando il tuo Package Manager preferito o scarica la versione più recente.' - failing_emails_warning: 'Ci sono %{num_failed_jobs} job di email falliti. Controlla il tuo file config/environments/production.rb e accertati che le impostazioni config.action_mailer siano corrette. Guarda i job falliti in Sidekiq.' - default_logo_warning: "Non hai personalizzato le immagini dei logo per il tuo sito. Aggiorna logo_url, logo_small_url, e favicon_url nelle Impostazioni Sito." - contact_email_missing: "Non hai fornito una email di contatto per il tuo sito. Per favore aggiorna il campo contact_email nelle Impostazioni Sito." - contact_email_invalid: "Il contatto email non è valido. Per favore aggiorna contact_email nelle Impostazioni Sito." - title_nag: "Il titolo del sito è ancora impostato al valore di default. Per favore aggiornalo con il titolo del tuo sito, dalle Impostazioni Sito." - site_description_missing: "L'impostazione site_description è vuota. Scrivi una breve descrizione del forum nelle Impostazioni del sito." - consumer_email_warning: "Il tuo sito è configurato per usare Gmail (o un altro servizio email consumer) per inviare le email. Gmail limita il numero di email che puoi inviare. Considera l'utilizzo di un provider come mandrill.com per l'invio delle tue email." - access_password_removal: "Il tuo sito usava l'impostazione access_password, che è stata rimossa. Al suo posto, vanno usati login_required e must_approve_users settings. Li puoi gestire nelle Impostazioni del sito. Assicurati di approvare gli utenti nella lista d'attesa. (Questo messaggio scomparirà dopo 2 giorni)" - site_contact_username_warning: "L'impostazione site_contact_username è vuota. Per favore inseriscila nelle Impostazioni del sito. Impostala come username dell'amministratore che figura da mittente dei messaggi di sistema." - notification_email_warning: "L'impostazione di notification_email è vuota. Per favore aggiornala nelle Impostazioni del sito." + s3_config_warning: 'Il server è configurato per caricare file su s3, ma almeno uno dei seguenti parametri non è impostato: s3_access_key_id, s3_secret_access_key, s3_upload_bucket. Vai nelle Impostazioni Sito e aggiorna i parametri. Leggi anche "How to set up image uploads to S3?" per saperne di più.' + s3_backup_config_warning: 'Il server è configurato per caricare i backup su S3, ma almeno una delle seguenti impostazioni non è impostata: s3_access_key_id, s3_secret_access_key, s3_backup_bucket. Vai nelle Impostazioni Sito e aggiorna le impostazioni. Leggi anche "How to set up image uploads to S3?" per saperne di più.' + image_magick_warning: 'Il server è configurato per creare miniature di immagini grandi, ma ImageMagick non è installato. Installa ImageMagick usando il tuo package manager preferito o scarica la versione più recente.' + failing_emails_warning: 'Ci sono %{num_failed_jobs} job di email falliti. Controlla il file config/discourse.conf e accertati che le impostazioni del mail server siano corrette. Guarda i job falliti in Sidekiq.' + default_logo_warning: "Non hai personalizzato il logo del tuo sito. Aggiorna logo_url, logo_small_url e favicon_url nelle Impostazioni Sito." + contact_email_missing: "Inserisci un indirizzo email di contatto per il sito, in modo da essere raggiungibile per questioni urgenti riguardanti il sito. Aggiornalo nelle Impostazioni Sito." + contact_email_invalid: "La email di contatto non è valida. Aggiornala nelle Impostazioni Sito." + title_nag: "Inserisci il nome del tuo sito. Aggiorna il titolo nelle Impostazioni Sito." + site_description_missing: "Scrivi una breve descrizione del sito che comparirà nei risultati di ricerca. Aggiorna site_description nelle Impostazioni Sito." + consumer_email_warning: "Il tuo sito è configurato per usare Gmail (o un altro servizio email consumer) per inviare le email. Gmail limita il numero di email che puoi inviare. Considera l'utilizzo di un fornitore di servizi come mandrill.com per assicurare l'invio delle email." + site_contact_username_warning: "Scrivi il nome di un membro dello staff disponibile, da cui inviare messaggi privati importanti in maniera automatica. Aggiorna site_contact_username nelle Impostazioni Sito." + notification_email_warning: "Le email di notifica non vengono inviate da un indirizzo email valido del tuo dominio; l'invio di email sarà inaffidabile. Definisci notification_email con un indirizzo email valido nelle Impostazioni Sito." content_types: education_new_reply: title: "Educazione Nuovi Utenti: Prime Risposte" @@ -532,7 +606,7 @@ it: title: "Benvenuto: Utente Invitato" description: "Verrà inviato automaticamente un messaggio privato a tutti gli utenti invitati quando accettano l'invito a partecipare da parte di un altro utente." login_required_welcome_message: - title: "Accesso richiesto: messaggio di benvenuto" + title: "Accesso Richiesto: Messaggio di Benvenuto" description: "Messaggio che viene mostrato agli utenti non collegati quando l'impostazione \"login obbligatorio\" è attiva." login_required: title: "Login Richiesto: Homepage" @@ -563,9 +637,9 @@ it: allow_duplicate_topic_titles: "Permetti più argomenti con lo stesso identico titolo" unique_posts_mins: "Quanti minuti prima che un utente possa inviare un altro argomento con lo stesso contenuto" educate_until_posts: "Per i primi (n) messaggi di un utente, mostra la finestra pop-up con il pannello di istruzioni per nuovi utenti." - title: "Breve titolo di questo sito, usato nel tag title." - site_description: "Descrivi questo sito in una sola frase. La descrizione verrà usata nel tag meta description." - contact_email: "Indirizzo email del contatto principale del sito. A questo indirizzo potrebbero essere inviate notifiche importanti da parte di discourse.org riguardo aggiornamenti critici." + title: "Il nome di questo sito, da usare nell'attributo title." + site_description: "Descrivi questo sito in una frase, da usare nell'attributo meta description." + contact_email: "Gli indirizzi email delle persone chiave responsabili di questo sito. Usati per notifiche importanti, come segnalazioni da gestire, come pure nel modulo di contatto /about per questioni urgenti." queue_jobs: "SOLO PER SVILUPPATORI! ATTENZIONE! Per default, accoda i job in sidekiq. Se disabilitato, il tuo sito non funzionerà." crawl_images: "Recupera le immagini dagli URL remoti per inserire le dimensioni corrette di ampiezza e altezza nel tag." download_remote_images_to_local: "Scarica localmente le immagini remote; ciò permettei di evitare immagini assenti." @@ -584,11 +658,16 @@ it: exclude_rel_nofollow_domains: "Una lista di domini separati da pipe | in cui il nofollow non viene aggiunto (tld.com autorizzerà automaticamente sub.tld.com)" post_excerpt_maxlength: "Lunghezza massima dell'estratto / riassunto di un messaggio." post_onebox_maxlength: "Lunghezza massima in caratteri di un messaggio Discourse in Onebox." - logo_url: "Il logo mostrato in alto a sinistra del tuo sito, es: http://example.com/logo.png" + onebox_domains_whitelist: "Lista di domini per i quali consentire la funzione di onebox; questi domini devono supportare OpenGraph o oEmbed. Testali su http://iframely.com/debug" + logo_url: "L'immagine del logo in alto a sinistra sul tuo sito; se non impostato verrà mostrato il testo del titolo del sito." digest_logo_url: "Il logo alternativo usato in cima alla email di riepilogo. Se lasciato vuoto, sarà utilizzato il campo `logo_url`. es: http://example.com/logo.png" + logo_small_url: "La piccola immagine del logo in alto a sinistra sul tuo sito, che appare quando si scorre in giù. Se non impostato, verrà mostrato l'icona di una casa." favicon_url: "La favicon del tuo sito, vedi http://it.wikipedia.org/wiki/Favicon" + mobile_logo_url: "L'immagine del logo a posizione fissa usata in alto a sinistra del sito mobile. Se non impostato, verrà usato il testo del titolo del sito." apple_touch_icon_url: "Icona usata per dispositivi touch Apple. La dimensione consigliata è 144 x 144 pixel." + notification_email: "L'indirizzo presente nel campo from: usato per inviare tutte le email essenziali di sistema. Il dominio indicato deve avere i record SPF, DKIM e reverse PTR impostati correttamente perché l'email arrivi." email_custom_headers: "Una lista di intestazioni email personalizzate delimitata da una barra verticale (pipe |)" + email_subject: "Formato personalizzabile dell'oggetto per le email standard. Vedi https://meta.discourse.org/t/customize-subject-format-for-standard-emails/20801" use_https: "La URL completa del sito (Discourse.base_url) deve essere http o https? NON ATTIVARE SE HTTPS NON E' GIA' CONFIGURATO E FUNZIONANTE!" summary_score_threshold: "Il punteggio minimo richiesto affinché un messaggio sia incluso in 'Riassumi Questo Argomento'" summary_posts_required: "Minimo numero di messaggi in un argomento affinché venga abilitato 'Riassumi Questo Argomento' " @@ -596,89 +675,124 @@ it: summary_percent_filter: "Quando un utente clicca su 'Riassumi Questo Argomento', mostra i primi % messaggi" summary_max_results: "Massimo numero di messaggi mostrati in 'Riassumi Argomento'" enable_private_messages: "Autorizza gli utenti con livello di esperienza 1 a creare e rispondere a messaggi privati" - enable_long_polling: "Message bus used for notification can use long polling" - anon_polling_interval: "How often should anonymous clients poll in milliseconds" + enable_long_polling: "Il message bus per le notifiche può usare il long polling" + long_polling_base_url: "URL di base usato per il long polling (quando una CDN serve contenuto dinamico, bisogna impostarlo come origin pull) es. http://origin.site.com" + long_polling_interval: "Tempo di attesa prima che il server risponda ai client che non ci sono dati da trasmettere (solo per utenti autenticati)" + polling_interval: "Se non si esegue il long polling, quanto spesso i client autenticati devono fare poll in millisecondi" + anon_polling_interval: "Frequenza in millisecondi con cui client anonimi effettuano il poll" + background_polling_interval: "Quanto spesso i client devono fare poll in millisecondi (quando la finestra è in background)" auto_track_topics_after: "Valore globale in millisecondi prima che un argomento venga automaticamente seguito, modificabile dagli utenti (0 sempre, -1 mai)" new_topic_duration_minutes: "Valore globale in minuti per i quali un argomento è considerato come nuovo, modificabile dagli utenti (-1 sempre, -2 ultima visita)" flags_required_to_hide_post: "Numero di segnalazioni che rendono un argomento automaticamente nascosto. L'utente riceve un messaggio privato. (0 mai)" - cooldown_minutes_after_hiding_posts: "Numero di minuti che l'utente deve attendere prima di modificare un post nascosto per segnalazione" + cooldown_minutes_after_hiding_posts: "Quanti minuti l'utente deve attendere prima di poter modificare un messaggio che è stato nascosto a causa di segnalazioni" max_topics_in_first_day: "Numero massimo di argomenti che l'utente può creare nel suo primo giorno di forum" - max_replies_in_first_day: "Numero massimo di risposte che un utente può pubblicare nel suo primo giorno di forum" + max_replies_in_first_day: "Numero massimo di risposte che un utente può pubblicare nel suo primo giorno sul sito" num_flags_to_block_new_user: "Se i messaggi di un nuovo utente ricevono segnalazioni di spam da num_users_to_block_new_user diversi utenti, nascondi tutti i messaggi dell'utente e impediscigli ulteriori invii. 0 per disabilitare questa funzionalità." num_users_to_block_new_user: "Se i messaggi di un nuovo utente ricevono num_flags_to_block_new_user segnalazioni di spam da così tanti utenti diversi, nascondi tutti i messaggi dell'utente e impediscigli ulteriori invii. 0 per disabilitare questa funzionalità." notify_mods_when_user_blocked: "Se un utente è bloccato automaticamente, manda un messaggio ai moderatori." flag_sockpuppets: "Se un nuovo utente risponde ad un argomento dallo stesso indirizzo IP dell'utente che ha aperto l'argomento stesso, segnala entrambi i messaggi come potenziale spam." traditional_markdown_linebreaks: "Usa l'accapo tradizionale in Markdown, cioè due spazi a fine riga per andare a capo." - post_undo_action_window_mins: "Numero di minuti durante i quali gli utenti sono autorizzati ad annullare le loro azioni su un post (segnalazioni, like, etc)" - must_approve_users: "Lo staff deve approvare tutti i nuovi account utente prima che loro possano accedere al sito." - ga_tracking_code: "Codice Google Analytics per il tracking, es: UA-12345678-9; per informazioni http://google.com/analytics" - ga_domain_name: "Nome del Dominio Google analytics, esempio: miosito.com; see http://google.com/analytics" - ga_universal_tracking_code: "Codice di tracking Google Universal Analytics (analytics.js), es: UA-12345678-9; leggi http://google.com/analytics" - ga_universal_domain_name: "Nome di dominio Google Universal Analytics (analytics.js), es: mysite.com; leggi http://google.com/analytics" + post_undo_action_window_mins: "Numero di minuti durante i quali gli utenti possono annullare le loro azioni recenti su un messaggio (segnalazioni, Mi piace, ecc.)." + must_approve_users: "Lo staff deve approvare tutti i nuovi account utente prima che essi possano accedere al sito." + ga_tracking_code: "Codice Google Analytics per il tracking, es: UA-12345678-9; vedi http://google.com/analytics" + ga_domain_name: "Nome del dominio Google analytics (ga.js), esempio: miosito.com; vedi http://google.com/analytics" + ga_universal_tracking_code: "Codice di tracking Google Universal Analytics (analytics.js), es: UA-12345678-9; vedi http://google.com/analytics" + ga_universal_domain_name: "Nome di dominio Google Universal Analytics (analytics.js), es: miosito.com; vedi http://google.com/analytics" + enable_escaped_fragments: "Usa le Ajax-Crawling API di Google se non viene rilevato un webcrawler. Vedi https://support.google.com/webmasters/answer/174992?hl=en" enable_noscript_support: "Abilita il supporto standard ai webcrawler dei motori di ricerca attraverso il tag noscript" allow_moderators_to_create_categories: "Permetti ai moderatori di creare nuove categorie" - top_menu: "Determina quali elementi vengono mostrati nella navigazione in homepage, ed in quale ordine. Esempio latest|new|unread|starred|categories|top|read|posted" - post_menu: "Determina quali elementi vengono mostrati nel menù del post, ed in quale ordine. Esempio like|edit|flag|delete|share|bookmark|reply" + cors_origins: "Origini permesse per richieste cross-origin (CORS). Ogni origine deve includere http:// o https://. La variabile d'ambiente DISCOURSE_ENABLE_CORS deve essere impostata come true per abilitare CORS." + top_menu: "Determina quali oggetti appaiono nella navigazione della pagina iniziale, e in quale ordine. Esempio latest|new|unread|categories|top|read|posted|bookmarks" + post_menu: "Determina quali elementi vengono mostrati nel menu del messaggio, e in quale ordine. Esempio like|edit|flag|delete|share|bookmark|reply" post_menu_hidden_items: "Le voci di menu da nascondere per difetto nel menu di composizione del messaggio, a meno che non vengano premuti i punti di sospensione per espanderle." share_links: "Determina quali elementi appaiono nella finestra di condivisione e in quale ordine." track_external_right_clicks: "Segui i collegamenti esterni sui quali viene fatto click destro (es: apri in una nuova tab). Disabilitato di default perché riscrive le URL" - topics_per_page: "Quanti argomenti vengono caricati per difetto nella lista di argomenti, e quando si scorre in basso per caricare altri argomenti" + site_contact_username: "Un nome utente valido dello staff da cui inviare tutti i messaggi privati automatici. Se lasciato vuoto verrà usato l'account System di default." send_welcome_message: "Invia a tutti i nuovi utenti un messaggio di benvenuto con una guida di avvio rapido." + suppress_reply_directly_below: "Non mostrare il conteggio espandibile delle risposte quando c'è una sola risposta sotto quel messaggio." + suppress_reply_directly_above: "Non mostrare in-risposta-a espandibile in un messaggio quando c'è una sola risposta sopra quel messaggio. " + suppress_reply_when_quoting: "Non mostrare in-risposta-a espandibile in un messaggio quando il messaggio include la citazione." + max_reply_history: "Numero massimo di risposte da espandere quando si espande in-risposta-a" + experimental_reply_expansion: "Nascondi le risposte intermedie quando si espande una risposta (sperimentale)" topics_per_period_in_top_summary: "Numero di argomenti di punta mostrati nel riepilogo di default." + topics_per_period_in_top_page: "Numero di argomenti di punta mostrati nella vista espansa 'Mostra Altro'" + redirect_users_to_top_page: "Redirigi automaticamente i nuovi utenti e quelli assenti da tempo sulla pagina degli argomenti di punta." show_email_on_profile: "Mostra l'email di un utente nel suo profilo (visibile solo a se stesso e allo staff)" + email_token_valid_hours: "I messaggi di password dimenticata / attivazione account sono validi per (n) ore." + email_token_grace_period_hours: "I messaggi di password dimenticata / attivazione account sono ancora validi per un periodo di grazia di (n) ore dopo essere stati convalidati." enable_badges: "Attiva il sistema a targhette" allow_index_in_robots_txt: "Specifica nel file robots.txt che questo sito permette l'indicizzazione da parte dei motori di ricerca." + email_domains_blacklist: "Una lista di domini email con cui gli utenti non possono registrare un account. Ad esempio: mailinator.com trashmail.net" + email_domains_whitelist: "Una lista di domini email che gli utenti DEVONO usare per registrare un account. ATTENZIONE: gli utenti con un dominio email diverso da quelli elencati non saranno permessi!" + forgot_password_strict: "Non informare gli utenti dell'esistenza dell'account quando richiamano la finestra per la password dimenticata." + log_out_strict: "Quando si fa logout, esci da TUTTE le sessioni dell'utente su tutti i dispositivi" + version_checks: "Verifica su Discourse Hub l'esistenza di aggiornamenti e mostra i messaggi per le nuove versioni nel cruscotto /admin" new_version_emails: "Invia un'email all'indirizzo contact_email quando è disponibile una nuova versione di Discourse." port: "SOLO PER SVILUPPATORI! ATTENZIONE! Usa questa porta HTTP al posto della porta di default 80. Lascia vuoto per usare il default 80." force_hostname: "SOLO PER SVILUPPATORI! ATTENZIONE! Specifica un hostname nella URL. Lascia vuoto per usare il default." invite_expiry_days: "Per quanti giorni le chiavi per inviti utente sono valide" + invite_passthrough_hours: "Per quanto tempo un utente può usare una chiave d'invito per fare login, in ore" invite_only: "La registrazione pubblica è disabilitata, tutti i nuovi utenti devono essere invitati esplicitamente da altri membri o dallo staff." login_required: "E' richiesta l'autenticazione per leggere contenuti su questo sito, disabilita l'accesso anonimo." min_username_length: "Lunghezza minima del nome utente in caratteri. ATTENZIONE: GLI UTENTI GIÀ ESISTENTI CON NOMI PIÙ CORTI DI QUESTO NON RIUSCIRANNO AD ACCEDERE AL SITO." max_username_length: "Lunghezza massima del nome utente in caratteri. ATTENZIONE: GLI UTENTI GIÀ ESISTENTI CON NOMI PIÙ LUNGHI DI QUESTO NON RIUSCIRANNO AD ACCEDERE AL SITO." min_password_length: "Minima lunghezza della password." block_common_passwords: "Non permettere password che sono nelle 10.000 password più comuni." - enable_sso: "Abilita il single sign on attraverso un sito esterno (nota: disabilita gli inviti)" + enable_sso: "Attiva il signle sign.on tramite un sito esterno (ATTENZIONE: l'opzione mal configurata può impedire a chiunque di accedere; inoltre disabilita gli inviti)" + enable_sso_provider: "Implementa il protocollo SSO Discourse nell'endpoint /session/sso_provider, richiede che sia impostata l'opzione sso_secret " sso_url: "URL per l'endpoint del single sign on " - sso_secret: "Stringa segreta utilizzata per criptare/decriptare le informazioni SSO, assicurati che sia lunga almeno 10 caratteri" - sso_overrides_email: "Sostituisce le email locali con quelli del sito esterno con cui ci si è connessi tramite SSO (ATTENZIONE: potrebbero avvenire discrepanze a causa delle direttive per le email locali)" - sso_overrides_username: "Sostituisce gli username locali con quelli del sito esterno con cui ci si è connessi tramite SSO (ATTENZIONE: potrebbero avvenire discrepanze a causa delle differenze dei requisiti di lunghezza)" - sso_overrides_name: "Sostituisce i nomi locali con quelli del sito esterno con cui ci si è connessi tramite SSO (ATTENZIONE: potrebbero avvenire discrepanze a causa delle direttive per i nomi locali)" + sso_secret: "Stringa segreta utilizzata per autenticare crittograficamente le informazioni SSO, assicurati che sia lunga almeno 10 caratteri" + sso_overrides_email: "Sostituisce le email locali con quelle del sito esterno con cui ci si è connessi tramite SSO (ATTENZIONE: potrebbero avvenire discrepanze a causa delle direttive per le email locali)" + sso_overrides_username: "Sovrascrive il nome utente locale con quello del sito esterno con cui ci si è connessi tramite SSO (ATTENZIONE: potrebbero verificarsi discrepanze a causa delle differenze tra lunghezza effettiva e richiesta del nome utente)" + sso_overrides_name: "Sovrascrive il nome locale con quello del sito esterno con cui ci si è connessi tramite SSO (ATTENZIONE: potrebbero verificarsi discrepanze a causa della normalizzazione dei nomi locali)" + sso_overrides_avatar: "Sostituisce l'avatar dell'utente con un avatar su un sito esterno usando SSO. Se abilitato, si raccomanda di disabilitare allow_uploaded_avatars" + enable_local_logins: "Abilita account basati su login con nome utente e password locali. (Nota: deve essere abilitato affinché funzionino gli inviti)" allow_new_registrations: "Abilita la registrazione di nuovi utenti. Se deselezionato, non sarà possibile creare nuovi account." - enable_google_logins: "(deprecato) Attiva l'autenticazione Google. Questo è il metodo OpenID che Google ha deprecato. Le nuove installazioni non funzioneranno con questo. Utilizza Google Oauth2 invece. Le installazioni esistenti devono migrare a Google Oauth2 entro il 20 Aprile 2015." + enable_google_logins: "(obsoleto) Attiva l'autenticazione Google. Questo è il metodo di autenticazione OpenID che Google ha definito obsoleto. Le nuove installazioni NON funzioneranno con questa opzione. Utilizzare Google Oauth2. Le installazioni esistenti devono migrare a Google Oauth2 entro il 20 aprile 2015." enable_yahoo_logins: "Abilita l'autenticazione con Yahoo" enable_google_oauth2_logins: "Abilita l'autenticazione con Google Oauth2. Questo è il metodo di autenticazione attualmente supportato da Google. Richiede una chiave e un secret." google_oauth2_client_id: "Client ID della tua applicazione Google." google_oauth2_client_secret: "Client secret della tua applicazione Google." enable_twitter_logins: "Abilita l'autenticazione con Twitter, richiede twitter_consumer_key e twitter_consumer_secret" - twitter_consumer_key: "Consumer key per autenticazione via Twitter, ottenibile da http://dev.twitter.com" - twitter_consumer_secret: "Consumer secret per autenticazione via Twitter, ottenibile da http://dev.twitter.com" + twitter_consumer_key: "Consumer key per autenticazione via Twitter, come registrata su http://dev.twitter.com" + twitter_consumer_secret: "Consumer secret per autenticazione via Twitter, come registrata su http://dev.twitter.com" enable_facebook_logins: "Abilita l'autenticazione via Facebook, richiede facebook_app_id e facebook_app_secret" - facebook_app_id: "App id per autenticazione Facebook, ottenibile da https://developers.facebook.com/apps" - facebook_app_secret: "App secret per autenticazione Facebook, ottenibile da https://developers.facebook.com/apps" + facebook_app_id: "App id per autenticazione Facebook, come registrata su https://developers.facebook.com/apps" + facebook_app_secret: "App secret per autenticazione Facebook, come registrata su https://developers.facebook.com/apps" enable_github_logins: "Abilita l'autenticazione via Github, richiede github_client_id e github_client_secret" - github_client_id: "Client id per autenticazione Github, ottenibile da https://github.com/settings/applications" - github_client_secret: "Client secret per autenticazione Github, ottenibile da https://github.com/settings/applications" - maximum_backups: "Il numero massimo di backup da tenere sul disco. I backup più vecchi vengono automaticamente cancellati." + github_client_id: "Client id per autenticazione Github, come registrata su https://github.com/settings/applications" + github_client_secret: "Client secret per autenticazione Github, come registrata su https://github.com/settings/applications" + allow_restore: "Abilita il ripristino, che sostituisce TUTTI i dati del sito! Lascia a falso a meno che non hai intenzione di ripristinare un backup." + maximum_backups: "Il numero massimo di backup da mantenere sul disco. I backup più vecchi vengono automaticamente cancellati." backup_daily: "Crea automaticamente un backup del sito una volta al giorno." - s3_backup_bucket: "Il bucket remoto che contiene i backup. ATTENZIONE: Assicurati che sia un bucket privato." - active_user_rate_limit_secs: "Quanto frequentemente viene aggiornato il campo 'last_seen_at' field, in secondi" + enable_s3_backups: "Carica i backup su S3 quando completati. IMPORTANTE: richiede che siano inserite valide credenziali S3 nelle impostazioni File." + s3_backup_bucket: "Il bucket remoto che contiene i backup. ATTENZIONE: assicurati che sia un bucket privato." + active_user_rate_limit_secs: "Ogni quanti secondi viene aggiornato il campo 'last_seen_at'" + verbose_localization: "Mostra suggerimenti per la traduzione nell'interfaccia utente" previous_visit_timeout_hours: "Durata di una visita prima che venga considerata la visita 'precedente', in ore" rate_limit_create_topic: "Dopo aver creato un argomento, gli utenti devono aspettare (n) secondi prima di poterne creare un altro." rate_limit_create_post: "Dopo aver inviato un messaggio, gli utenti devono aspettare (n) secondi prima di creare un altro messaggio. " rate_limit_new_user_create_topic: "Dopo aver creato un argomento, i nuovi utenti devono aspettare (n) secondi prima di poter creare un altro argomento." rate_limit_new_user_create_post: "Dopo aver inviato un messaggio, i nuovi utenti devono aspettare (n) secondi prima di creare un altro messaggio. " max_likes_per_day: "Massimo numero di \"Mi piace\" per utente al giorno." - max_flags_per_day: "Massimo numero di segnalazioni per utente al giorno." + max_flags_per_day: "Numero massimo di segnalazioni per utente al giorno." max_bookmarks_per_day: "Massimo numero di segnalibri per utente al giorno." max_edits_per_day: "Massimo numero di modifiche per utente al giorno." - max_stars_per_day: "Numero massimo di argomenti che possono essere inseriti nei preferiti per utente al giorno." max_topics_per_day: "Massimo numero di argomenti che un utente può creare al giorno." - max_private_messages_per_day: "Massimo numero di messaggi privati che un utente può creare al giorno." + max_private_messages_per_day: "Numero massimo di messaggi privati che gli utenti possono creare al giorno." + max_invites_per_day: "Numero massimo di inviti che un utente può inviare in un giorno." suggested_topics: "Numero di argomenti suggeriti mostrati in fondo ad un argomento." limit_suggested_to_category: "Negli argomenti suggeriti, mostra soltanto argomenti della categoria corrente. " - clean_orphan_uploads_grace_period_hours: "Quante ore prima che un upload \"orphan\" venga rimosso." - purge_deleted_uploads_grace_period_days: "Giorni che devono trascorrere prima che un upload cancellato venga rimosso del tutto." + clean_up_uploads: "Elimina i caricamenti orfani per evitare l'hosting di materiale illegale. ATTENZIONE: prima di attivare questa impostazione è meglio effettuare un backup della cartella /uploads." + clean_orphan_uploads_grace_period_hours: "Dopo quante ore un caricamento orfano (senza riferimenti) viene rimosso." + purge_deleted_uploads_grace_period_days: "Dopo quanti giorni un caricamento cancellato viene rimosso." + purge_unactivated_users_grace_period_days: "Periodo di grazia (in giorni) prima che un utente che non abbia attivato il proprio account venga cancellato." + enable_s3_uploads: "Mette i caricamenti sullo spazio disco Amazon S3. IMPORTANTE: richiede che siano inserite valide credenziali S3 (sia l'access key id sia l'access key secret)." + s3_upload_bucket: "Il nome del bucket Amazon S3 sul quale verranno caricati i file. ATTENZIONE: deve essere tutto minuscolo, senza punti, senza trattini bassi." + s3_access_key_id: "La access key id Amazon S3 che verrà usata per caricare le immagini." + s3_secret_access_key: "La access key secret Amazon S3 che verrà usata per caricare le immagini." + s3_region: "La region name Amazon S3 che verrà usata per caricare le immagini." + enable_flash_video_onebox: "Attiva l'inserimento di collegamenti swf e flv (Adobe Flash) in onebox. ATTENZIONE: comporta rischi per la sicurezza." default_invitee_trust_level: "Livello di esperienza (0-4) assegnato di default agli utenti invitati." default_trust_level: "Livello di esperienza (0-4) assegnato di default ai nuovi utenti." tl1_requires_topics_entered: "Quanti argomenti deve inserire un utente per essere promosso a livello di esperienza 1." @@ -701,46 +815,83 @@ it: tl3_promotion_min_duration: "Per quanti giorni al minimo mantenere la promozione al livello di esperienza 3 prima che un utente venga degradato al livello di esperienza 2." tl3_requires_likes_given: "Il minimo numero di Mi piace che devono essere dati negli ultimi 100 giorni per essere promossi al livello di esperienza 3." tl3_requires_likes_received: "Il minimo numero di Mi piace che devono essere ricevuti negli ultimi 100 giorni per essere promossi al livello di esperienza 3." + tl3_links_no_follow: "Non rimuovere rel=nofollow dai collegamenti pubblicati da utenti con livello di esperienza 3." min_trust_to_create_topic: "Livello minimo richiesto per creare un nuovo argomento." min_trust_to_edit_wiki_post: "Livello minimo richiesto per modificare un argomento segnato come wiki." - newuser_max_links: "Quanti collegamenti un nuovo utente può aggiungere a un post" - newuser_max_images: "Quante immagini un nuovo utente può aggiungere ad un post." - newuser_max_attachments: "Quanti allegati un nuovo utente può aggiungere ad un post." - newuser_max_replies_per_topic: "Numero massimo di risposte, che un utente può inoltrare in un singolo topic, prima che qualcuno risponda." + newuser_max_links: "Quanti collegamenti può aggiungere a un messaggio un nuovo utente ." + newuser_max_images: "Quante immagini può aggiungere ad un messaggio un nuovo utente." + newuser_max_attachments: "Quanti allegati può aggiungere ad un messaggio un nuovo utente." + newuser_max_mentions_per_post: "Massimo numero di menzioni ad un @nome che un utente può fare in un messaggio." + newuser_max_replies_per_topic: "Numero massimo di risposte che un nuovo utente può inviare in un singolo argomento, prima che qualcuno risponda." + max_mentions_per_post: "Numero massimo di menzioni ad un @nome che chiunque può fare in un messaggio." + create_thumbnails: "Crea anteprime e lightbox delle immagini che sono troppo grandi per essere contenute in un messaggio." + email_time_window_mins: "Aspetta (n) minuti prima di inviare email di notifica, per dare agli utenti la possibilità di modificare e completare i loro messaggi." + email_posts_context: "Quante risposte precedenti inserire come contesto nelle email di notifica." + flush_timings_secs: "Frequenza di svuotamento dei dati temporali verso il server, in secondi." + max_word_length: "Numero massimo di caratteri che compongono il titolo di un argomento." + title_min_entropy: "Minima entropia (caratteri unici) richiesti come titolo di un argomento." + body_min_entropy: "Minima entropia (caratteri unici) richiesti come corpo di un messaggio." title_fancy_entities: "Converti caratteri ASCII comuni in HTML nei titoli dell'argomento, tipo SmartyPants http://daringfireball.net/projects/smartypants/" - title_prettify: "Previeni refusi ed errori comuni nei titoli, inclusi Tutto maiuscolo, Primo carattere minuscolo, ! e ? ripetuti, . aggiuntivi alla fine, etc etc." - faq_url: "Se hai un documento sulle FAQ ospitato da qualche altra parte che vuoi usare, fornisci l'URL qui." - tos_url: "Se hai un documento dei Termini di Servizio ospitato da qualche altra parte che vuoi usare, fornisci l'URL qui." - privacy_policy_url: "Se hai un documento sulle Politiche di Privacy ospitato da qualche altra parte che vuoi usare, fornisci l'URL qui." - newuser_spam_host_threshold: "Il numero delle volte che un nuovo utente può postare un link verso lo stesso host compreso nel `newuser_spam_host_posts` post prima che venga considerato spam." - reply_by_email_address: "Template di risposta via email, per esempio: %{reply_key}@risposta.esempio.com o risposte+%{reply_key}@esempio.com" + min_title_similar_length: "Lunghezza minima di un titolo che attiva il controllo su argomenti simili." + min_body_similar_length: "Lunghezza minima del corpo di un messaggio che attiva il controllo su argomenti simili." + category_colors: "Un elenco di valori esadecimali di colori permessi per le categorie." + category_style: "Stile grafico per le etichette di categoria." + authorized_extensions: "Una lista di estensioni dei file che è permesso caricare (usa '*' per permettere tutti i tipi di file) " + max_similar_results: "Quanti argomenti simili mostrare sopra l'editor quando si scrive un nuovo argomento. Il paragone viene fatto sul titolo e sul corpo." + title_prettify: "Evita refusi ed errori comuni nei titoli, incluso il testo tutto maiuscolo, il primo carattere minuscolo, troppi caratteri ! e ?, puntini aggiuntivi alla fine della parola ecc." + topic_views_heat_low: "Dopo tale numero di visualizzazioni, il campo visualizzazioni è evidenziato leggermente." + topic_views_heat_medium: "Dopo tale numero di visualizzazioni, il campo visualizzazioni è evidenziato mediamente." + topic_views_heat_high: "Dopo tale numero di visualizzazioni, il campo visualizzazioni è evidenziato fortemente." + cold_age_days_low: "Dopo tale numero di giorni di conversazione, la data di ultima attività viene evidenziata leggermente." + cold_age_days_medium: "Dopo tale numero di giorni di conversazione, la data di ultima attività viene evidenziata mediamente." + cold_age_days_high: "Dopo tale numero di giorni di conversazione, la data di ultima attività viene evidenziata fortemente." + history_hours_low: "Un messaggio modificato entro tale numero di ore ha l'indicatore di modifica evidenziato leggermente." + history_hours_medium: "Un messaggio modificato entro tale numero di ore ha l'indicatore di modifica evidenziato mediamente." + history_hours_high: "Un messaggio modificato entro tale numero di ore ha l'indicatore di modifica evidenziato fortemente." + topic_post_like_heat_low: "Dopo che il rapporto Mi piace:Messaggi supera tale valore, il campo con il contatore di messaggi è evidenziato leggermente." + topic_post_like_heat_medium: "Dopo che il rapporto Mi piace:Messaggi supera tale valore, il campo con il contatore di messaggi è evidenziato mediamente." + topic_post_like_heat_high: "Dopo che il rapporto Mi piace:Messaggi supera tale valore, il campo con il contatore di messaggi è evidenziato fortemente." + faq_url: "Se vuoi usare un documento sulle FAQ ospitato da qualche altra parte, fornisci qui la sua URL completa." + tos_url: "Se vuoi usare un documento dei Termini di Servizio ospitato da qualche altra parte, fornisci qui la sua URL completa." + privacy_policy_url: "Se vuoi usare un documento sulle Politiche di Privacy ospitato da qualche altra parte, fornisci qui la sua URL completa." + newuser_spam_host_threshold: "Quante volte un nuovo utente può inviare un collegamento verso lo stesso host compreso nei messaggi `newuser_spam_host_posts` prima che venga considerato spam." + reply_by_email_enabled: "Abilita la possibilità di rispondere ai messaggi tramite email." + reply_by_email_address: "Modello per rispondere via email, per esempio: %{reply_key}@risposta.esempio.com o risposte+%{reply_key}@esempio.com" + disable_emails: "Evita che Discourse invii qualunque tipo di email" pop3_polling_port: "La porta dell'account POP3." + email_in: "Permetti agli utenti di pubblicare nuovi argomenti per email (richiede il pop3 polling). Configura gli indirizzi nella scheda \"Impostazioni\" di ciascuna categoria." + email_in_min_trust: "Livello di esperienza minimo necessario affinché un utente possa pubblicare nuovi argomenti via email." delete_user_max_post_age: "Non permettere di cancellare utenti il cui primo messaggio è più vecchio di (x) giorni." - delete_all_posts_max: "Massimo numero di post che possono essere eliminati contemporaneamente con il pulsante Cancella tutti i post. Se un utente ha più post attivi, questi non possono essere eliminati contemporaneamente e l'utente non può essere cancellato." - username_change_period: "Numero di giorni (dall'iscrizione) dopo i quali è possibile modificare lo username. Digita 0 per non permettere cambi di username" + delete_all_posts_max: "Numero massimo di messaggi che possono essere eliminati contemporaneamente con il pulsante Cancella Tutti. Se un utente ha un numero maggiore di tali messaggi, questi non possono essere eliminati contemporaneamente e l'utente non può essere cancellato." + username_change_period: "Numero di giorni dall'iscrizione dopo i quali è possibile modificare il nome utente (imposta a 0 per non permettere il cambio del nome utente)." email_editable: "Permetti agli utenti di cambiare l'indirizzo email dopo la registrazione." + logout_redirect: "Dove redirigere il browser dopo aver effettuato il logout, es.: (http://qualchesito.com/logout)" allow_uploaded_avatars: "Permetti agli utenti di caricare un avatar personale." - default_digest_email_frequency: "Quanto spesso gli utenti ricevono mail di riepilogo. Possono modificare questa impostazione nel loro pannello utente." - default_external_links_in_new_tab: "Apri link esterni in una nuova scheda. Gli utenti possono modificare questa impostazione nelle loro preferenze." - public_user_custom_fields: "Una whitelist di campi personalizzati di un utente che possono essere mostrati pubblicamente." + digest_topics: "Numero massimo di argomenti da mostrare nel riassunto email." + default_digest_email_frequency: "Quanto spesso gli utenti ricevono email di riepilogo. Gli utenti possono modificare questa impostazione nelle loro preferenze." + default_external_links_in_new_tab: "Apri i collegamenti esterni in una nuova scheda. Gli utenti possono modificare questa impostazione nelle loro preferenze." + public_user_custom_fields: "Una lista bianca di campi personalizzati di un utente che possono essere mostrati pubblicamente." allow_profile_backgrounds: "Permetti agli utenti di caricare immagini di sfondo per il profilo." - enable_mobile_theme: "I dispositivi mobile usano un tema apposito, con possibilità di passare alla visualizzazione desktop. Disabilita questa opzione se vuoi usare un foglio di stile custom e completamente responsive." + enable_mobile_theme: "I dispositivi mobili usano un tema apposito, con possibilità di passare alla visualizzazione completa. Disabilita questa opzione se vuoi usare un foglio di stile personalizzato che sia completamente reattivo." suppress_uncategorized_badge: "Non mostrare la targhetta per gli argomenti senza categoria nell'elenco degli argomenti." global_notice: "Mostra a tutti i visitatori un avviso con striscione globale URGENTE, EMERGENZA, lascia vuoto per nasconderlo (HTML consentito)." - short_progress_text_threshold: "Quando il numero di messaggi di un argomento supera questo valore, la barra di avanzamento mostrerà solo il numero attuale di messaggi. Se modifichi la lunghezza della barra di avanzamento, potresti dover cambiare questo valore." + invites_per_page: "Inviti predefiniti mostrati nella pagina utente." + short_progress_text_threshold: "Quando il numero di messaggi di un argomento supera questo valore, la barra di avanzamento mostrerà solo il numero di messaggi attuale. Se modifichi l'ampiezza della barra di avanzamento, potresti dover cambiare questo valore." default_code_lang: "Ai blocchi di codice GitHub viene applicata l'evidenziazione della sintassi del linguaggio di programmazione di default (lang-auto, ruby, python ecc.)" + embed_by_username: "Nome utente Discourse dell'utente che crea gli argomenti incorporati." embed_truncate: "Tronca i messaggi incorporati." embed_category: "Categoria degli argomenti incorporati." embed_post_limit: "Numero massimo di messaggi da incorporare." - notify_about_flags_after: "Se ci sono segnalazioni che non sono state revisionate dopo tutte queste ore, invia un'email al contact_email. Imposta a 0 per disattivare." + notify_about_flags_after: "Se ci sono segnalazioni che non sono state revisionate dopo un tale numero di ore, invia un'email al contact_email. Imposta a 0 per disattivare." show_create_topics_notice: "Se il sito ha meno di 5 argomenti pubblici, mostra un avviso chiedendo agli amministratori di creare qualche argomento." + enable_emoji: "Attiva gli emoji" errors: invalid_email: "Indirizzo email non valido." invalid_username: "Non c'è alcun utente con quel nome." invalid_integer_min_max: "Il valore deve essere tra %{min} e %{max}." invalid_integer_min: "Il valore deve essere %{min} o maggiore." invalid_integer_max: "Il valore non può essere maggiore di %{max}." - invalid_integer: "Deve essere un valore intero." + invalid_integer: "Il valore deve essere un intero." regex_mismatch: "Il valore non rispetta il formato richiesto." invalid_string: "Valore non valido." invalid_string_min_max: "Il numero di caratteri deve essere tra %{min} e %{max}." @@ -765,6 +916,10 @@ it: category: 'Categorie' topic: 'Risultati' user: 'Utenti' + sso: + account_not_approved: "L'account è in attesa di approvazione, riceverai una notifica per email una volta approvato" + unknown_error: "Errore nell'aggiornamento delle informazioni, contatta l'amministratore del sito" + timeout_expired: "Il tempo di autenticazione di questo account è scaduto, prova ad autenticarti nuovamente" original_poster: "Autore Iniziale" most_posts: "Maggioranza Messaggi" most_recent_poster: "Autore Più Recente" @@ -825,7 +980,6 @@ it: errors: "%{errors}" not_available: "Non disponibile. Prova %{suggestion}?" something_already_taken: "Qualcosa è andato storto, forse il nome utente o l'email sono già registrati. Prova a recuperare la password." - omniauth_error: "Spiacenti, si è verificato un errore nel verificare il tuo account %{strategy}. Forse non hai accettato l'autorizzazione?" omniauth_error_unknown: "Qualcosa è andato storto durante il collegamento, per favore riprova." new_registrations_disabled: "Non sono permesse nuove registrazioni al momento." password_too_long: "Le password sono limitate a 200 caratteri." @@ -876,29 +1030,124 @@ it: flag_reasons: off_topic: "Il tuo messaggio è stato segnalato come **fuori tema**: la comunità pensa non riguardi l'argomento, come attualmente definito dal titolo e dal contenuto del primo messaggio." inappropriate: "Il tuo messaggio è stato segnalato come **inappropriato**: la comunità lo ritiene offensivo, ingiurioso o una violazione delle [linee guida della comunità](/guidelines)." - spam: "Il tuo messaggio è stato segnalato come **spam**: la comunità pensa che sia una pubblicità, non utile né rilevante all'argomento, ma di natura promozionale." - notify_moderators: "Il tuo messaggio è stato segnalato **all'attenzione dei moderatori**: la comunità pensa che il tuo messaggio richieda l'intervento dei moderatori." + spam: "Il tuo messaggio è stato segnalato come **spam**: la comunità ritiene che sia pubblicità o qualcosa di natura meramente promozionale invece di essere utile o rilevante per l'argomento, come invece ci si aspetterebbe." + notify_moderators: "Il tuo messaggio è stato segnalato **all'attenzione dei moderatori**: la comunità ritiene che ci sia qualcosa nel messaggio che richiede l'intervento manuale di un membro dello staff." flags_dispositions: agreed: "Grazie per averci informato. Sappiamo che c'è un problema e lo stiamo analizzando." agreed_and_deleted: "Grazie per averci informato. Sappiamo che c'è un problema e abbiamo cancellato il messaggio." disagreed: "Grazie per averci informato. Stiamo provvedendo." deferred: "Grazie per averci informato. Stiamo provvedendo." deferred_and_deleted: "Grazie per averci informato. Abbiamo rimosso il messaggio." + temporarily_closed_due_to_flags: "Questo argomento è temporaneamente chiuso a causa di numerose segnalazioni della comunità." system_messages: post_hidden: subject_template: "Messaggio nascosto a causa di segnalazioni dalla comunità" + text_body_template: | + Salve, + + questo è un messaggio automatico da parte di %{site_name} per informarti che il tuo messaggio è stato nascosto. + + %{base_url}%{url} + + %{flag_reason} + + Vari membri della comunità hanno segnalato il messaggio prima che venisse nascosto, quindi per favore pensa come modificarlo alla luce di queste osservazioni. **Puoi modificare il messaggio dopo %{edit_delay} minuti, e verrà automaticamente ripubblicato.** Ciò accrescerà il tuo livello di esperienza. + + Tuttavia, se il messaggio dovesse venire nascosto dalla comunità una seconda volta, rimarrà nascosto fino a che un membro dello staff se ne occuperà – e saranno possibili ulteriori sanzioni, compresa la sospensione del tuo account. + + Per ulteriori informazioni, puoi far riferimento alle nostre [community guidelines](%{base_url}/guidelines). + usage_tips: + text_body_template: | + Questo messaggio privato contiene alcune indicazioni per cominciare. + + ## Continua a scorrere la pagina + + Non ci sono pulsanti o numeri delle pagine successive - per leggere di più, **scorri semplicemente in giù la pagina** + + I nuovi post compariranno automaticamente man mano. + + ## Dove mi trovo? + + - Per fare una ricerca, per la tua pagina utente o per il menu, **usa i pulsanti icona in alto a destra**. + + - Il titolo di una discussione porta sempre al prossimo post non letto. Usa la data dell'ultima attività per saltare rapidamente al primo o ultimo post in una discussione. + + - Quando leggi una discussione, vai in cima ↑ selezionando il titolo della discussione. Seleziona la barra di avanzamento verde che si trova in basso a destra per mostrare tutti i controlli di navigazione, o usa i tasti inizio e fine della tastiera. + + + + ## Come faccio a rispondere? + + - Per rispondere alla discussione generale, usa il pulsante Rispondi in fondo alla pagina. + + - Per rispondere a un post specifico, usa il pulsante Rispondi su quel messaggio. + + - Per portare la conversazione in una nuova direzione, ma mantenendole collegate, usa Rispondi come nuovo argomento alla destra del messaggio. + + Per citare qualcuno nella tua risposta, seleziona il testo che desideri citare e fai clic su uno dei pulsanti Rispondi. + + + + Per mandare una notifica a qualcuno nella tua risposta, menziona il suo nome. Scrivere `@` ed apparirà un completatore automatico. + + + + For [standard Emoji](http://www.emoji.codes/), just start typing `:` or the traditional smileys `:)` :smile: + + ## Che altro posso fare? + + In fondo a ogni messaggio ci sono dei pulsanti attivi. + + + + Per dire a qualcuno che ti è piaciuto il suo messaggio, usa il bottone **Mi piace** in fondo al messaggio. Se trovi un messaggio con dei problemi, usa subito il bottone **Segnala** in fondo al post e fallo sapere privatamente all'autore o allo staff. + + Puoi anche **condividere** un link a un messaggio, o fare un **segnalibro** di un messaggio per usarlo più tardi sulla tua pagina utente. + + ## Chi sta parlando con me? + + Quando qualcuno risponde a un tuo post, lo cita o menziona il tuo `@nome`, comparirà un numero sul pulsante delle conversazioni in alto a destra nella pagina. Usalo accedere alle tue **notifiche**. + + + + Non preoccuparti di perdere delle risposte – se non sei online quando arrivano, ti verranno inviate per email le risposte dirette (e i messaggi privati) che arrivano. + + ## Quand'è che le conversazioni sono nuove? + + Di norma tutte le conversazioni più recenti di due giorni si considerano nuove, e verrà seguita automaticamente ogni conversazione a cui tu abbia partecipato (risposto, creato, o letto per un certo tempo). + + Vedrai gli indicatori blu "nuovo" e "numero" accanto a queste conversazioni: + + + + Puoi modificare le impostazioni di notifica di ciascuna discussione con i controlli in fondo al topic (questa impostazione si può anche definire per ciascuna categoria). Per modificare il modo in cui segui le discussioni, o la definizione di nuovo, vedi le [tue preferenze utente](%{baseurl}/my/preferences). + + ## Perché non posso fare certe cose? + + Per ragioni di sicurezza i nuovo utenti hanno alcune limitazioni. Man mano che parteciperai guadagnerai la fiducia della comunità, ne diverrai partecipante a pieno titolo e questi limiti verranno rimossi automaticamente. Ad un [livello di fiducia](https://meta.discourse.org/t/what-do-user-trust-levels-do/4924) sufficientemente alto guadagnerai ulteriori capacità per aiutarci a gestire insieme questa comunità. welcome_user: + subject_template: "Benvenuto su %{site_name}!" + text_body_template: "Grazie per esserti unito a %{site_name}, e benvenuto!\n\n %{new_user_tips} \n\nNoi crediamo sempre in un [comportamento comunitario civile](%{base_url}/guidelines). \n\nBuona permanenza!\n\n(Se vuoi comunicare in privato con [i membri dello staff](%{base_url}/about) come nuovo utente, rispondi semplicemente a questo messaggio privato.)\n" + welcome_invite: subject_template: "Benvenuto su %{site_name}!" text_body_template: | - Grazie per esserti unito a %{site_name}, e benvenuto! + Grazie per aver accettato l'invito a %{site_name} -- benvenuto/a!! + + Ti abbiamo creato il nuovo account **%{username}** e sei autenticato sul sito. Puoi cambiare il tuo nome visitando [il tuo profilo utente][prefs]. + + Per fare login in futuro: + + 1. **Usa sempre lo stesso indirizzo email dell'invito originale** quando fai login. Altrimenti non capiremo che sei tu!! + + 2. Crea una password unica per [il tuo profilo utente][prefs], e usala per fare login. %{new_user_tips} - Noi crediamo fermamente in un [comportamento comunitario civile](%{base_url}/guidelines). + Crediamo nei [comportamenti civili e costruttivi nella comunità](%{base_url}/guidelines), sempre. - Buona permanenza! - welcome_invite: - subject_template: "Benvenuto su %{site_name}!" + (Se vuoi comunicare privatamente con i [membri dello staff](%{base_url}/about) come nuovo utente, rispondi a questo messaggio privato.) + + [prefs]: %{user_preferences_url} backup_succeeded: subject_template: "Backup completato correttamente" backup_failed: @@ -926,49 +1175,58 @@ it: ``` bulk_invite_succeeded: subject_template: "Invito di massa processato con successo" - text_body_template: "Il tuo invito di massa è stato processato, %{sent} inviti spediti." + text_body_template: "Il tuo file con l'invito di massa è stato processato, %{sent} inviti spediti." bulk_invite_failed: subject_template: "Invito di massa processato con errori" csv_export_succeeded: - subject_template: "Esportazione dati completata con successo" + subject_template: "Esportazione dati completa" csv_export_failed: - subject_template: "Esportazione fallita" - text_body_template: "Esportazione fallita. Controlla i log." + subject_template: "Esportazione dati fallita" email_reject_trust_level: subject_template: "Problema email -- Livello Esperienza insufficente" text_body_template: | Ci dispiace, ma il tuo messaggio email per %{destination} (con il titolo %{former_title}) non è stato processato. Il tuo utente non ha il livello di esperienza richiesto per inviare nuovi argomenti a questo indirizzo email. Se credi che ciò sia un errore, contatta un membro dello staff. + email_reject_no_account: + subject_template: "Problema relativo all'email -- Account Sconosciuto" email_reject_empty: - subject_template: "Problema email -- Nessun contenuto" + subject_template: "Problema relativo all'email -- Nessun Contenuto" email_reject_parsing: - subject_template: "Problema email -- Contenuto non riconosciuto" + subject_template: "Problema relativo all'email -- Contenuto non riconosciuto" email_reject_post_error: subject_template: "Problema email -- Errore di invio" email_reject_post_error_specified: subject_template: "Problema email -- Errore di invio" + email_reject_topic_not_found: + subject_template: "Problema relativo all'email -- Argomento Non Trovato" + email_reject_topic_closed: + subject_template: "Problema email: argomento chiuso" + text_body_template: | + Spiacenti, ma il tuo messaggio email a %{destination} (intitolato %{former_title}) non è andato a buon fine. + + L'argomento è chiuso. Se pensi che sia un errore, contatta un membro dello staff. too_many_spam_flags: subject_template: "Nuovo account bloccato" text_body_template: "Ciao, \n\nil presente è un messaggio automatico da %{site_name} per informarti che i tuoi messaggi sono stati automaticamente nascosti perché segnalati dalla comunità. \n\nCome misura precauzionale, il tuo nuovo account non può creare altre risposte o argomenti finché il tuo account non verrà revisionato da un membro dello staff. \n\nPer ulteriori informazioni, ti rimandiamo alle [Linee guida della comunità](%{base_url}/guidelines).\n" blocked_by_staff: subject_template: "Account bloccato" text_body_template: | - Ciao + Ciao, - Questo è un messaggio automatico da %{site_name} per informarti che il tuo account è stato bloccato da un membro dello staff. + questo è un messaggio automatico da %{site_name} per informarti che il tuo account è stato bloccato da un membro dello staff. Per maggiori dettagli, fai riferimento alle nostre [linee guida della community](%{base_url}/guidelines). user_automatically_blocked: subject_template: "Nuovo utente %{username} bloccato a causa delle segnalazioni" text_body_template: | - (Questo è un messaggio automatico.) + Questo è un messaggio automatico. - Il nuovo utente [%{username}](%{base_url}%{user_url}) è stato bloccato in automatico perchè più utenti hanno contrassegnato i post di %{username}. + Il nuovo utente [%{username}](%{base_url}%{user_url}) è stato automaticamente bloccato perché più utenti hanno contrassegnato i messaggi di %{username}. Per favore [controlla le segnalazioni](%{base_url}/admin/flags). Se %{username} è stato bloccato per errore, clicca sul tasto di sblocco nella [pagina di amministrazione di questo utente](%{base_url}%{user_url}). - La soglia può essere modificata tramite l'impostazione di blocco dei nuovi utenti. + La soglia può essere modificata tramite l'impostazione del sito `block_new_user`. spam_post_blocked: subject_template: "Il messaggi del nuovo utente %{username} sono stati bloccati a causa dell'invio di ripetuti collegamenti." unblocked: @@ -986,17 +1244,17 @@ it: text_body_template: "Ci sono nuove iscrizioni che vanno approvate (o rifiutate) prima che possano accedere a questo forum. \n\n[Per favore valutale nella sezione di amministrazione](%{base_url}/admin/users/list/pending).\n" download_remote_images_disabled: subject_template: "Lo scaricamento delle immagini remote è disabilitato" - text_body_template: "L'impostazione `download_remote_images_to_local` è stata disabilitata perché il limite di spazio sul disco definito in `download_remote_images_threshold` è stato raggiunto." - unsubscribe_link: "Se vuoi cancellarti per non ricevere più queste email, visita le tue [preferenze utente](%{user_preferences_url})." + text_body_template: "L'impostazione `download_remote_images_to_local` è stata disabilitata perché è stato raggiunto il limite di spazio su disco definito in `download_remote_images_threshold`." + unsubscribe_link: "Se vuoi annullare l'iscrizione a queste email, visita le tue [preferenze utente](%{user_preferences_url})." subject_re: "R:" subject_pm: "[MP]" user_notifications: previous_discussion: "Risposte Precedenti" unsubscribe: - title: "Cancella iscrizione" - description: "Non sei interessato a ricevere queste email? Nessun problema! Clicca qui sotto per cancellarti istantaneamente:" + title: "Annulla iscrizione" + description: "Non vuoi ricevere queste email? Nessun problema! Clicca qui sotto per annullare l'iscrizione:" reply_by_email: "Per intervenire, rispondi a questa email o visita %{base_url}%{url}." - visit_link_to_respond: "Per intervenire, visita %{base_url}%{url}" + visit_link_to_respond: "Per rispondere, visita %{base_url}%{url}" posted_by: "Pubblicato da %{username} il %{post_date}" user_invited_to_private_message_pm: subject_template: "[%{site_name}] %{username} ti invita alla conversazione privata '%{topic_title}'" @@ -1050,30 +1308,39 @@ it: --- %{respond_instructions} digest: - subject_template: "[%{site_name}] Riepilogo per %{date}" + why: "Un breve sommario di %{site_link} dalla tua ultima visita il %{last_seen_at}" + subject_template: "[%{site_name}] Riepilogo del %{date}" new_activity: "Nuove attività nei tuoi argomenti e messaggi:" - top_topics: "Post Popolari" + top_topics: "Messaggi di successo" other_new_topics: "Argomenti più attivi" + unsubscribe: "Questo riassunto viene spedito da %{site_link} ogniqualvolta non ti vediamo per un po' di tempo. Per cancellarti %{unsubscribe_link}." click_here: "clicca qui" from: "%{site_name} riepilogo" - read_more: "Leggi altro" + read_more: "Leggi Altro" + more_topics: "Ci sono stati %{new_topics_since_seen} altri nuovi argomenti." + more_topics_category: "Ulteriori nuovi argomenti:" posts: - one: "1 post" - other: "%{count} post" + one: "1 messaggio" + other: "%{count} messaggi" forgot_password: - subject_template: "[%{site_name}] Reset Password" + subject_template: "[%{site_name}] Reimposta password" text_body_template: | - Qualcuno ha richiesto il reset della password per questo account su [%{site_name}](%{base_url}). + Qualcuno ha richiesto l'azzeramento della tua password su [%{site_name}](%{base_url}). Se non sei stato tu, puoi tranquillamente ignorare questa email. - Clicca il link qui sotto per scegliere una nuova password: + Clicca il seguente collegamento per scegliere una nuova password: %{base_url}/users/password-reset/%{email_token} set_password: - subject_template: "[%{site_name}] Imposta una password" + subject_template: "[%{site_name}] Imposta Password" text_body_template: "Qualcuno ha richiesto di aggiungere una password al tuo account su [%{site_name}](%{base_url}). In alternativa, puoi collegarti usando uno dei servizi online supportati (Google, Facebook ecc.) associato con questo indirizzo email validato. \n\nSe non hai fatto tu questa richiesta, ignora tranquillamente questa email.\n\nClicca sul seguente collegamento per scegliere una password:\n%{base_url}/users/password-reset/%{email_token}\n" account_created: subject_template: "[%{site_name}] Il Tuo Nuovo Account " + text_body_template: | + Ti è stato creato un account su %{site_name} + + Fai clic sul seguente collegamento per scegliere una password per il tuo nuovo account: + %{base_url}/users/password-reset/%{email_token} authorize_email: subject_template: "[%{site_name}] Conferma il tuo nuovo indirizzo email" text_body_template: | @@ -1081,35 +1348,19 @@ it: %{base_url}/users/authorize-email/%{email_token} signup_after_approval: - subject_template: "Puoi partecipare a %{site_name}!" - text_body_template: |+ - Benvenuto su %{site_name}! - - Un membro dello staff ha approvato il tuo account su %{site_name}. - - Clicca sul link che segue per confermare e attivare il tuo nuovo account: - %{base_url}/users/activate-account/%{email_token} - - Se il link qui sopra non è cliccabile, prova a copiarlo e a incollarlo nella barra degli indirizzi del tuo browser. - - %{new_user_tips} - - Noi crediamo in [civilized community behavior](%{base_url}/guidelines) da sempre. - - Buona permanenza! - + subject_template: "Sei stato ammesso su %{site_name}!" signup: subject_template: "[%{site_name}] Attiva il tuo nuovo account" text_body_template: | Benvenuto su %{site_name}! - Clicca sul link qui sotto per confermare ed attivare il tuo nuovo account: + Clicca sul collegamento seguente per confermare ed attivare il tuo nuovo account: %{base_url}/users/activate-account/%{email_token} - Se il link non è cliccabile, copialo ed incollalo manualmente nella barra degli indirizzi del browser. + Se il collegamento non è cliccabile, copialo ed incollalo manualmente nella barra degli indirizzi del tuo browser. page_not_found: title: "La pagina richiesta non esiste oppure è privata." - popular_topics: "Popolari" + popular_topics: "Di successo" recent_topics: "Recenti" see_more: "Altro" search_title: "Cerca nel sito" @@ -1121,16 +1372,16 @@ it: terms_of_service: title: "Termini di Servizio" signup_form_message: 'Ho letto e accetto i Termini del Servizio.' - deleted: 'cancellato' + deleted: 'cancellati' upload: edit_reason: "copie locali delle immagini scaricate" unauthorized: "Spiacenti, il file che stai cercando di caricare non è autorizzato (estensioni autorizzate: %{authorized_extensions})." pasted_image_filename: "Immagine incollata" - store_failure: "Upload #%{upload_id} fallito per l'utente #%{user_id}." + store_failure: "Caricamento n°%{upload_id} fallito per l'utente n°%{user_id}." attachments: - too_large: "Spiacenti, la dimensione del file che vuoi caricare è eccessiva (la dimensione massima è %{max_size_kb}%kb)." + too_large: "Spiacenti, ma il file che stai cercando di caricare è troppo grande (la dimensione massima è %{max_size_kb}KB)." images: - too_large: "Spiacenti, l'immagine che stai cercando di caricare è troppo grande (le dimensioni massime sono %{max_size_kb}%kb). Per favore ridimensionala e riprova." + too_large: "Spiacenti, ma l'immagine che stai cercando di caricare è troppo grande (la dimensione massima è %{max_size_kb}KB), per favore ridimensionala e riprova." fetch_failure: "Spiacenti, si è verificato un errore nel recupero dell'immagine." unknown_image_type: "Spiacenti, ma il file che hai provato a caricare non sembra un'immagine." size_not_found: "Spiacenti, ma non riusciamo a determinare la dimensione dell'immagine. E' forse danneggiata?" @@ -1156,14 +1407,17 @@ it: guidelines: "Linee Guida" privacy: "Privacy" edit_this_page: "Modifica questa pagina" + csv_export: + boolean_yes: "Sì" + boolean_no: "No" static_topic_first_reply: | Modifica il primo messaggio in questo argomento per cambiare il contenuto della pagina %{page_name}. guidelines_topic: title: "FAQ/Linee Guida" - body: "\n\n## [Un luogo civile per discussioni pubbliche](#civilized)\n\nSu questo forum per favore comportati con lo stesso rispetto con cui ti comporteresti in un parco comunale. Come il parco, infatti, anche questa è una risorsa pubblica — un luogo dove condividere competenze, conoscenze ed interessi attraverso continue conversazioni.\n\nQuelle che seguono non sono regole ferree, ma solo dei consigli per aiutarti a giudicare il comportamento degli altri membri di questa comunità. Usiamo queste linee guida per far sì che questo luogo rimanga sempre un posto piacevole e limpido dove discutere civilmente.\n\n\n\n## [Migliorare le discussioni](#improve)\n\nPuoi aiutarci a fare di questo forum un piacevole luogo di discussione, facendo in modo di migliorare una discussione, anche se in piccola misura. Se non sei convinto che il tuo messaggio aggiunga qualcosa alla conversazione, pensa meglio a ciò che intendi dire o magari riprova più tardi.\n \nGli argomenti di cui trattiamo qui, per noi sono importanti, e vorremmo che lo fossero anche per te. Porta rispetto sia per l'argomento di discussione sia per le persone che stanno partecipando, anche se sei in disaccordo con qualcosa.\n\nUno dei modi per migliorare le discussioni è scoprire quelle che sono già in corso. Fatti un giro, spendi un po' di tempo a leggere le discussioni già inserite, prima di rispondere o di iniziare una nuova discussione; in questo modo avrai più possibilità di parlare con persone con i tuoi stessi interessi.\n\n\n\n## [Sii gentile, anche quando sei in disaccordo](#agreeable)\n\nTi capiterà a volte di aver voglia di rispondere ad un messaggio esprimendo il tuo dissenso. Va benissimo, però ricorda sempre di _criticare le idee, non le persone_. Perciò per favore evita:\n\n* Di offendere.\n* Portare attacchi _ad personam_.\n* Rispondere al tono di un messaggio invece che al suo reale contenuto.\n* Avviare un contraddittorio per partito preso – o sindrome del Bastian Contrario.\n\nPiuttosto fornisci contro-argomentazioni valide\ - \ che migliorino la discussione.\n\n\n\n## [La tua partecipazione è importante](#participate)\n\nLe discussioni qui presenti modellano il nostro futuro. Aiutaci a dirigere ed influenzare il futuro di questa comunità scegliendo di partecipare a quelle discussioni che rendono questo forum un posto interessante dove stare e, di contro, evitando quelle altre discussioni che invece non lo fanno.\n\nQuesta piattaforma fornisce degli strumenti che permettono alla comunità di identificare i contributi migliori (e i peggiori): i preferiti, i segnalibri, i \"mi piace\", le bandiere, le risposte, le modifiche, ecc. Usa tutti questi strumenti per migliorare la tua esperienza e quella degli altri.\n\nFacciamo in modo di rendere insieme questo parco pubblico un po' meglio di come l'abbiamo trovato.\n\n\n\n## [Se noti un problema, segnalalo](#flag-problems)\n\nI moderatori hanno speciali autorità; sono loro i responsabili di questo forum. Ma anche tu lo sei. Con il tuo aiuto, i moderatori possono rappresentare solo dei bravi animatori, non dei custodi o dei poliziotti.\n\nSe ti accorgi di comportamenti impropri, non rispondere. La tua risposta incoraggerebbe l'autore a continuare, inoltre consuma inutilmente le tue energie e fa perdere tempo a tutti. Semplicemente _segnalalo_. Se arrivano abbastanza segnalazioni, verranno presi provvedimenti, sia automatici che con l'intervento di un moderatore. \n\nAl fine di manutenere la nostra comunità, i moderatori si riservano il diritto di cancellare qualsiasi contenuto o qualsiasi utente in qualunque momento. I moderatori non leggono i nuovi messaggi prima che vengano pubblicati, perciò loro non sono responsabili per contenuti inseriti dai membri di questa comunità.\n\n\n\n## [Sii sempre civile](#be-civil)\n\nNon c'è niente di meglio della maleducazione per sabotare una conversazione salutare:\n\n* Sii civile. Non inviare niente che una persona ragionevole possa considerare offensivo, ingiurioso o che inciti all'odio.\n* Non sporcare. Non inviare niente di osceno o\ - \ di sessualmente esplicito.\n* Rispetta tutti. Non offendere né attaccare nessuno, non spacciarti per qualcun altro, non pubblicare informazioni private altrui.\n* Rispetta il nostro forum. Non pubblicare spam o contenuti vandalici.\n\nAnche se le precedenti non sono regole formali con definizioni esatte — evita di pubblicare materiale anche se hai il semplice _sospetto_ che rientri in una di queste categorie. Se non sei sicuro se pubblicare o no, chiediti che cosa succederebbe se il tuo messaggio fosse pubblicato sulla prima pagina de \"Il corriere della sera\".\n\nEssendo questo un forum pubblico, i motori di ricerca indicizzano tutte le nostre discussioni. Mantieni il linguaggio, i tuoi collegamenti e le tue immagini consoni anche per essere visualizzati dai nostri amici e dalle nostre famiglie. \n\n\n\n## [Tieni in ordine](#keep-tidy)\n\nFai il piccolo lo sforzo di mettere tutte le cose al loro posto, in modo tale che possiamo tutti spendere più tempo a discutere e meno tempo a ripulire. Quindi:\n\n* Non iniziare una discussione nella categoria sbagliata.\n* Non inviare lo stesso messaggio in discussioni diverse.\n* Non inviare risposte prive di contenuto.\n* Non andare fuori tema cambiando discorso.\n* Non firmare i tuoi messaggi — ogni messaggio ha già allegate le informazioni del tuo profilo.\n\nPiuttosto che inviare un messaggio \"+1\" oppure \"Sono d'accordo\", usa il bottone \"Mi piace\". Piuttosto che andare fuori tema, usa il bottone \"Rispondi con nuovo argomento\".\n\n\n\n## [Farina del tuo sacco](#stealing)\n\nPer favore non inviare materiale che appartiene a qualcun altro senza il suo permesso. Non inviare descrizioni, collegamenti o metodi per rubare la proprietà intellettuale altrui (software, video, musica, immagini) o che violi qualche altra legge.\n\n\n\n## [Termini del Servizio](#tos)\n\nSì, l'avvocatese è noioso, ma ci dobbiamo proteggere – e, per estensione, proteggere anche te ed i tuoi dati – contro persone non amichevoli. Abbiamo dei [Termini\ - \ di Servizio](/tos) che descrivono il tuo (e il nostro) comportamento ed i diritti che riguardano i contenuti, la privacy e le leggi. Per poter usare questo servizio, devi accettare i nostri [TDS](/tos).\n" + body: "\n\n## [Questo è un luogo civile per discussioni pubbliche](#civilized)\n\nFrequenta questo forum di discussione con lo stesso rispetto che avresti in un parco pubblico. Anche questo è un bene pubblico — un luogo dove condividere competenze, conoscenza e interessi tramite la discussione.\n\nQueste non sono leggi ferree, ma solo un sostegno al giudizio della nostra comunità. Usa queste linee guida per mantenere questo un luogo sereno e piacevole di discorso pubblico civile. \n\n\n\n## [Migliora la discussione](#improve)\n\nAiutaci a migliorare questo luogo cercando sempre di migliorare in qualche modo, anche piccolo, la discussione. Se non sei sicuro che il tuo messaggio possa migliorare la conversazione, ripensa a quel che vuoi dire e riprovaci più tardi. \n\nAbbiamo a cuore gli argomenti che si discutono qui, e vorremmo che stessero a cuore anche a te. Rispetta le discussioni e le persone che vi partecipano, anche quando sei in disaccordo con ciò che viene detto.\n\nUn modo per migliorare la conversazione è scoprire discussioni già in corso. Ti preghiamo di passare qualche tempo a leggere gli altri argomenti prima di rispondere o di iniziare un nuovo argomento, sarà più facile incontrare altre persone coi tuoi stessi interessi. \n\n\n\n## [Sii cortese, anche quando non sei d'accordo](#agreeable)\n\nTalvolta vorresti rispondere a qualcosa con cui non sei d'accordo: fai pure. Ricorda però di _criticare le idee, non le persone_. Per favore evita:\n\n* Insulti.\n* Attacchi personali.\n* Risposte al tono invece che al contenuto di un messaggio.\n* Contraddire per partito preso.\n\nPorta invece delle obiezioni ragionate che migliorino la conversazione.\n\n\n\n## [La tua partecipazione è importante](#participate)\n\nIl clima di questo forum dipende dal tono delle conversazioni. Aiutaci a costruire il futuro di questa comunità partecipando alle conversazioni più costruttive e interessanti — ed evita quelle che non lo sono.\n\nDiscourse ha degli strumenti che\ + \ aiutano la comunità a identificare i migliori (e i peggiori) contributi: preferiti, segnalibri, mi piace, segnalazioni, risposte, modifiche eccetera. Usa questi strumenti per migliorare la tua esperienza e quella di tutti.\n\nCerchiamo di lasciare il parco meglio di come l'abbiamo trovato.\n\n\n\n## [Se vedi un problema, segnalalo](#flag-problems)\n\nI moderatori hanno una speciale autorità e sono responsabili di questo forum. Proprio come te: col tuo aiuto i moderatori possono diventare dei facilitatori della comunità, non semplici guardiani o poliziotti.\n\nSe vedi dei comportamenti scorretti, non rispondere. Incoraggerebbe il comportamento negativo dandogli riconoscimento, esaurirebbe le tue energie e sprecherebbe il tempo di tutti. _Segnalalo e basta_. Se arrivano abbastanza segnalazioni verranno presi provvedimenti, automatici o con l'intervento del moderatore.\n\nPer mantenere la comunità i moderatori si riservano il diritto di rimuovere qualsiasi contenuto e qualsiasi account utente per qualsiasi ragione, in qualsiasi momento. I moderatori non controllano anticipatamente i nuovi messaggi in alcun modo; i moderatori ed i gestori di questo sito non si assumono alcuna responsabilità per i contenuti pubblicati dagli utenti.\n\n\n\n## [Sii sempre civile](#be-civil)\n\nNulla rovina una conversazione come la maleducazione: \n\n - Sii civile. Non pubblicare nulla che una persona possa ragionevolmente considerare offensivo, ingiurioso, discriminatorio, odioso.\n - Sii adulto. Non pubblicare cose oscene o sessualmente esplicite.\n - Rispetto reciproco. Non molestare o minacciare nessuno, non impersonare altre persone, non pubblicare informazioni personali altrui.\n - Rispetta questo forum. Non pubblicare spam e vandalismo\n\nQuesti non sono regolamenti precisi con definizioni esatte — evita addirittura di dare l'_apparenza_ di qualcuno di questi comportamenti di queste cose. Se non sei sicuro, prova a chiederti come ti sentiresti se il tuo messaggio comparisse sulla prima pagina del Corriere della Sera. \n\nQuesto è un\ + \ forum pubblico e viene indicizzato dai motori di ricerca. Fai che il linguaggio, i link e le immagini siano adatti ad amici e familiari.\n\nThis is a public forum, and search engines index these discussions. Keep the language, links, and images safe for family and friends.\n\n\n\n## [Tieni in ordine](#keep-tidy)\n\nSforzati di mettere ogni cosa al posto giusto, così che passiamo più tempo a conversare e meno a riordinare. Quindi per favore:\n\n - Non iniziare un argomento nella categoria sbagliata\n - Non ripubblicare la stessa cosa su più categorie.\n - Non pubblicare risposte vuote.\n - Non deviare un argomento cambiandolo a metà strada.\n - Non firmare i tuoi messaggi — ogni messaggio contiene le informazioni complete sul tuo profilo.\n\nInvece di rispondere “+1” or “D'accordo”, usa il pulsante Mi Piace. Invece di deviare un argomento esistente in una direzione completamente diversa, usa rispondi come argomento collegato.\n\n\n\n## [Pubblica solo cose tue](#stealing)\n\nNon puoi pubblicare alcun materiale digitale che appartenga a qualcun altro senza il suo permesso. Non puoi pubblicare descrizioni, link, metodi che consentano di violare la proprietà intellettuale altrui (software, audio, video, immagini) o di infrangere la legge.\n\n\n\n## [fatto da Te](#power)\n\nQuesto sito è gestito dal tuo [simpatico staff locale](/about) e da *te*, dalla comunità. Se hai altre domande sul funzionamento apri un nuovo argomento nella [ categoria meta](/c/meta) e parliamone! Se c'è un problema urgente e importante che non si può affrontare in un argomento meta o con una segnalazione,contattaci tramite la [pagina staff](/about).\n\n\n\n## [Termini del Servizio](#tos)\n\nIl linguaggio legale è noioso, ma dobbiamo tuterale noi stessi – e per estensione te ed i tuoi dati – contro persone poco amichevoli. Abbiamo dei [Termini di Servizio](/tos) che descrivono il tuo (e nostro) comportamento e i diritti sui contenuti, la privacy e le leggi. Per usare questo servizio devi acconsentire\ + \ ai nostri [TDS](/tos).\n" tos_topic: title: "Termini del Servizio" body: "I seguenti termini e condizioni regolano l’uso del sito web %{company_domain} e di tutti i contenuti, i servizi e i prodotti disponibili direttamente sul sito web e mediante di esso, compresi, ma non limitatamente a, il Software Forum di %{company_domain}, il Forum di Supporto di %{company_domain} e il Servizio Hosting (\"Hosting\") di %{company_domain} (collettivamente chiamati nel seguito il Sito). Il Sito è di proprietà ed è gestito da %{company_full_name} (\"%{company_name}\"). L’uso del Sito è soggetto all’accettazione incondizionata di tutti i termini e le condizioni previsti nel presente documento e di tutte le altre regole operative, le politiche (incluse, senza limitazione, la [Politica sulla Privacy](/privacy) di %{company_domain} e le [Linee guida della comunità](/faq)) e le procedure che possono essere pubblicate di volta in volta su questo Sito da %{company_name} (nel seguito, il “Contratto”).\n\nLeggi il presente Contratto prima di accedere o utilizzare il Sito. L’accesso o l’utilizzo di qualsiasi parte del Sito implica l’accettazione dei termini e delle condizioni del presente contratto. In caso di mancata accettazione di tutti i termini e le condizioni del presente contratto, l’accesso al Sito o l’uso di qualsiasi servizio potrebbe non essere consentito. Se questi termini e condizioni sono considerati un’offerta di %{company_name}, l’accettazione è espressamente limitata ai presenti termini. Il Sito è disponibile solo per gli utenti con almeno 13 anni di età.\n\n\n\n## [1. Il tuo Account su %{company_domain}](#1)\n\nSe crei un account sul Sito, è tua responsabilità mantenere la sicurezza dell’account e sei interamente responsabile di tutte le attività eseguite con l’account. L’utente è tenuto a notificare immediatamente a %{company_name} usi non autorizzati dell’account o altre violazioni della sicurezza. %{company_name} non sarà responsabile per qualsiasi atto od omissione da parte tua, inclusi eventuali danni di qualsiasi tipo derivanti da tali atti o omissioni.\n\n \n\n## [2. Responsabilità dei Collaboratori](#2)\n\nSe\ @@ -1180,3 +1434,5 @@ it: body: "\n\n## [Quali informazioni memorizziamo?](#collect)\nQuando ti registri sul nostro sito memorizziamo alcune informazioni e raccogliamo dati quando partecipi al forum leggendo, scrivendo e valutando il contenuto qui condiviso.\n\nQuando ti registri sul sito, ti potrebbe venir chiesto di inserire il tuo nome e il tuo indirizzo email. Puoi comunque visitare il sito senza registrarti. Il tuo indirizzo email sarà verificato attraverso l’invio di una email che contiene un collegamento univoco. Se il collegamento viene visitato, sapremo che sei tu ad avere il controllo di quell'indirizzo email.\n\nUna volta registrato, quando invii del contenuto noi registriamo l’indirizzo IP da cui il tuo messaggio ha avuto origine. Potremmo anche mantenere dei log sul server che contengono gli indirizzi IP di ogni richiesta effettuata verso il nostro server.\n\n\n\n## [A che ci servono le tue informazioni?](#use)\n\nQualunque tua informazione che memorizziamo può essere usata in uno dei modi seguenti:\n\n * Per personalizzare l’esperienza — tali informazioni ci servono a rispondere meglio alle esigenze individuali.\n * Per migliorare il nostro sito — ci sforziamo continuamente di migliorare il nostro sito usando le informazioni e i ritorni ricevuti.\n * Per inviare email periodiche — l’indirizzo email che fornisci può essere usato per inviarti informazioni, notifiche da te richieste circa modifiche agli argomenti o in risposta al tuo nome utente, rispondere a domande e/o altre richieste o domande.\n\n\n\n## [Come proteggiamo le tue informazioni?](#protect)\n\nImplementiamo diverse misure di sicurezza per salvaguardare le tue informazioni personali, ogni volta che le inserisci, le modifichi o vi accedi.\n\n\n\n## [Qual è la vostra politica di conservazione dei dati?](#data-retention)\n\nCi impegniamo a:\n\n * Conservare i log contenenti gli indirizzi IP di tutte le richieste giunte sui nostri server per non più di 90 giorni.\n * Conservare gli indirizzi IP associati agli utenti registrati\ \ ed ai loro messaggi per non più di 5 anni.\n\n\n\n## [Usate i cookie?](#cookies)\n\nSì. I cookie sono dei piccoli file che un sito copia sul tuo disco fisso attraverso il browser web (se lo permetti). Tali cookie permettono al sito di riconoscere il browser e di associarlo con il tuo account registrato, se presente. \n\nUsiamo i cookie per comprendere e memorizzare le tue preferenze per le visite future e per raccogliere dati aggregati circa il traffico sul sito e le interazioni, in modo da offrire successivamente una migliore esperienza di navigazione e migliori strumenti. Potremmo accordarci con fornitori di servizio terzi che per aiutarci a comprendere meglio i visitatori del nostro sito. Tali fornitori di servizi non hanno il permesso di usare le informazioni raccolte per nostro conto, tranne quando lo scopo è aiutarci a portare avanti e migliorare la nostra attività.\n\n \n\n## [Rivelate informazioni a terze parti?](#disclose)\n\nNon vendiamo, scambiamo né trasferiamo a terze parti le informazioni che ti identificano personalmente. Ciò ad esclusione di quelle parti che ci aiutano gestire il sito, a portare avanti le attività o i servizi, fintanto che tali parti acconsentano a mantenere le informazioni confidenziali. Potremmo inoltre rilasciare le informazioni quando pensiamo che il rilascio sia appropriato per il rispetto della legge, per far rispettare le regole del sito, o per proteggere i nostri o gli altrui diritti, la proprietà o la sicurezza. Tuttavia, informazioni dei visitatori che non personalmente identificanti potrebbero essere fornite ad altri soggetti per fini di marketing, pubblicità, o per altri usi.\n\n\n\n## [Collegamenti con terze parti](#third-party)\n\nA volte, a nostra discrezione, potremmo includere sul nostro sito offerte di prodotti o servizi di terze parti. I siti delle terze parti hanno politiche sulla privacy separate ed indipendenti dalle nostre. Pertanto non ci assumiamo alcuna responsabilità per il contenuto e le attività di tali siti collegati. Tuttavia, cercheremo\ \ di proteggere l'integrità del nostro sito pertanto accettiamo eventuali riscontri negativi sui siti terzi.\n\n\n\n\n## [Rispetto della legge sulla protezione della privacy online dei minori - Children's Online Privacy Protection Act Compliance - COPPA](#coppa)\n\nIl nostro sito, nonché i prodotti ed i servizi ad esso collegati, sono tutti diretti a persone maggiorenni. Se non si è maggiorenni secondo le leggi del proprio Paese, per il rispetto della COPPA ([Legge sulla protezione della privacy online dei minori]( http://en.wikipedia.org/wiki/Children's_Online_Privacy_Protection_Act)), non usare questo sito.\n\n\n\n## [Politica sulla privacy online](#online)\n\nLa presente politica sulla privacy online si applica soltanto alle informazioni raccolte attraverso il nostro sito e non alle informazioni fuori linea.\n\n\n\n## [Consenso](#consent)\n\nUsando il nostro sito, accetti senza riserve la presente politica sulla privacy.\n\n\n\n## [Cambiamenti alla politica sulla privacy](#changes)\n\nQualora decidessimo di cambiare la nostra politica sulla privacy, invieremo tali modifiche su questa pagina.\n\nQuesto documento è CC-BY-SA (http://it.wikipedia.org/wiki/Licenze_Creative_Commons). \nAggiornato il 31 maggio 2013.\n" + static: + search_help: "

Suggerimenti

\n

\n

    \n
  • La ricerca nei titoli è prioritaria, perciò, in caso di dubbi, cerca per titolo
  • \n
  • I risultati migliori si ottengono sempre con parole uniche e non comuni
  • \n
  • Quando possibile, circoscrivi l'ambito della ricerca a una categoria, a un utente o a un argomento in particolare
  • \n
\n

\n

Opzioni

\n

\n \n\n \n \n \n\n
order:viewsorder:latest
status:openstatus:closedstatus:archivedstatus:norepliesstatus:singleuser
category:foouser:foo
in:likesin:postedin:watchingin:trackingin:private
in:bookmarks
\n

\n

\narcobaleni category:parchi status:open order:latest ricercherà gli argomenti contenenti la parola \"arcobaleni\" nella categoria \"parchi\" che non siano chiusi o archiviati, ordinati secondo la data dell'ultimo messaggio.

\n" diff --git a/config/locales/server.ja.yml b/config/locales/server.ja.yml index d38949c0b5..2a5e934260 100644 --- a/config/locales/server.ja.yml +++ b/config/locales/server.ja.yml @@ -6,15 +6,20 @@ # https://www.transifex.com/projects/p/discourse-org/ ja: + stringex: + characters: + number: "-" + i18n: + transliterate: + rule: + ț: "t" + Ț: "t" + ș: "s" + Ș: "s" dates: short_date_no_year: "MMM D" short_date: "MMM D, YYYY" long_date: "MMMM D, YYYY h:mma" - time: - formats: - short: "%m-%d-%Y" - short_no_year: "%B %-d" - date_only: "%b %-d, %Y" title: "Discourse" topics: "トピック" posts: "投稿" @@ -23,12 +28,30 @@ ja: log_in: "ログイン" via: "%{username} via %{site_name}" is_reserved: "is reserved" + disable_remote_images_download_reason: "ディスク容量が不足しているため、リモートでの画像ダウンロードは無効になっています。" errors: messages: too_long_validation: "は、最大文字数(%{max}文字)を超えています。(入力したのは%{length}文字 ) " + invalid_boolean: "無効なboolean." + empty: 本文が未入力です。. + embed: + load_from_remote: "投稿の読み込みに失敗しました。" + bulk_invite: + file_should_be_csv: "アップロードするファイルは、csv または txt 形式である必要があります。" + backup: + operation_already_running: "操作を実行しています。他の操作はできません。" + backup_file_should_be_tar_gz: "バックアップファイルは .tar .gz 形式でなければいけません。" + not_enough_space_on_disk: "このバックアップファイルをアップロードするディスクの空き容量が足りません。" + not_logged_in: "ログインしてください。" + read_only_mode_enabled: "このサイトは読み取り専用モードです。会話は無効になっています。" + too_many_replies: + other: "申し訳ありません、新しいユーザーの同じトピックへの返信は、一時的に %{count} 回に制限されています。" embed: start_discussion: "議論の開始" continue: "議論を続ける" + more_replies: + other: "%{count} 以上の返信" + loading: "会話をロードしています…" permalink: "パーマリンク" in_reply_to: "▶ %{username}" replies: @@ -78,6 +101,7 @@ ja: errors: can_not_modify_automatic: "自動作成グループの変更はできません" default_names: + everyone: "みなさん" admins: "管理者" moderators: "モデレータ" staff: "スタッフ" @@ -117,10 +141,6 @@ ja: is_invalid: "は不正です。もう少し説明を追加してください" has_already_been_used: "は既に使用中です" models: - topic: - attributes: - archetype: - cant_send_pm: "申し訳ありませんが、このユーザにはプライベートメッセージを送信できません。" user: attributes: password: @@ -317,6 +337,8 @@ ja: title: "ブックマーク" xaxis: "日" yaxis: "新規ブックマーク数" + starred: + title: "お気に入り" users_by_trust_level: title: "トラストレベル毎のユーザ" xaxis: "トラストレベル" @@ -374,15 +396,7 @@ ja: s3_config_warning: 'サーバが S3 にファイルをアップロードするように設定されていますが、次のうち少なくとも1つが設定されていません: s3_access_key_id, s3_secret_access_key or s3_upload_bucket。サイトの設定 にて設定を更新してください。詳しくは "How to set up image uploads to S3?" を参考にしてください。' image_magick_warning: 'サーバが大きな画像のサムネイルを作成する設定になっていますが、ImageMagick がインストールされていません。お好きなパッケージマネージャを使って ImageMagick をインストールするか、最新のリリースをダウンロードしてください。' failing_emails_warning: '%{num_failed_jobs}個のメールジョブが失敗しています。config/environments/production.rb ファイルを確認して config.action_mailer の設定が正しいか確認してください。Sidekiq で失敗したジョブを確認。' - default_logo_warning: "サイトのロゴ画像をカスタマイズしていません。サイトの設定において logo_url, logo_small_url, favicon_url を更新してください。" - contact_email_missing: "サイトの連絡先メールアドレスが設定されていません。サイトの設定において contact_email を更新してください。" - contact_email_invalid: "サイトの連絡先メールアドレスが正しくありません。サイトの設定において contact_email を更新してください。" - title_nag: "サイトのタイトルがデフォルトのままになっています。サイトの設定において正しいサイトのタイトルを設定してください。" - site_description_missing: "サイトの site_description 設定が空欄になっています。サイトの設定においてこのフォーラムに関する簡単な説明文を追加してください。" consumer_email_warning: "サイトはメール送信に Gmail (または他のカスタムメールサービス) を利用するように設定されています。Gmail で送信可能なメール数には制限があります。メールを確実に送信するために mandrill.com などのメールサービスプロバイダーのり用を検討してください。" - access_password_removal: "現在サイトは access_password 設定を利用していますが、この設定は廃止されたため代わりに login_required と must_approve_users 設定を利用してください。サイトの設定にて変更ができます。この際、保留中リストのユーザを承認してください。(このメッセージは2日後に削除されます)" - site_contact_username_warning: "site_contact_username 設定が空欄です。サイトの設定にてシステムメッセージの送信者となる管理者のユーザ名を設定してください。" - notification_email_warning: "notification_email 設定が空欄です。サイトの設定にて正しい値を設定してください。" content_types: education_new_reply: title: "新規ユーザ支援: 初めての回答" @@ -428,7 +442,6 @@ ja: ga_tracking_code: "Google analytics のトラッキングコード。例: UA-12345678-9; 参考 http://google.com/analytics" ga_domain_name: "Google analytics のドメイン名。例: mysite.com; 参考 http://google.com/analytics" enable_noscript_support: "noscript タグ経由でアクセスしてきた標準サーチエンジンクローラのサポートを有効にする" - top_menu: "ホームページナビゲーションに表示する項目を指定。例 latest|hot|read|favorited|unread|new|posted|categories" post_menu: "ポストメニューに表示する項目を指定。例 like|edit|flag|delete|share|bookmark|reply" track_external_right_clicks: "右クリックされた外部リンクをトラックする (例: 新しいタブで開く)。URL のリライトを要するためデフォルトでは無効化されています" port: "開発者用! 警告! デフォルトの80番ポートではなく、ここで指定した HTTP ポートを利用する。空欄でデフォルトの80番ポートを利用。" @@ -500,7 +513,6 @@ ja: errors: "%{errors}" not_available: "利用できません。%{suggestion} を試してみては?" something_already_taken: "エラーが発生しました。ユーザ名またはメールアドレスが既に使用中の可能性があります。パスワードリセットを行ってください。" - omniauth_error: "あなたの %{strategy} アカウントの認可に失敗しました。アカウントの認可を許可したか確認してください。" omniauth_error_unknown: "ログインに失敗しました。もう一度試してください。" user: username: @@ -517,11 +529,6 @@ ja: subject_template: "%{invitee_name} あなたを %{site_domain_name}に招待しました。" test_mailer: subject_template: "[%{site_name}] メール送信テスト" - flag_reasons: - off_topic: "あなたの投稿やトピックは「関係ない話題」としてフラグされています。" - inappropriate: "あなたの投稿は「不適切」にフラグされています。誹謗中傷、恫喝、名誉毀損、わいせつ、犯罪行為など他人を不快にさせる内容を含んでいる。" - spam: "あなたの投稿は「スパム」にフラグされています。営利目的、宣伝目的の書き込みの可能性がある。" - notify_moderators: "あなたの投稿は「管理人に通知」にフラグされています。管理人の確認が必要の内容を含んでいる" system_messages: post_hidden: subject_template: "コミュニティによるフラグによりポストが非表示になりました" @@ -635,10 +642,7 @@ ja: upload: unauthorized: "申し訳ありません、ファイルのアップロードが拒否されました (サポートするファイルの拡張子: %{authorized_extensions})." pasted_image_filename: "貼り付けた画像" - attachments: - too_large: "申し訳ありませんが、ファイルが大きすぎてアップロードできません (最大サイズは %{max_size_kb}%kb です)" images: - too_large: "申し訳ありませんが、画像が大きすぎてアップロードできません (最大サイズは %{max_size_kb}%kb です)。リサイズして再アップロードしてください。" fetch_failure: "申し訳ありませんが、画像取得中にエラーが発生しました。" unknown_image_type: "申し訳ありませんが、アップロードしようとしたファイルは画像ではないようです。" size_not_found: "申し訳ありませんが、画像のサイズを判定できませんでした。画像が壊れているかもしれません。" diff --git a/config/locales/server.ko.yml b/config/locales/server.ko.yml index 2b825e7f13..efdd8844a2 100644 --- a/config/locales/server.ko.yml +++ b/config/locales/server.ko.yml @@ -6,15 +6,20 @@ # https://www.transifex.com/projects/p/discourse-org/ ko: + stringex: + characters: + number: "-" + i18n: + transliterate: + rule: + ț: "t" + Ț: "t" + ș: "s" + Ș: "s" dates: short_date_no_year: "MMM D" short_date: "YYYY-MMM-D" long_date: "YYYY-MMM-D a h:mm" - time: - formats: - short: "%Y-%m-%d" - short_no_year: "%B %-d" - date_only: "%Y %b %-d" title: "Discourse" topics: "토픽들" posts: "게시물들" @@ -23,9 +28,13 @@ ko: log_in: "로그인" via: "%{site_name}의 %{username}" is_reserved: "예약됨" + purge_reason: "비활성화된 계정은 자동적으로 삭제됩니다." + disable_remote_images_download_reason: "서버에 저장공간이 부족해서 원격 이미지 저장이 비활성화됨. " errors: + format: '%{attribute} %{message}' messages: too_long_validation: "%{length}자를 입력하셨습니다. 최대 %{max}자까지 입력 가능합니다. " + invalid_boolean: "잘못된 값" embed: load_from_remote: "글 로딩 중 오류가 발생하였습니다." bulk_invite: @@ -181,8 +190,8 @@ ko: base: warning_requires_pm: "개인 메시지를 통해 경고를 보낼 수 있습니다." too_many_users: "한 번에 한 명의 사용자에게 경고를 보낼 수 있습니다." - archetype: - cant_send_pm: "그 사용자에게 개인 메세지를 보낼 수 없습니다." + cant_send_pm: "죄송합니다. 그 사용자에게 개인 메세지를 보낼 수 없습니다." + no_user_selected: "유효한 사용자를 선택해주십시오" user: attributes: password: @@ -309,12 +318,14 @@ ko: activation: action: "계정 활성화" already_done: "죄송합니다. 이 계정 확인 링크는 더 이상 유효하지 않습니다." + please_continue: "계정이 활성화 되었습니다; 홈페이지로 이동합니다." continue_button: "%{site_name}으로 가기" welcome_to: "%{site_name}에 오신것을 환영합니다." approval_required: "이 포럼을 사용하기 위해선 운영자가 수동으로 당신의 새로운 계정을 수락해야 합니다. 계정이 수락이 되면 자동으로 이메일이 발송됩니다." post_action_types: off_topic: title: '오프 토픽' + description: '이 게시물은 이곳의 토픽과 관련된것 같지 않습니다. 다른 토픽으로 옮기거나 새로운 토픽을 시작하는 것이 어떤가요?' long_form: '오프 토픽으로 신고하기' spam: title: '스팸' @@ -325,10 +336,15 @@ ko: description: '이 게시물은 다른 사용자들에게 공격적이나 모욕적 또는 침해적인 글을 담고 있습니다.' long_form: '부적절함으로 신고하기' notify_user: + title: '비공개 메세지 @{{username}}' description: '이 게시물에 내가 작성자와 직접 대화하고 싶은 내용이 있습니다. 신고에서 제외해주세요.' + long_form: '비공개 메세지' email_title: '"%{title}" 내의 당신의 게시글' email_body: "%{link}\n\n%{message}" notify_moderators: + title: "뭔가 다른것" + description: '이 게시물은 리스트에 없는 이유로 관리자의 주의가 필요합니다.' + long_form: '관리자의 주의를 위해 신고' email_title: '"%{title}" 게시물에 대한 운영자의 확인이 필요합니다' email_body: "%{link}\n\n%{message}" bookmark: @@ -355,7 +371,9 @@ ko: 커뮤니티 가이드라인에 맞지 않습니다.' long_form: '부적절함으로 신고하였습니다.' notify_moderators: + title: "뭔가 다른것" description: '이 게시물은 가이드라인, 이용약관, 또는 다른 이유와 관련해서 운영자의 주의가 필요합니다.' + long_form: '관리자의 주의를 위해 신고' email_title: '토픽 "%{title}" 은 운영자의 확인이 필요합니다' email_body: "%{link}\n\n%{message}" flagging: @@ -471,17 +489,10 @@ ko: twitter_config_warning: '당신의 서버는 트위터를 통한 가입을 설정하였습니다(enable_twitter_logins), 그러나 key 와 secret 값이 입력하지 않았습니다. 사이트 설정 에서 업데이트 해주세요. 가이드 읽어보기.' github_config_warning: '당신의 서버는 Github를 통한 가입을 설정하였습니다(enable_github_logins), 그러나 client id 와 secret 값을 입력하지 않았습니다. 사이트 설정 에서 업데이트 해주세요. 가이드 읽어보기.' s3_config_warning: '파일을 Amazon S3로 업로드 하도록 설정하였습니다. 하지만 아직 Access key id(s3_access_key_id), Secret access key(s3_secret_access_key) 혹은 S3 bucket(s3_upload_bucketat) 중 하나를 설정하지 않았습니다. 사이트 세팅에서 설정해주세요. 자세히 알아보기("How to set up image uploads to S3?").' + s3_backup_config_warning: 's3를 이용한 서버 설정이 완료 되지 않았습니다: s3_access_key_id, s3_secret_access_key, s3_secret_access_key 또는 s3_upload_bucket. 사이트 설정 에 가셔서 업데이트 하세요. "How to set up image uploads to S3?" 읽어 보세요.' image_magick_warning: '큰 이미지의 섬네일 만드는 설정이 있지만, ImageMagick가 설치되지 않았습니다. 최신버전 받기.' failing_emails_warning: '%{num_failed_jobs} 개의 이메일 잡이 실패하였습니다. config.action_mailer 설정이 정확한지 config/environments/production.rb 파일을 체크해보세요.' - default_logo_warning: "당신은 사이트의 커스텀 로고를 사용하지 않고 있습니다. logo_url, logo_small_url, 와 favicon_url 를 사이트 설정 에서 설정하세요." - contact_email_missing: "당신의 사이트에 연락가능한 이메일을 설정하지 않았습니다. contact_email 을 사이트 설정 에서 설정해주세요." - contact_email_invalid: "이 사이트의 연락가능한 이메일이 부정확 합니다. contact_email 을 사이트 설정 에서 업데이트 해주세요." - title_nag: "이 사이트의 타이틀이 아직 기본 값 입니다. 사이트 설정 에서 사이트의 타이틀을 업데이트 해주세요." - site_description_missing: "이 사이트의 사이트 설명이 공백입니다. 간단한 설명을 작성해주세요. 사이트 설정." consumer_email_warning: "이메일 전송을 위해 Gmail(또는 다른 커스텀 이메일 서비스)을 사용하고 있습니다. Gmail의 이메일 전송 제한 을 읽어보세요. mandrill.com을 이메일 서비스로 설정하는 것을 고려해보세요." - access_password_removal: "이 사이트는 이미 삭제된 설정인 access_password 을 사용하고 있습니다. 대신 사용해야만 하는 login_required 와 must_approve_users settings 이 활성화 되어있습니다. 사이트 설정 에서 변경할 수 있습니다. 대기중인 사용자 리스트에서 사용자들을 승인하세요. (이 메시지는 2일 뒤 사라집니다.)" - site_contact_username_warning: "사이트의 site_contact_username 설정이 공백입니다. 사이트 설정 에서 업데이트 해주세요. 이 값을 시스템 메시지를 보낼 운영자의 사용자 이름으로 설정하세요." - notification_email_warning: "notification_email 설정이 공백입니다. 사이트 설정에서 업데이트 해주세요." content_types: education_new_reply: title: "새로운 사용자 교육: 첫번째 답글들" @@ -491,6 +502,7 @@ ko: description: "새로운 사용자가 첫 두개의 토픽을 작성하면 자동 팝업됩니다." usage_tips: title: "새로운 사용자 가이드" + description: "새로운 사용자를 위한 가이드와 팁." welcome_user: title: "환영: 새로운 사용자" description: "새로운 사용자가 가입하면 자동으로 개인 메시지를 보냅니다." @@ -511,7 +523,10 @@ ko: description: "모든 페이지 상단에 추가할 HTML(헤더 아래, 네비게이션이나 토픽 제목 위)." bottom: title: "페이지 하단" + description: "모든 페이지 하단에 추가할 HTML" site_settings: + censored_words: "단어는 자동적으로 `■■■■` 로 대체 됩니다." + delete_old_hidden_posts: "30일이 지난 숨겨진 글은 자동으로 삭제됩니다." default_locale: "Discourse 인스턴스가 사용하는 기본 언어 (ISO 639-1 Code)" allow_user_locale: "사용자에게 자신이 원하는 언어를 선택할 수 있는 옵션을 제공한다." min_post_length: "포스트의 최소 글자 수" @@ -526,12 +541,7 @@ ko: allow_duplicate_topic_titles: "같은 제목의 동일한 토픽을 허용한다." unique_posts_mins: "같은 컨텐츠를 다시 포스트 할 수 있는 기간(분)" educate_until_posts: "새로운 사용자가 포스트를 작성할 시 포스트 작성 방법에 대한 교육패널을 보여주는데, 해당 패널이 보여지는 초기 포스트 개수" - title: "title 태그에 쓰일, 이 싸이트의 짧은 제목" - site_description: "이 사이트를 한 문장으로 설명해 주세요. meta description 태그에 사용됩니다." - contact_email: "이 사이트에 대한 주 이메일 주소. discourse.org로부터 중요한 업데이트에 대한 정보가 이 메일 주소로 보내질 것입니다." - company_full_name: "DEPRECATED. Not used anymore and will be removed. The full name of the company that runs this site, used in legal documents like /tos and /privacy" - company_short_name: "DEPRECATED. Not used anymore and will be removed. The full name of the company that runs this site, used in legal documents like /tos and /privacy" - company_domain: "DEPRECATED. Not used anymore and will be removed. The full name of the company that runs this site, used in legal documents like /tos and /privacy" + title: "타이틀 태그에 쓰일 이 사이트의 이름" queue_jobs: "오직 개발자만!! 주의!! By default, queue jobs in sidekiq. If disabled, your site will be broken." crawl_images: "제대로된 넓이와 높이를 추가하기 위해, 원격 URL로 부터 이미지를 회수해온다." download_remote_images_to_local: "이미지를 다운로드하면 원격 이미지를 로컬이미지로 변경한다. 이미지가 깨지는 것을 막을 수 있다." @@ -551,19 +561,21 @@ ko: post_excerpt_maxlength: "게시글 인용에 허용되는 최대 글자수" post_onebox_maxlength: "onebox가 적용된 Discourse 포스트에 허용되는 최대 글자수" onebox_domains_whitelist: "onebox 사용을 허가할 도메일 리스트; 이 도메인은 OpenGraph나 oEmbed를 지원하여야한다. http://iframely.com/debug를 통해 테스트해볼 수 있다." - logo_url: "사이트의 왼쪽 상단에 로고 이미지. 예: http://example.com/logo.png" + logo_url: "로고 이미지는 사이트의 왼쪽 상단에 나타납니다. 만약 왼쪽이 비어있다면 사이트의 이름이 나타납니다." digest_logo_url: "요약 이메일 발송시 사용될 로고. 공백으로 남겨두면 `logo_url`이 사용됩니다. 예: http://example.com/logo.png" - logo_small_url: "토픽 리스트에서 스크롤다운 할 때 사용되는 작은 로고. 예: http://example.com/logo-small.png" + logo_small_url: "작은 로고 이미지는 사이트 왼쪽 상단에 스크롤링할 때 나타납니다. 만일 왼쪽이 빈다면 홈으로 가는 글자가 표시됩니다." favicon_url: "이 사이트의 favicon, 참고 http://en.wikipedia.org/wiki/Favicon" - mobile_logo_url: "모바일 사이트 좌상단에 고정되어 있는 로고 이미지. 만약 설정하지 않는다면 `logo_url`설정 값이 사용된다. eg: http://example.com/uploads/default/logo.png" apple_touch_icon_url: "애플 디바이스는 144px의 아이콘을 사용함. 144px X 144px 사이즈를 추천함" notification_email: "The from: 이 이메일 주소는 모든 기본 시스템 메일을 보내는데 사용됩니다. 여기에 명시된 도메인은 SPF, DKIM가 적용되어 있어야하며, reverse PTR 레코드가 제대로 설정되어 있어야 메일이 도착 할 수 있습니다." email_custom_headers: "커스텀 이메일 해더의 pipe-delimited" + email_subject: "일반 메일을 위한 제목 커스터마이징. 이곳을 참고 https://meta.discourse.org/t/customize-subject-format-for-standard-emails/20801" use_https: "사이트의 사이트의 전체 url((Discourse.base_url)이 http인가 아니면 https인가? 이미 HTTPS를 설정하고 동작중이 아니라면 활성화하지 마세요." summary_score_threshold: "요약본에 포함되기 위한 게시글의 최소 점수 값" summary_posts_required: "하나의 토픽에 대하여 요약본 보기 모드가 활성화되기 전까지 요구되는 최소 게시글 수" summary_likes_required: "하나의 토픽에 대하여 요약본 보기 모드가 활성화되기 전까지 요구되는 최소 좋아요 수" summary_percent_filter: "요약본 보기를 클릭시, 글 중에 몇 %의 상위 글을 보여줄 것인가?" + summary_max_results: "이 주제에 대한 요약 글 최대 갯수" + enable_private_messages: "신뢰도 1 사용자들만 개인 메세지와 개인 메세지에 대한 답글을 허용합니다" enable_long_polling: "Message bus used for notification can use long polling" anon_polling_interval: "How often should anonymous clients poll in milliseconds" auto_track_topics_after: "토픽이 자동으로 추적되기까지의 기본 시간, 사용자에 따라 재정의 될 수 있음.(밀리초, 0 : 항상, -1 : 안함)" @@ -586,19 +598,20 @@ ko: enable_escaped_fragments: "구형의 검색엔진 및 크롤러도 잘 동작하게 하기 위한 동작을 활성화. https://support.google.com/webmasters/answer/174992?hl=en 참고하세요." enable_noscript_support: "당신의 사이트를 검색하기 위한 noscript 태그를 이용한 일반적인 웹 크롤러를 활성화" allow_moderators_to_create_categories: "운영자가 새로운 카테고리를 만드는 것을 허용" - top_menu: "홈페이지 네비게이션에 어떤 아이템이 나올지, 어떤 순서로 나올지 결정한다.예 latest(최신)|new(새로운)|unread(읽지않은)|starred(즐겨찾는)|categories(카테고리)|top(인기)|read(읽은)|posted(작성된)" post_menu: "게시물 메뉴에 어떤 사항들이 무슨 순서로 올라올지 결정합니다. 예: like(좋아요)|edit(수정)|flag(신고)|delete(삭제)|share(공유)|bookmark(북마크)|reply(답글)" share_links: "공유 다이얼로그에 어떤 아이템이 어떤 순서로 나올지 결정" track_external_right_clicks: "우클릭한 외부 링크를 추적한다 (에: 새 탭 열기) 이 설정을 위해선 해당 URL를 다시 써야하는 이유로 기본값은 비활성화이다." - topics_per_page: "토픽 리스트에서 기본으로 보여지는 토픽 수와 스크롤로 인해 더 많은 토픽들이 불려올 때의 수" send_welcome_message: "신규 사용자에게 가입 환영 메세지를 보냅니까?" suppress_reply_directly_below: "게시글의 바로 아래에 단 하나의 댓글만 있는 경우 '댓글 수'를 보여주지 않음" suppress_reply_directly_above: "단 하나의 댓글 위의 게시글이 하나 있는 상황에서 '~에 대한 댓글'을 보여주지 않음." + max_reply_history: "덧글 확장해서 보여지는 최대 갯수" topics_per_period_in_top_summary: "인기 토픽 요약에 기본으로 보여질 토픽 수" topics_per_period_in_top_page: "인기 토픽에서 '더 보기'를 요청할 시 보여질 토픽 수" redirect_users_to_top_page: "자동으로 신규 사용자와 오래간만에 들어온 사용자를 탑 페이지로 리다이렉트 시킴" + show_email_on_profile: "사용자 프로필에서 자신의 이메일 주소 보기( 오직 본인과 관리자만 볼 수 있음)" email_token_valid_hours: "비밀번호 찾기, 계정 활성화에 사용되는 토큰의 유효 기간(시간)" email_token_grace_period_hours: "비밀번호 찾기, 계정 활성화에 사용되는 토큰은 사용되어진 이후 유효한 기간(시간)" + enable_badges: "훈장 시스템 활성화" allow_index_in_robots_txt: "이 사이트가 검색엔진에 의해 인덱스되는 것을 허용합니다.(robots.txt 수정)" email_domains_blacklist: "허용하지 않는 Email 도메인을 파이프(|)기호로 구분하여 입력. 예: mailinator.com|trashmail.net" email_domains_whitelist: "허용하는 Email 도메인을 파이프(|)기호로 구분하여 입력. 경고: 여기에 주어진 email domain이 아닌 경우 모두 거부됩니다." @@ -611,9 +624,7 @@ ko: login_required: "게시글을 읽으려면 인증(로그인)이 필요함" min_password_length: "비밀번호 최소 글자수." block_common_passwords: "가장 흔히 사용되는 10,000개 비밀번호 목록에 있는 비밀번호를 사용하는 것을 허용하지 않음." - enable_sso: "외부 사이트를 통한 SSO(Single Sign On)을 허용. (초대 기능을 사용할 수 없음)" sso_url: "SSO endpoint의 URL" - sso_secret: "SSO 정보를 encrypt/decrypt할 때 사용할 문자열. (10글자 이상이어야 함)" sso_overrides_email: "내부에서 사용되는 이메일을 SSO로부터 부여받은 외부 이메일로 사용. (주의 : 내부에서 사용되는 이메일 표준화로 인해 불일치가 발생할 수 있)" sso_overrides_username: "내부에서 사용되는 사용자 이름을 SSO로부터 부여받은 외부 사용자 이름으로 사용. (주의 : 내부에서 사용되는 사용자 이름의 길이/요구조건의 차이점 때문에 불일치가 발생할 수 있)" sso_overrides_name: "내부에서 사용되는 이름을 SSO로부터 부여받은 외부 이름으로 사용. (주의 : 내부에서 사용되는 이름의 표준화로 인해 불일치가 발생할 수 있)" @@ -636,8 +647,10 @@ ko: allow_restore: "데이터 복원을 허용한다. 이 사이트의 모든 데이터가 변경될 수 있다. 백업이나 복원 계획이 없다면 비활성화로 놔둔다." maximum_backups: "디스크에 유지할 최대 백업 개수. 오래된 백업순으로 자동으로 삭제된다." backup_daily: "하루에 한 번 자동으로 백업한다." + enable_s3_backups: "백업이 완료되면 Amazon S3로 업로드한다. s3 credentials을 기입되어있는지 확인해야한다." s3_backup_bucket: "백업본을 유지할 s3 버켓 이름. 주의 : 프라이빗 버켓인지 확인해야한다." active_user_rate_limit_secs: "'last_seen_at'을 업데이트 하는 주기(초)" + verbose_localization: "UI에 언어 팁을 확장해 보여줌" previous_visit_timeout_hours: "최근 방문한 시간을 저장할 주기(시간)" rate_limit_create_topic: "사용자가 토픽을 생성하고 난 후 다음 토픽을 생성할 때 까지의 대기 기간(초)" rate_limit_create_post: "사용자가 포스팅을하고 난 후 다음 포스팅까지 대기 기간(초)" @@ -647,20 +660,42 @@ ko: max_flags_per_day: "사용자가 하루동안 할 수 있는 최대 신고 개수" max_bookmarks_per_day: "사용자가 하루동안 할 수 있는 최대 북마크 개수" max_edits_per_day: "사용자가 하루동안 할 수 있는 최대 편집 수" - max_stars_per_day: "사용자당 하루동안 즐겨찾기 할 수 있는 최대 토픽 개수" max_topics_per_day: "사용자가 하루동안 생성할 수 있는 최대 토픽 개수" max_private_messages_per_day: "사용자가 하루동안 생성할 수 있는 개인 메시지 개수" + max_invites_per_day: "하루에 보낼 수 있는 초대장의 최대치입니다." suggested_topics: "토픽 아랫부분에 몇개의 토픽을 제안하여 보여준다." limit_suggested_to_category: "제안된 토픽은 현재의 카테고리에 있는 토픽 중에서만 보여준다." clean_up_uploads: "불법 호스팅을 막기 위해서 참조되지 않은 업로드 파일은 제거한다. 주의 : 이 설정을 활성화 하기 전에 `/uploads` 디렉토리를 백업하는 것이 좋다." clean_orphan_uploads_grace_period_hours: "참조되지 않은 업로드 파일을 제거하기 전 기간(시간)" purge_deleted_uploads_grace_period_days: "참조되지 않은 업로드 파일을 완전 삭제하지 전 기간(일)" + purge_unactivated_users_grace_period_days: "유예기간(하루) 동안 활성화하지 않으면 계정이 삭제됩니다." s3_access_key_id: "이미지를 업로드 할 때 사용할 Amazon S3의 access key id" s3_secret_access_key: "이미지를 업로드 할 때 사용할 Amazon S3의 secret access key" s3_region: "이미지를 업로드 할 때 사용할 Amazon S3 region" enable_flash_video_onebox: "swf, flv(어도비 플래쉬)링크를 embed 할 수 있도록 함. 주의: 보안에 대한 위험성을 알려주는 것이 좋다." default_invitee_trust_level: "사용자를 초대하기 위한 기본 신뢰도(0-4)" default_trust_level: "새로운 사용자의 기본 신뢰도(0-4)" + tl1_requires_topics_entered: "새로운 사용자가 신뢰받는 사용자-1 이 되기 위해 들어가봐야되는 토픽의 개수" + tl1_requires_read_posts: "새로운 사용자가 신뢰받는 사용자-1 가 되기 위해 읽어야 하는 포스트 개수" + tl1_requires_time_spent_mins: "새로운 사용자가 신뢰받는 사용자-1 이 되기 위해 몇 분 동안 포스트를 읽어야 하는지." + tl2_requires_topics_entered: "기본 사용자가 자주오는 사용자-2 가 되기 위해 들어가 봐야 되는 토픽 갯수" + tl2_requires_read_posts: "기본 사용자가 자주오는 사용자-2 가 되기 위해 읽어야 하는 토픽 갯수" + tl2_requires_time_spent_mins: "기본 사용자가 자주오는 사용자-2 가 되기 위해 몇 분 동안 포스트를 읽어야 하는지." + tl2_requires_days_visited: "기본 사용자가 자주오는 사용자-2 가 되기 위해 몇 일을 방문해야 하는지." + tl2_requires_likes_received: "기본 사용자가 자주오는 사용자-2 가 되기 위해 받아야 하는 좋아요 갯수" + tl2_requires_likes_given: "기본 사용자가 자주오는 사용자-2 가 되기 위해 눌러야하는 좋아요 수" + tl2_requires_topic_reply_count: "기본 사용자가 자주오는 사용자-2 가 되기 위해 달아야 하는 덧글 수" + tl3_requires_days_visited: "VIP 사용자-3 이 되기 위해 지난 100일 동안 최소한 사이트에 방문해야 하는 수 (0 ~ 100)" + tl3_requires_topics_replied_to: "VIP 사용자-3 이 되기 위해 지난 100일 동안 답글을 달아야하는 최소 토픽수(0 ~ )" + tl3_requires_topics_viewed: "VIP 사용자-3 이 되기 위해 지난 100일 동안 생성된 전체 토픽 중 본 토픽의 비율(%, 0 ~ 100)" + tl3_requires_posts_read: "VIP 사용자-3 이 되기 위해 지난 100일 동안 생성된 전체 포스트 중 본 포스트의 비율(%, 0 ~ 100)" + tl3_requires_topics_viewed_all_time: "VIP 사용자-3 이 되기 위해 꼭 보아야 하는 토픽의 전체 개수" + tl3_requires_posts_read_all_time: "VIP 사용자-3 이 되기 위해 꼭 보아야 하는 토픽의 전체 개수" + tl3_requires_max_flagged: "사용자가 VIP 사용자-3 이 되기 위해 지난 100일동안 서로 다른 사용자에게 최소한으로 받지 말아야하는 신고된 포스트 수(0 ~ )" + tl3_promotion_min_duration: "신뢰도가 2로 떨어진 후 다시 VIP 사용자-3 이 될 수 있는 최소 일 수" + tl3_requires_likes_given: "VIP 사용자-3 이 되기 위해 지난 100일 동안 해야 할 좋아요 수" + tl3_requires_likes_received: "VIP 사용자-3 이 되기 위해 지난 100일 동안 받아야 할 좋아요 수" + tl3_links_no_follow: "VIP 사용자-3 의 글에 있는 링크에서 rel=nofollow 를 제거하지 마시오." min_trust_to_create_topic: "새로운 토픽을 생성하기 위한 최소 신뢰도" min_trust_to_edit_wiki_post: "위키로 설정된 포스트를 수정할 수 있는 최소 신뢰도" newuser_max_links: "새로운 사용자가 포스트에 붙일 수 있는 최대 링크 개수" @@ -685,6 +720,12 @@ ko: authorized_extensions: "파일 업로드에 허용되는 확장자 리스트 '*'을 사용하면 모든 타입의 파일이 가능하다." max_similar_results: "새로운 토픽을 작성할 때, 에디터 위에 보여줄 비슷한 토픽들의 개수. 제목과 본문을 바탕으로 비교한다." title_prettify: "일반적인 제목의 오타 및 오류를 수정해준다. 모두 대문자로 쓰거나, 첫자가 소문자이거나(영문), 복수의 !, ? 혹은 마침표(.)가 중복으로 들어간 것 등" + topic_views_heat_low: "토픽이 연하게 하이라이트 되기 위한 조회수" + topic_views_heat_medium: "토픽이 적당하게 하이라이트 되기 위한 조회수" + topic_views_heat_high: "토픽이 진하게 하이라이트 되기 위한 조회수" + cold_age_days_low: "대화가 몇 일 이상 지난 후, 마지막 활동 날짜를 연하게 하이라이트" + cold_age_days_medium: "대화가 몇 일 이상 지난 후, 마지막 활동 날짜를 적당히 하이라이트" + cold_age_days_high: "대화가 몇 일 이상 지난 후, 마지막 활동 날짜를 강하게 하이라이트" faq_url: "FAQ주소가 있으면 전체 URL을 적어주세요." tos_url: "이용약관이 있으면 전체 URL을 적어주세요." privacy_policy_url: "개인정보 보호가 있으면 전체 URL을 적어주세요." @@ -694,6 +735,12 @@ ko: levenshtein_distance_spammer_emails: "스패머 메일을 체크할 때, 허용할 다른 글자 개수(fuzzy match)" reply_by_email_enabled: "이메일을 통해 토픽에 댓글을 달 수 있음." reply_by_email_address: "이메일 주소로 답글을 다는 템플릿. 예: %{reply_key}@reply.myforum.com" + pop3_polling_enabled: "POP3를 이용한 이메일 설문조사" + pop3_polling_period_mins: "이메일을 받기 위해 POP3S 계정을 체크하는 분 단위 NOTE: 재시동 필요." + pop3_polling_port: "이메일 설문조사를 위한 POP3 포트번호 " + pop3_polling_host: "이메일 설문조사를 위한 POP3 호스트" + pop3_polling_username: "이메일 설문조사를 위한 POP3 사용자 계정 이름" + pop3_polling_password: "이메일 설문조사를 위한 POP3 사용자 비밀번호" email_in: "이메일을 통해 새로운 토픽을 포스팅할 수 있도록 허가한다.(POP3 polling 필요) 각 카테고리의 \"Setting\" 탭에서 주소를 설정한다." email_in_min_trust: "이메일을 통해 새 토픽을 포스팅 할 수 있는 최소 사용자 신뢰도" email_prefix: "이메일 제목에 쓰일 [라벨]. 설정하지 않으면 기본적으로 'title(필수 설정의)' 이 된다." @@ -716,12 +763,15 @@ ko: public_user_custom_fields: "A whitelist of custom fields for a user that can be shown publically." allow_profile_backgrounds: "사용자에게 프로필 배경 이미지 업로드를 허용한다." enable_mobile_theme: "모바일 디바이스는 모바일 환경에 친화적인 테마를 사용합니다, 그리고 PC용 화면으로 전환할 수 있습니다. 만약 커스텀 스타일 시트를 사용한다면 이것을 비활성화 시키세요." + dominating_topic_minimum_percent: "한 토픽에서 한 사용자의 영향력을 결정하는 게시글 수의 퍼센트" suppress_uncategorized_badge: "토픽 리스트에서 카테고리가 없는 토픽에 대한 훈장을 보여주지 않는다." - enable_names: "사용자의 전체 이름을 보여준다." display_name_on_posts: "포스트에 @username 뿐만 아니라 사용자의 전체 이름도 보여준다." + invites_per_page: "사용자 페이지에 방문자를 표시합니다." short_progress_text_threshold: "토픽의 포스트 개수가 이 값을 넘어서면, 포스트 프로그래스바는 오직 현재 포스트 넘버만 보여준다. 만약 포스트 프로그래스바의 넓이는 변경하면, 이 숫자도 변경해야한다." default_code_lang: "기본 programming language syntax highlighting은 GitHub code blocks이 적용된다. (lang-auto, ruby, python etc.)" + warn_reviving_old_topic_age: "이 값보다 오래된 토픽에 답글을 달면, 오래된 토론이라는 것을 상기시키기 위해 주의 알림이 보여진다. 비활성화 값음 0 이다. " autohighlight_all_code: "형식이 지정되기 전의(되지 않은) 모든 코드 블럭들에 대해, 사용자가 언어를 지정하지 않아도 강제로 code highlighting이 적용된다." + embed_post_limit: "포스트 개수가 최대치입니다." errors: invalid_email: "유효하지 않은 이메일 주소입니다." invalid_username: "해당 이름의 사용자가 없습니다." @@ -753,6 +803,9 @@ ko: category: '카테고리' topic: '검색 결과' user: '사용자' + sso: + unknown_error: "업데이트 정보에 오류가 있습니다. 사이트 관리자에게 문의해주세요." + timeout_expired: "로그인 연결 시간이 초과되었습니다. 다시한번 시도해주세요" original_poster: "원본 게시자" most_posts: "최대 게시자" most_recent_poster: "최근 게시자" @@ -767,6 +820,9 @@ ko: other: "%{count}개의 글을 기존 토픽으로 옮겼습니다: %{topic_link}" change_owner: post_revision_text: "소유권이 %{old_user}에서 %{new_user}으로 이전되었습니다." + emoji: + errors: + name_already_exists: "죄송합니다. '%{name}'은/는 이미 다른 이모티콘이 사용하고 있습니다." topic_statuses: archived_enabled: "이 토픽은 보관되었고 더이상 변경하실 수 없습니다." archived_disabled: "이 토픽의 보관이 풀렸고 이제 변경하실 수 있습니다." @@ -781,7 +837,7 @@ ko: autoclosed_enabled_lastpost_days: other: "이 토픽은 마지막 답글이 달리고 %{count}일 뒤 자동적으로 닫혔습니다. 새로운 답글을 다실 수 없습니다." autoclosed_enabled_lastpost_hours: - other: "이 토픽은 마지막 답글이 달리고 %{count}시간 뒤 자동적으로 닫혔습니다. 새로운 답글을 다실 수 없습니다." + other: "마지막 댓글로부터 %{count} 시간이 지나 토픽이 자동으로 닫혔습니다. 새로운 댓글을 다실 수 없습니다." autoclosed_enabled_lastpost_minutes: other: "이 토픽은 마지막 답글이 달리고 %{count}분 뒤 자동적으로 닫혔습니다. 새로운 답글을 다실 수 없습니다." autoclosed_disabled: "이 토픽은 이제 열렸습니다. 새로운 답글을 허용합니다." @@ -798,14 +854,19 @@ ko: wait_approval: "가입해 주셔서 감사합니다. 곧 활성화 메일이 도착할 것 입니다." active: "당신의 계정이 활성화되었습니다." not_activated: "당신은 아직 로그인 할 수 없습니다. 계정 활성화 이메일을 보냈습니다. 계정을 활성화하기 위해 지침을 따라주세요." + not_allowed_from_ip_address: "이 IP 주소에서 %{username} 으로 로그인 할 수 없습니다." suspended: "당신은 %{date} 까지 로그인할 수 없습니다. " suspended_with_reason: "당신은 %{date} 까지 로그인할 수 없습니다. 이유: %{reason}" errors: "%{errors}" not_available: "불가능합니다. %{suggestion}" something_already_taken: "뭔가가 이상합니다. 아마도 계정의 아이디 또는 이메일이 이미 등록된것 같습니다. 비밀번호 찾기 링크를 이용해주세요." - omniauth_error: "죄송합니다, 당신의 계정을 인증하는데 문제가 있습니다. 이미 인증이 되었나요?" omniauth_error_unknown: "로그인에 문제가 있습니다. 다시 시도해주세요." + new_registrations_disabled: "지금은 새로 가입할 수 없습니다." + password_too_long: "비밀번호는 200글자 이내만 허용됩니다." + missing_user_field: "사용자 정보 입력을 덜 끝마쳤습니다." + close_window: "사용자 인증 완료. 계속하기 위해 브라우저 창을 닫으세요." user: + no_accounts_associated: "연계된 계정이 없음" username: short: "최소 %{min}자 이상이어야 합니다." long: "%{max}자 이상 사용할 수 없습니다." @@ -818,25 +879,72 @@ ko: blocked: "는 허용되지 않습니다" ip_address: blocked: "는 블락되었습니다." + invite_forum_mailer: + subject_template: "%{site_domain_name}의 %{invitee_name}님께서 당신을 초대하였습니다." + text_body_template: | + %{invitee_name} 님께서 당신을 초대하였습니다. + + > **%{site_title}** + > + > %{site_description} + + 초대를 허락하시려면 아래 링크를 클릭하여 주세요. + + %{invite_link} + + 당신은 신뢰받는 사용자로부터 초대를 받으셨습니다. 그래서 로그인 하지 않아도 됩니다. + invite_password_instructions: + subject_template: "%{site_name} 계정을 위해 비밀번호 설정" test_mailer: subject_template: "[%{site_name}] 이메일 발송 테스트" + new_version_mailer: + subject_template: "[%{site_name}] 새 Discourse 버전으로 업데이트 가능." + new_version_mailer_with_notes: + subject_template: "[%{site_name}] 업데이트 가능." + flags_reminder: + please_review: "후기를 써주세요." + post_number: "게시글" + flags_dispositions: + agreed: "알려줘서 감사합니다. 문제가 있음에 동의하며 내용을 살펴보겠습니다." + agreed_and_deleted: "알려줘서 감사합니다. 문제가 있음에 동의하며 해당 글을 삭제했습니다." + disagreed: "알려줘서 감사합니다. 살펴보겠습니다." + deferred: "알려줘서 감사합니다. 살펴보겠습니다." + deferred_and_deleted: "알려줘서 감사합니다. 해당 글을 삭제했습니다." system_messages: post_hidden: subject_template: "포럼 신고로 인한 게시글 숨김" welcome_user: subject_template: "%{site_name} 사이트에 오신것을 환영합니다!" - text_body_template: | - %{site_name} 사이트에 오신것을 환영합니다! - - %{new_user_tips} - - 저희는 [커뮤니티 행동 방침](%{base_url}/guidelines)을 잘 따라주실 것을 믿습니다. - - 다시한번 환영합니다! welcome_invite: subject_template: "%{site_name} 사이트에 오신것을 환영합니다!" + backup_succeeded: + subject_template: "백업 성공" backup_failed: subject_template: "백업에 실패했습니다." + restore_succeeded: + subject_template: "복원 성공" + text_body_template: "복원에 성공했습니다." + restore_failed: + subject_template: "복원 실패" + csv_export_succeeded: + subject_template: "데이터 추출이 완료되었습니다." + csv_export_failed: + subject_template: "데이터 추출 실패" + text_body_template: "죄송합니다. 데이터 추출에 실패하였습니다. 로그를 확인하시거나 관리자에게 문의해주세요." + email_reject_no_account: + subject_template: "이메일 문제 -- 모르는 계정" + email_reject_empty: + subject_template: "이메일 문제 -- 컨텐츠 없음" + email_reject_post_error: + subject_template: "이메일 문제 -- 글쓰기 에러" + email_reject_post_error_specified: + subject_template: "이메일 문제 -- 글쓰기 에러" + email_reject_destination: + subject_template: "이메일 오류 -- 잘못된 주소" + email_reject_topic_not_found: + subject_template: "이메일 오류 -- 주제를 찾을 수 없습니다" + email_reject_topic_closed: + subject_template: "이메일 오류 -- 토픽이 닫힘" too_many_spam_flags: subject_template: "새로운 계정은 블락되었습니다." blocked_by_staff: @@ -857,6 +965,8 @@ ko: [운영자 색션에서 새로운 사용자들을 리뷰하세요](/admin/users/list/pending). unsubscribe_link: "만약 구독해지를 원하시면 [사용자 환경설정](%{user_preferences_url})을 방문하세요." + subject_re: "덧: " + subject_pm: "[PM] " user_notifications: previous_discussion: "이전 답글" unsubscribe: @@ -901,6 +1011,15 @@ ko: %{context} + --- + %{respond_instructions} + user_posted_pm: + subject_template: "[%{site_name}] [PM] %{topic_title}" + text_body_template: | + %{message} + + %{context} + --- %{respond_instructions} digest: @@ -911,6 +1030,7 @@ ko: click_here: "클릭" from: "%{site_name} 요약" read_more: "더 읽기" + more_topics_category: "더 많은 새글:" posts: other: "%{count} 글" forgot_password: @@ -920,6 +1040,10 @@ ko: 아래 링크를 클릭하여 패스워드를 재설정하세요: %{base_url}/users/password-reset/%{email_token} + set_password: + subject_template: "[%{site_name}] 패스워드 재설정" + account_created: + subject_template: "[%{site_name}] 당신의 새 계정" authorize_email: subject_template: "[%{site_name}] 이메일 확인" text_body_template: | @@ -939,20 +1063,57 @@ ko: 만약 위의 링크를 클릭 할 수 없으면 주소를 복사하여 당신의 웹브라우저에 붙여넣으세요. page_not_found: title: "당신이 요청한 페이지를 찾을 수 없습니다. 혹시 아래와 같은 토픽을 찾으신 것은 아닌가요?" + popular_topics: "인기" + recent_topics: "최근" + see_more: "더" + search_title: "이 사이트 검색" + search_google: "Google" + login_required: + welcome_message: | + #[%{title}에 오신 것을 환영합니다.](#welcome) + 계정이 필요합니다. 회원가입 또는 로그인 해주세요 terms_of_service: + title: "서비스 이용약관" signup_form_message: 'I have read and accept the Terms of Service.' deleted: '삭제되었습니다' upload: + edit_reason: "이미지를 다운로드 합니다." unauthorized: "업로드 하시려는 파일 확장자는 사용이 불가능합니다 (사용가능 확장자: %{authorized_extensions})." pasted_image_filename: "게시된 이미지" - attachments: - too_large: "업로드하려는 파일의 크기가 너무 큽니다 (최대 크기는 %{max_size_kb}%kb 입니다)." images: - too_large: "업로드하려는 이미지의 크기가 너무 큽니다. (최대 크기는 %{max_size_kb}%kb 입니다), 사이즈를 조정하고 다시 시도해보세요." fetch_failure: "죄송합니다. 이미지 업로드에 오류가 있습니다." unknown_image_type: "죄송합니다. 당신은 이미지로 보이지 않는 파일을 업로드 하려고 하였습니다." size_not_found: "죄송합니다. 이미지 사이즈가 잘못 되었습니다. 혹시 깨진 이미지가 아닌가요?" flag_reason: sockpuppet: "새로운 사용자가 토픽을 만들었습니다. 그리고 같은 IP를 사용하는 새 사용자가 답글을 달았습니다. 사이트 설정의 flag_sockpuppets를 확인하세요." + email_log: + no_user: "ID %{user_id} 인 사용자를 찾을 수 없습니다" + suspended_not_pm: "사용자는 정지되어 메세지 불가" + seen_recently: "최근 보여진 사용자" + post_not_found: "해당하는 ID %{post_id} 을 찾을 수 없습니다. " + notification_already_read: "이 알림 이미 읽은 것에 대한 이메일 입니다." + topic_nil: "글 제목이 없음" + post_deleted: "글쓴이에 의해 삭제된 글" + user_suspended: "정지된 사용자" + already_read: "이미 읽은 글" + message_blank: "메세지가 비어있음" + message_to_blank: "메세지가 비어있음" + text_part_body_blank: "본문이 비어있음" + body_blank: "본문이 비어있음" color_schemes: base_theme_name: "기본 테마 색상" + guidelines: "가이드라인" + privacy: "개인정보 취급방침" + edit_this_page: "이 페이지 수정" + csv_export: + boolean_yes: "네" + boolean_no: "아니오" + static_topic_first_reply: |+ + 이 토픽인 %{page_name} 페이지의 첫글 수정. + + guidelines_topic: + title: "FAQ/가이드" + tos_topic: + title: "서비스 이용약관" + privacy_topic: + title: "개인정보취급방침" diff --git a/config/locales/server.nl.yml b/config/locales/server.nl.yml index f38aa59ec2..d353827ee7 100644 --- a/config/locales/server.nl.yml +++ b/config/locales/server.nl.yml @@ -17,11 +17,6 @@ nl: short_date_no_year: "D MMM" short_date: "D MMM YYYY" long_date: "D MMMM YYYY H:mm" - time: - formats: - short: "%d-%m-%Y" - short_no_year: "%-d %B" - date_only: "%-d %b %Y" title: "Discourse" topics: "Topics" posts: "berichten" @@ -30,7 +25,6 @@ nl: log_in: "Inloggen" via: "%{username} via %{site_name}" is_reserved: "is gereserveerd" - purge_reason: "Automatisch verwijderd omdat het oud en niet geverifieerd is" errors: messages: too_long_validation: "is beperkt tot %{max} tekens; je gebruikt %{length}." @@ -191,8 +185,6 @@ nl: base: warning_requires_pm: "Je kunt alleen waarschuwingen aan privéberichten toevoegen." too_many_users: "Je kunt een waarschuwing per keer slechts naar één ander lid sturen." - archetype: - cant_send_pm: "Sorry, je kan geen privébericht sturen naar deze persoon." user: attributes: password: @@ -495,15 +487,7 @@ nl: s3_config_warning: 'De server is geconfigureerd om bestanden naar S3 te uploaden, maar tenminste een van de volgende instellingen is niet opgegeven: s3_access_key_id, s3_secret_access_key of s3_upload_bucket. Ga naar de Instellingen en vul de waarden in. Zie "How to set up image uploads to S3?" voor meer informatie.' image_magick_warning: 'De server is geconfigureerd om thumbnails te maken van grote afbeeldingen, maar ImageMagick is niet geïnstalleerd. Installeer ImageMagick met je favoriete package manager of download de laatste release.' failing_emails_warning: 'Er zijn %{num_failed_jobs} mislukte emailtaken. Check of de instellingen voor config.action_mailer in het bestand config/discourse.conf kloppen. Bekijk alle mislukte Sidekiq jobs.' - default_logo_warning: "Je hebt nog niet een eigen logo ingesteld voor je site. Werk logo_url, logo_small_url, en favicon_url bij in de Instellingen." - contact_email_missing: "Je hebt nog geen contactadres opgegeven voor je site. Werk contact_email bij in de Instellingen." - contact_email_invalid: "Je hebt een ongeldig contactadres opgegeven voor je site. Werk contact_email bij in de Instellingen." - title_nag: "Je hebt nog geen title ingesteld voor je site. Geef een titel voor je site op in de Instellingen." - site_description_missing: "Er is nog geen omschrijving van deze site. Schrijf een korte omschrijving in de Instellingen" consumer_email_warning: "Je site is ingesteld om Gmail te gebruiken voor het versturen van mails. Gmail heeft limieten voor het aantal mails dat je kan versturen. Overweeg om een andere e-mailprovider te gebruiken om er zeker van te zijn dat mails aankomen." - access_password_removal: "Je site gebruikte een toegangswachtwoord (access_password) setting, maar die optie is uit Discourse verwijderd. De login_required en must_approve_users instellingen zijn er voor in de plaats gekomen. Je kan ze in de Instellingen aanpassen. Zorg er voor dat je gebruikers op de wachtlijst accepteert. (Dit bericht verdwijnt na twee dagen.)" - site_contact_username_warning: "De instelling site_contact_username is leeg. Werk deze bij in de Instellingen. Stel het in op de gebruikersnaam van een admin die als afzender van de systeemberichten zal worden wordt gebruikt." - notification_email_warning: "De instelling notification_email is leeg. Werk deze bij in de Instellingen." content_types: education_new_reply: title: "Uitleg voor nieuw lid: Eerste reacties" @@ -547,12 +531,6 @@ nl: allow_uncategorized_topics: "Sta topics zonder categorie toe" allow_duplicate_topic_titles: "Sta topics met dezelfde titels toe" unique_posts_mins: "Hoeveel minuten iemand moet wachten voordat deze een bericht met dezelfde inhoud mag plaatsen" - title: "Korte titel van deze website, wordt gebruikt in de titeltag" - site_description: "Omschrijf dit forum in een zin. Deze wordt getoond in de meta omschrijvingstag." - contact_email: "E-mailadres van een contactpersoon van deze site. Belangrijke updates van Discourse.org worden naar dit adres gestuurd." - company_full_name: "ACHTERHAALD. Wordt niet meer gebruikt en zal verwijderd worden. De volledige naam van het bedrijf dat deze site draait. Wordt gebruikt in juridische delen van de site, zoals /tos en /privacy" - company_short_name: "ACHTERHAALD. Wordt niet meer gebruikt en zal verwijderd worden. De korte naam van het bedrijf dat deze site draait. Wordt gebruikt in juridische delen van de site, zoals /tos en /privacy" - company_domain: "ACHTERHAALD. Wordt niet meer gebruikt en zal verwijderd worden. De domeinnaam van het bedrijf dat deze site draait. Wordt gebruikt in juridische delen van de site, zoals /tos en /privacy" queue_jobs: "DEVELOPERS ONLY! WARNING! Zet verschillende taken in een queue binnen sidekiq, bij 'false' worden taken ineens uitgevoerd" crawl_images: "Haal afbeeldingen van externe URLs om de correcte breedte- en hoogtedimensies in te voegen" download_remote_images_to_local: "Download externe afbeeldingen en sla ze lokaal op. Dit voorkomt dat afbeeldingen niet meer beschikbaar zouden kunnen worden." @@ -579,7 +557,6 @@ nl: ga_universal_domain_name: "Google Universal Analytics (analytics.js) domeinnaam, bijv. mijnsite.nl; zie http://google.com/analytics" enable_noscript_support: "Ondersteun <noscript> tag" allow_moderators_to_create_categories: "Moderators mogen nieuwe categorieën maken" - top_menu: "De volgorde en selectie van items in het hoofdmenu. Bijvoorbeeld latest|new|unread|starred|categories|top|read|posted" post_menu: "De volgorde en selectie van items in het berichtmenu. Bijvoorbeeld like|edit|flag|delete|share|bookmark|reply" track_external_right_clicks: "Houd bij welke externe links met de rechtermuisknop worden aangeklikt (bijv: open in nieuwe tab). Dit staat standaard uit, gezien het de URLs moet herschrijven, waardoor het gebruiksgemak afneemt" topics_per_period_in_top_summary: "Aantal topics in het top-topics overzicht" @@ -591,7 +568,6 @@ nl: invite_expiry_days: "Hoe lang uitnodigingscodes geldig blijven (in dagen)." min_password_length: "Minimum lengte wachtwoord." sso_url: "URL van single sign on eindpunt" - sso_secret: "Geheime tekenreeks voor het encrypten/decrypten van SSO informatie, zorg er voor dat het 10 tekens of langer is" sso_overrides_email: "Gebruikt het mailadres van de SSO service i.p.v. het lokaal opgegegeven adres. (WAARSCHUWING: er kunnen discrepanties ontstaan door normalisatie van lokale mailadressen)" sso_overrides_username: "Gebruikt de gebruikersnaam van de SSO service i.p.v. de lokaal gekozen gebruikersnaam. (WAARSCHUWING: er kunnen discrepanties ontstaan door verschillen in eisen aan bijv. de lengte van de gebruikersnaam)" sso_overrides_name: "Gebruikt de naam die gebruikt wordt bij de SSO service i.p.v. de lokaal opgegeven naam. (WAARSCHUWING: er kunnen discrepanties ontstaan door normalisatie van lokale namen)" @@ -645,7 +621,6 @@ nl: enable_mobile_theme: "Mobiele apparaten gebruiken een mobiel-vriendelijke theme met de mogelijkheid te schakelen naar de volledige site. Schakel deze optie uit als je een eigen stylesheet wil gebruiken die volledig responsive is." suppress_uncategorized_badge: "Laat de badge niet zien voor topics zonder categorie in de topiclijsten." global_notice: "Laat een LET OP, BELANGRIJK-banner zien aan alle gebruikers. Laat leeg om niet te tonen (HTML is toegestaan)." - enable_names: "Gebruikers mogen hun volledige naam laten zien. Zet uit om volledige namen te verbergen." display_name_on_posts: "Laat de volledige naam van een gebruiker bij zijn berichten zien, na de @gebruikersnaam" short_progress_text_threshold: "Als het aantal berichten in een topic meer is dan dit aantal zal de voortgangsbalk alleen het nummer van het huidige bericht tonen. Als je de breedte van de voortgangsbalk verandert, moet je dit getal misschien ook aanpassen." default_code_lang: "Standaard programmeertaal die gebruikt wordt voor syntax highlighting van GitHub codeblokken (lang-auto, ruby, python etc.)" @@ -719,7 +694,6 @@ nl: errors: "%{errors}" not_available: "Niet beschikbaar. Probeer %{suggestion}?" something_already_taken: "Er ging iets mis, misschien zijn de gebruikersnaam en/of e-mailadres al in gebruik? Gebruik dan de 'wachtwoord vergeten' link" - omniauth_error: "Sorry, er was een fout met je %{strategy} account. Wellicht heb je de autorisatie niet goedgekeurd?" omniauth_error_unknown: "Er is iets misgegaan bij het inloggen. Probeer het opnieuw." new_registrations_disabled: "Het registeren van nieuwe accounts is niet toegestaan op dit moment." password_too_long: "Wachtwoorden mogen maximaal 200 tekens lang zijn." @@ -761,8 +735,6 @@ nl: flags_reminder: please_review: "Kijk hier even naar." post_number: "bericht" - flag_reasons: - inappropriate: "Uw bericht bevat inhoud dat iemand als beledigend, discriminerend of kwetsend kan ervaren. Ook kan het een overtreding van [de regels](/guidelines)." flags_dispositions: agreed: "Bedankt dat je het ons hebt laten weten. We zijn het er mee eens dat er een probleem is en we gaan er naar kijken." agreed_and_deleted: "Bedankt dat je het ons hebt laten weten. We zijn het er mee eens dat er een probleem is en we hebben het bericht verwijderd." @@ -774,21 +746,8 @@ nl: subject_template: "Bericht van %{site_name}: je bericht is verborgen wegens meldingen uit de community" welcome_user: subject_template: "Welkom bij %{site_name}!" - text_body_template: | - Fijn dat je lid bent geworden van %{site_name} en welkom! - - %{new_user_tips} - - We geloven altijd in [beschaafd gemeenschappelijk gedrag](%{base_url}/guidelines). - - Geniet van je verblijf! welcome_invite: subject_template: "Welkom bij %{site_name}!" - csv_export_succeeded: - subject_template: "Data Export succesvol afgerond." - csv_export_failed: - subject_template: "Export mislukt" - text_body_template: "De export is mislukt. Bekijk de logbestanden." too_many_spam_flags: subject_template: "Account geblokkeerd" text_body_template: | @@ -980,10 +939,7 @@ nl: unauthorized: "Sorry, je mag dat bestand niet uploaden (toegestane extensies: %{authorized_extensions})." pasted_image_filename: "Geplakte afbeelding" store_failure: "Het opslaan van upload #%{upload_id} voor gebruiker #%{user_id} is mislukt." - attachments: - too_large: "Sorry, het bestand dat je wil uploaden is te groot (maximum grootte is %{max_size_kb}%kb)." images: - too_large: "De afbeelding die je wil uploaden is te groot (maximum grootte is %{max_size_kb}%kb). Verklein de afbeelding en probeer het opnieuw." fetch_failure: "Er ging iets mis bij het opvragen van de afbeelding." unknown_image_type: "Het bestand dat je wil uploaden is geen afbeelding." size_not_found: "Het is niet gelukt de afmetingen van de afbeelding te bepalen. Misschien is het bestand corrupt?" diff --git a/config/locales/server.pl_PL.yml b/config/locales/server.pl_PL.yml index 0b4b22bbf6..66e32496ae 100644 --- a/config/locales/server.pl_PL.yml +++ b/config/locales/server.pl_PL.yml @@ -20,11 +20,6 @@ pl_PL: short_date_no_year: "D MMM" short_date: "D MMM YYYY" long_date: "D MMMM YYYY H:mm" - time: - formats: - short: "%d.%m.%Y" - short_no_year: "%-d %B" - date_only: "%-d %b %Y" title: "Discourse" topics: "Tematy" posts: "wpisy" @@ -33,13 +28,49 @@ pl_PL: log_in: "Logowanie" via: "%{username} z %{site_name}" is_reserved: "jest zarezerwowana" - purge_reason: "Automatycznie usunięto z uwagi na wiek i brak weryfikacji" + purge_reason: "Automatycznie usunięto jako porzucone, nieaktywne konto" disable_remote_images_download_reason: "Pobieranie zewnętrznych grafik zostało wyłączone z uwagi na niską ilość wolnego miejsca na dysku." errors: + format: '%{attribute} %{message}' messages: too_long_validation: "ograniczono do %{max} znaków, podano %{length}." invalid_boolean: "Nieprawidłowy boolean." - taken: "jest już zajęty" + accepted: musi zostać zaakceptowane + blank: nie może być puste + present: musi być puste + confirmation: "nie zgadza się z %{attribute}" + empty: nie może być puste + equal_to: musi być równe do %{count} + even: musi być parzyste + exclusion: jest zarezerwowane + greater_than: musi być większe niż %{count} + greater_than_or_equal_to: musi być większe lub równe %{count} + inclusion: nie jest zawarty w liście + invalid: jest nieprawidłowy + less_than: musi być mniejszy niż %{count} + less_than_or_equal_to: musi być mniejszy lub równy %{count} + not_a_number: nie jest liczbą + not_an_integer: musi być liczbą całkowitą + odd: musi być nieparzysty + record_invalid: 'Weryfikacja zakończyła się niepowiedzeniem: %{errors}' + restrict_dependent_destroy: + one: "Nie można usunąć wpisu, ponieważ zależny %{record} istnieje" + many: "Nie można usunąć wpisu, ponieważ zależny %{record} istnieje" + too_long: + one: jest za długa (maksymalnie 1 znak) + few: jest za długa (maksymalnie %{count} znaków) + other: jest za długa (maksymalnie %{count} znaków) + too_short: + one: jest za krótka (minimalnie 1 znak) + few: jest za krótka (minimalnie %{count} znaków) + other: jest za długa (minimum to %{count} znaków) + wrong_length: + one: ma nieprawidłową długość (powinna być 1 znakiem) + few: jest nieprawidłowej długości (powinna mieć %{count} znaków) + other: jest nieprawidłowej długości (powinna mieć %{count} znaków) + other_than: "musi być różna od %{count}" + template: + body: 'Wystąpiły problemy z następującymi polami:' embed: load_from_remote: "Wystąpił błąd podczas wczytywania tego wpisu." bulk_invite: @@ -110,6 +141,7 @@ pl_PL: latest: "Ostatnie wątki" hot: "Popularne wątki" too_late_to_edit: "Ten wpis został utworzony zbyt dawno. Nie może być edytowany ani usunięty." + excerpt_image: "zdjęcie" groups: errors: can_not_modify_automatic: "Nie możesz modyfikować automatycznej grupy" @@ -182,8 +214,8 @@ pl_PL: base: warning_requires_pm: "Ostrzeżenie można dołączać jedynie do prywatnych wiadomości." too_many_users: "Jedno ostrzeżenie możesz wysłać tylko do pojedynczego użytkownika." - archetype: - cant_send_pm: "Przepraszamy, nie możesz wysłać prywatnej wiadomości do tego użytkownika." + cant_send_pm: "Przepraszamy, niestety nie możesz wysłać prywatnej wiadomości temu użytkownikowi." + no_user_selected: "Musisz wybrać poprawnego użytkownika," user: attributes: password: @@ -387,11 +419,14 @@ pl_PL: post_action_types: off_topic: title: 'Nie-na-temat' + description: 'Ten post jest niezwiązany z obecną dyskusją zdefiniowaną przez tytuł i pierwszy post, i powinien zostać przeniesiony gdzieś indziej.' long_form: 'oflagowano jako nie-na-temat' spam: title: 'Spam' description: 'Ten wpis jest reklamą. Jest nieprzydatny i bez związku z obecnym tematem, ma naturę promocyjną.' long_form: 'oflagowano jako spam' + email_title: '%{title}" został oznaczony jako spam' + email_body: "%{link}\n\n%{message}" inappropriate: title: 'Niewłaściwe' description: 'Ten wpis zawiera treści które umiarkowana osoba może uznać za wulgarne, obraźliwe lub naruszające wytyczne społeczności.' @@ -399,9 +434,13 @@ pl_PL: notify_user: title: 'Prywatna wiadomość @{{username}}' description: 'Ten wpis zawiera coś o czym chciałbym porozmawiać z tą osobą bezpośrednio i prywatnie. Bez oflagowania.' + long_form: 'wysłał prywatną wiadomość do użytkownika' email_title: 'Twój wpis w "%{title}"' email_body: "%{link}\n\n%{message}" notify_moderators: + title: "Coś innego" + description: 'Ten post wymaga uwagi moderatora z powodu nie uwzględnionego na liście powyżej.' + long_form: 'oznaczył to dla uwagi moderatora' email_title: 'Wpis w "%{title}" wymaga uwagi moderatorów' email_body: "%{link}\n\n%{message}" bookmark: @@ -426,7 +465,9 @@ pl_PL: description: 'Ten temat zawiera treści jakie rozsądna osoba może uznać za agresywne, obraźliwe, lub sprzeczne z wytycznymi społeczności.' long_form: 'oflagowano jako niewłaściwe' notify_moderators: + title: "Coś innego" description: 'Ten temat wymaga interwencji moderatora z uwagi na niezgodność z wytycznymi społeczności, warunkami użytkowania lub z innego, niewymienionego tu powodu.' + long_form: 'oznaczył to dla uwagi moderatora' email_title: 'Temat "%{title}" wymaga uwagi moderatora' email_body: "%{link}\n\n%{message}" flagging: @@ -485,7 +526,7 @@ pl_PL: xaxis: "Dzień" yaxis: "Liczba oznaczonych tematów" users_by_trust_level: - title: "Użytkowników na poziom zaufania" + title: "Użytkownicy wg poziomu zaufania" xaxis: "Poziom zaufania" yaxis: "Liczba użytkowników" emails: @@ -527,6 +568,37 @@ pl_PL: title: "Najczęściej linkowane tematy" xaxis: "Temat" num_clicks: "Kliki" + page_view_anon_reqs: + title: "Anonimowy" + xaxis: "Dzień" + yaxis: "Anonimowych wyświetleń strony" + page_view_logged_in_reqs: + title: "Zalogowany" + xaxis: "Dzień" + yaxis: "Wyświetleń zalogowanych użytkowników" + page_view_crawler_reqs: + title: "Bot indeksujący" + xaxis: "Dzień" + yaxis: "Wyświetlenia botów indeksujących" + page_view_total_reqs: + title: "Łącznie" + xaxis: "Dzień" + yaxis: "Łączna ilość wyświetleń" + http_background_reqs: + title: "Tło" + xaxis: "Dzień" + http_2xx_reqs: + xaxis: "Dzień" + http_3xx_reqs: + xaxis: "Dzień" + http_4xx_reqs: + xaxis: "Dzień" + http_5xx_reqs: + xaxis: "Dzień" + http_total_reqs: + title: "Łącznie" + xaxis: "Dzień" + yaxis: "Razem zapytań" dashboard: rails_env_warning: "Twój serwer działa w trybie %{env}" ruby_version_warning: "Używasz Ruby w wersji 2.0.0, która może powodować problemy. Zaktualizuj się do patch level 247 lub późniejszej. " @@ -544,14 +616,9 @@ pl_PL: s3_config_warning: 'Serwer jest skonfigurowany by przesyłać pliki na s3, ale przynajmniej jedno z następujących ustawień nie jest ustawione: s3_access_key_id, s3_secret_access_key or s3_upload_bucket. Przejdź do ustawień serwisu i zmień ustawienia. Zobacz ten poradnik by dowiedzieć się więcej.' image_magick_warning: 'Serwer jest skonfigurowany by tworzyć miniaturki dużych obrazów, ale ImageMagick nie jest zainstalowany. Zainstaluj ImageMagick za pomocą ulubionego menedżera pakietów lub pobierz najnowszą wersję.' failing_emails_warning: '%{num_failed_jobs} prac emailowych nie powiodło się. Sprawdź plik config/discourse.conf i upewnij się, że ustawienia serwera poczty są poprawne. Zobacz nieudane prace w Sidekiqu.' - default_logo_warning: "Nie ustawiłeś loga dla Twojego serwisu. Zmień logo_url, logo_small_url i favicon_url w ustawieniach serwisu." - contact_email_missing: "Nie podałeś kontaktowego adresu email dla Twojego serwisu. Podaj contact_email w ustawieniach serwisu." - contact_email_invalid: "Ustawiony adres kontaktowy serwisu jest nieprawidłowy. Zmień contact_email w ustawieniach serwisu." - title_nag: "Ustawienie serwisu title nadal ma domyślną wartość. Zmień ją na tytuł Twojego serwisu w ustawieniach serwisu." - site_description_missing: "The site_description setting is blank. Write a brief description of this forum in the Site Settings." + default_logo_warning: "Ustaw logo dla swojej strony. Zaktualizuj logo_url, logo_small_url i favicon_url tutaj: ustawienia strony." + title_nag: "Wprowadź nazwę swojej strony. Zaktualizuj tytuł w Ustawieniach strony." consumer_email_warning: "Twój serwis jest skonfigurowany by używać Gmaila (lub innego konsumenckiego serwisu poczty) do wysyłania emaili. Gmail ogranicza jak dużo wiadomości możesz wysłać. Rozważ wykorzystanie dostawcy serwisu pocztowego jak mandrill.com by zagwarantować dostarczanie poczty." - site_contact_username_warning: "The site_contact_username setting is blank. Please update it in the Site Settings. Set it to the username of an admin user who should be the sender of system messages." - notification_email_warning: "The notification_email setting is blank. Please update it in the Site Settings." content_types: education_new_reply: title: "Edukacja nowych użytkowników: Pierwsze odpowiedzi" @@ -582,6 +649,7 @@ pl_PL: description: "Kod HTML jaki zostanie dołączony na górze każdej strony (po nagłówku, przed nawigacją lub tytułem tematu)." bottom: title: "Stopka każdej strony" + description: "Kod HTML, który zostanie załączony przed tagiem ." site_settings: censored_words: "Wskazane słowa będą automatycznie zamieniane na ■■■■" delete_old_hidden_posts: "Automatycznie kasuj wpisy ukryte dłużej niż 30 dni." @@ -599,9 +667,7 @@ pl_PL: allow_duplicate_topic_titles: "Pozwól na tworzenie tematów o identycznych tytułach." unique_posts_mins: "Ile minut musi upłynąć zanim użytkownik będzie mógł ponownie zrobić wpis z tą samą treścią" educate_until_posts: "Wyświetlaj okno edukacyjne po rozpoczęciu pisania dopóki nowy użytkownik nie napisze tylu wpisów." - title: "Tytuł tego serwisu, używany w tagu title." - site_description: "Opis serwisu w jednym zdaniu, używany w tagu meta description." - contact_email: "Adres email kogoś, z kim należy się kontaktować w kwestii serwisu. Istotne informacje z discourse.org dotyczące krytycznych aktualizacji mogą być wysyłane pod ten adres." + site_description: "Opis strony w jednym zdaniu, wykorzystywany w meta tagu description." queue_jobs: "DEVELOPER ONLY! WARNING! By default, queue jobs in sidekiq. If disabled, your site will be broken." crawl_images: "Pobieraj grafiki ze zdalnych URLi aby ustawić poprawną wysokość i szerokość w tagu img." download_remote_images_to_local: "Pobieraj zdalne grafiki i twórz ich lokalne kopie aby zapobiegać uszkodzonym/brakującym obrazkom na stronach." @@ -613,11 +679,13 @@ pl_PL: max_image_width: "Maksymalna szerokość miniaturki grafiki we wpisie. " max_image_height: "Maksymalna wysokość miniaturki grafiki we wpisie. " category_featured_topics: "Number of topics displayed per category on the /categories page. After changing this value, it takes up to 15 minutes for the categories page to update." - show_subcategory_list: "Po wejściu do kategorii zamiast listy tematów wyświetlaj listę podkategorii." + show_subcategory_list: "Po wejściu do kategorii, zamiast listy tematów wyświetlaj listę podkategorii." fixed_category_positions: "Zaznacz, aby ręcznie ustawiać kolejność kategorii. Odznacz, aby kategorie były sortowane na podstawie aktywności. " exclude_rel_nofollow_domains: "A pipe-delimited list of domains where nofollow is not added (tld.com will automatically allow sub.tld.com as well)" - favicon_url: "A favicon for your site, see http://en.wikipedia.org/wiki/Favicon" - apple_touch_icon_url: "Icon used for Apple touch devices. Recommended size is 144px by 144px." + digest_logo_url: "Alternatywne logo używane w biuletynie email. Jeśli puste, zostanie użyta wartość z pola `logo_url`, np: http://example.com/logo.png" + favicon_url: "Favicon serwisu. Więcej informacji: http://pl.wikipedia.org/wiki/Favicon" + apple_touch_icon_url: "Ikona używana przez urządzenia Apple. Rekomendowany wymiar to 144px na 144px." + notification_email: "Adres z którego wysyłane będą wszystkie istotne emaile systemowe.\nKonieczna jest poprawna konfiguracja rekordów SPF, DKIM oraz zwrotnego PTR użytej domeny." email_custom_headers: "A pipe-delimited list of custom email headers" enable_long_polling: "Message bus used for notification can use long polling" anon_polling_interval: "How often should anonymous clients poll in milliseconds" @@ -629,14 +697,22 @@ pl_PL: ga_tracking_code: "Identyfikator Google analytics (ga.js), np: UA-12345678-9; zobacz http://google.com/analytics" ga_domain_name: "Google analytics (ga.js) domain name, eg: mysite.com; see http://google.com/analytics" ga_universal_tracking_code: "Identyfikator Google Universal Analytics (analytics.js), np: UA-12345678-9; zobacz http://google.com/analytics" + ga_universal_domain_name: "Domena dla Google Universal Analytics (analytics.js), np: mysite.com; zobacz http://google.com/analytics" enable_noscript_support: "Enable standard webcrawler search engine support via the noscript tag" allow_moderators_to_create_categories: "Zezwól moderatorom na tworzenie nowych kategorii" - top_menu: "Determine which items appear in the homepage navigation, and in what order. Example latest|new|unread|starred|categories|top|read|posted" + post_menu: "Określ które elementy menu wpisu powinny być widoczne i w jakiej kolejności. Przykład like|edit|flag|delete|share|bookmark|reply" + share_links: "Określ które elementy menu udostępniania powinny być widoczne i w jakiej kolejności. " track_external_right_clicks: "Śledź zewnętrzne linki kliknięte prawym klawiszem (np. otwierane w nowej zakładce). Domyślnie wyłączone, gdyż wymaga nadpisywania URLi." + topics_per_period_in_top_summary: "Liczba tematów wyświetlanych w domyślnym podsumowaniu najbardziej popularnych wątków" + topics_per_period_in_top_page: "Liczba tematów wyświetlanych w widoku 'Pokaż więcej' na ekranie popularnych wątków." + enable_badges: "Włącz system odznak" port: "DEVELOPER ONLY! WARNING! Use this HTTP port rather than the default of port 80. Leave blank for default of 80." force_hostname: "DEVELOPER ONLY! WARNING! Specify a hostname in the URL. Leave blank for default." + invite_expiry_days: "Jak długo klucz zaproszenie użytkownika jest ważny, w dniach." invite_only: "Publiczna rejestracja jest wyłączona: wszyscy nowi użytkownicy muszą zostać zaproszeni przez innych użytkowników lub zespół." + login_required: "Wymagaj autoryzacji do wyświetlenia zawartości strony, zablokuj możliwość anonimowego dostępu." min_password_length: "Minimalna długość hasła." + allow_new_registrations: "Zezwól na rejestrację nowych użytkowników. Odznacz opcję żeby uniemożliwić rejestrację nowych kont." enable_yahoo_logins: "Enable Yahoo authentication" enable_twitter_logins: "Enable Twitter authentication, requires twitter_consumer_key and twitter_consumer_secret" twitter_consumer_key: "Consumer key for Twitter authentication, registered at http://dev.twitter.com" @@ -647,24 +723,41 @@ pl_PL: enable_github_logins: "Enable Github authentication, requires github_client_id and github_client_secret" github_client_id: "Client id for Github authentication, registered at https://github.com/settings/applications" github_client_secret: "Client secret for Github authentication, registered at https://github.com/settings/applications" + maximum_backups: "Maksymalna liczba kopii zapasowych do przechowywania na dysku. Starsze kopie zapasowe zostaną automatycznie usunięte." + backup_daily: "Automatycznie utwórz raz dziennie kopie zapasową strony." active_user_rate_limit_secs: "How frequently we update the 'last_seen_at' field, in seconds" previous_visit_timeout_hours: "How long a visit lasts before we consider it the 'previous' visit, in hours" + suggested_topics: "Liczba sugerowanych tematów widocznych na końcu aktualnego tematu." + limit_suggested_to_category: "Sugeruj tematy jedynie z tej samej kategorii." + default_invitee_trust_level: "Domyślny poziom zaufania (0-4) dla zaproszonych użytkowników." + default_trust_level: "Domyślny poziom zaufania (0-4) dla wszystkich nowych użytkowników." min_trust_to_create_topic: "The minimum trust level required to create a new topic." newuser_max_mentions_per_post: "Maksymalna liczba powiadomień poprzez @nazwę w jednym wpisie (dla nowych użytkowników)." max_mentions_per_post: "Maksymalna liczba powiadomień poprzez @nazwę w jednym wpisie (dla wszystkich)." title_fancy_entities: "Convert common ASCII characters to fancy HTML entities in topic titles, ala SmartyPants http://daringfireball.net/projects/smartypants/" + category_colors: "Lista kolorów w formacie hex do użycia w etykietach kategorii." title_prettify: "Prevent common title typos and errors, including all caps, lowercase first character, multiple ! and ?, extra . at end, etc." faq_url: "If you have a FAQ hosted elsewhere that you want to use, provide the full URL here." tos_url: "If you have a Terms of Service document hosted elsewhere that you want to use, provide the full URL here." privacy_policy_url: "If you have a Privacy Policy document hosted elsewhere that you want to use, provide the full URL here." newuser_spam_host_threshold: "How many times a new user can post a link to the same host within their `newuser_spam_host_posts` posts before being considered spam." staff_like_weight: "O ile większą wagę mają mieć polubienia przyznawane przez członków zespołu?" + reply_by_email_enabled: "Włącz możliwość odpowiadania na wpisy poprzez email." reply_by_email_address: "Template for reply by email incoming email address, for example: %{reply_key}@reply.example.com or replies+%{reply_key}@example.com" + pop3_polling_ssl: "Używaj SSL podczas łączenia z serwerem POP3. (Rekomendowane)" + relative_date_duration: "Liczba dni od wysłania wpisu gdy wyświetlana jest data relatywna (7d) zamiast absolutnej (20 Lut)." delete_all_posts_max: "The maximum number of posts that can be deleted at once with the Delete All Posts button. If a user has more than this many posts, the posts cannot all be deleted at once and the user can't be deleted." email_editable: "Allow users to change their e-mail address after registration." + allow_uploaded_avatars: "Zezwól użytkownikom przesyłanie własnych avatarów." default_digest_email_frequency: "How often users receive digest emails by default. They can change this setting in their preferences." - enable_mobile_theme: "Mobile devices use a mobile-friendly theme, with the ability to switch to the full site. Disable this if you want to use a custom stylesheet that is fully responsive." + default_external_links_in_new_tab: "Otwieraj zewnętrzne odnośniki w nowej karcie. Użytkownicy mogą zmienić to ustawienie w swoich preferencjach." + allow_profile_backgrounds: "Zezwól użytkownikom na przesyłanie obrazu tła dla profilu." + enable_mobile_theme: "Urządzenia mobilne używają dedykowanego mobilnego szablonu. Wyłącz to, jeśli chcesz użyć własnego, pojedynczego i responsywnego szablonu stylów. " suppress_uncategorized_badge: "Nie pokazuj etykiety z nazwą kategorii Inne na listach tematów." + errors: + invalid_email: "Nieprawidłowy adres email." + invalid_username: "Użytkownik o takiej nazwie nie istnieje." + invalid_string: "Nieprawidłowa wartość." notification_types: mentioned: "%{display_username} wspomina o Tobie w %{link}" liked: "%{display_username} polubił(-a) Twój wpis w %{link}" @@ -715,13 +808,15 @@ pl_PL: errors: "%{errors}" not_available: "Niedostępny. Może spróbuj %{suggestion}?" something_already_taken: "Coś poszło źle. Możliwe, że ta nazwa użytkownika lub email są już zarejestrowane. Spróbuj użyć procedury przypominania hasła." - omniauth_error: "Przepraszamy, wystąpił błąd podczas uwierzytelniania Twojego konta %{strategy}. Może nie zaakceptowałeś uwierzytelnienia?" omniauth_error_unknown: "Wystąpił błąd podczas logowania, spróbuj ponownie." password_too_long: "Hasła są ograniczone do 200 znaków." + missing_user_field: "Nie wypełniłeś wszystkich pól użytkownika" close_window: "Uwierzytelnianie zakończone. Aby kontynuować, zamknij to okno." user: + no_accounts_associated: "Brak powiązanych kont" username: short: "music zawierać co najmniej %{min} znaków" + long: "nie może zawierać więcej niż %{max} znaków" characters: "może zawierać tylko litery, cyfry i podkreślenia" unique: "musi być unikalna" blank: "musi zostać podana" @@ -729,6 +824,8 @@ pl_PL: email: not_allowed: "nie jest dopuszczany od tego dostawcy poczty. Użyj innego adresu email." blocked: "is not allowed." + ip_address: + blocked: "jest zablokowany" invite_forum_mailer: text_body_template: | %{invitee_name} wysłał Tobie zaproszenie @@ -761,27 +858,13 @@ pl_PL: one: "1 flaga oczekuje na reakcję" few: "%{count} flagi oczekują na reakcję" other: "%{count} flagi oczekują na reakcję" - flag_reasons: - off_topic: "Twój wpis został oznaczony jako **nie-na-temat**: społeczność uważa, że nie pasuje on do tematu zdefiniowanego przez tytuł i treść pierwszego wpisu w wątku." - spam: "Twój wpis został oznaczony jako **spam**: społeczność uważa, że jest to nieprzydatna reklama bez związku z tematem." - notify_moderators: "Twój wpis został oznaczony jako **wymagający uwagi moderatora**: społeczność uważa, że wpis wymaga interwencji moderatorów." system_messages: post_hidden: subject_template: "Wpis został ukryty z powodu oflagowania przez społeczność" welcome_user: subject_template: "Witaj na %{site_name}!" - text_body_template: | - Dziękujemy za rejestrację, witamy na %{site_name}! - - %{new_user_tips} - - Doceniamy cywilizowane zachowanie zgodne z [wytycznymi społeczności](%{base_url}/guidelines). - - Miłego korzystania! welcome_invite: subject_template: "Witaj na %{site_name}!" - text_body_template: "Dziękujemy za przyjęcie zaproszenia do %{site_name} – witamy!\n\nTwoja automatycznie wygenerowana nazwa użytkownika: **%{username}**. Możesz ją zmienić w każdej chwili w [swoich ustawieniach][prefs].\n\nAby zalogować się:\n\nA) Zaloguj się przyciskiem logowania przez zewnętrzny serwis. Pamiętaj, aby podpięty pod wybrany serwis adres e-mail **był taki sam** jak ten na który dostałeś swoje zaproszenie. W przeciwnym wypadku nie będziemy mogli stwierdzić, że ty to Ty! \n\nB) Stwórz unikalne hasło w [swoich ustawieniach][prefs] i zaloguj się przy jego pomocy.\n\n%{new_user_tips}\n\nWierzymy w [cywilizowane zachowanie](%{base_url}/guidelines) jako podstawę naszej społeczności.\n\n\ - Miłego uczestnictwa! \n\n[prefs]: %{user_preferences_url}\n" backup_succeeded: subject_template: "Kopia zapasowa jest gotowa" text_body_template: "Kopia zapasowa została wykonana pomyślnie.\nOdwiedź [admin > kopie zapasowe](/admin/backups) aby ją pobrać." @@ -957,10 +1040,7 @@ pl_PL: edit_reason: "lokalne kopie pobranych obrazów" unauthorized: "Sorry, the file you are trying to upload is not authorized (authorized extensions: %{authorized_extensions})." pasted_image_filename: "Pasted image" - attachments: - too_large: "Sorry, the file you are trying to upload is too big (maximum size is %{max_size_kb}%kb)." images: - too_large: "Sorry, the image you are trying to upload is too big (maximum size is %{max_size_kb}%kb), please resize it and try again." fetch_failure: "Sorry, there has been an error while fetching the image." unknown_image_type: "Sorry, but the file you tried to upload doesn't appear to be an image." size_not_found: "Sorry, but we couldn't determine the size of the image. Maybe your image is corrupted?" @@ -972,5 +1052,12 @@ pl_PL: user_suspended: "użytkownik został zawieszony" color_schemes: base_theme_name: "Podstawa" + csv_export: + boolean_yes: "Tak" + boolean_no: "Nie" + static_topic_first_reply: | + Edytuj pierwszy post w tym temacie by zmienić zawartość strony %{page_name}. + guidelines_topic: + title: "FAQ" tos_topic: title: "Warunki użytkowania" diff --git a/config/locales/server.pt.yml b/config/locales/server.pt.yml index 5bfc1b8f90..9202a0a519 100644 --- a/config/locales/server.pt.yml +++ b/config/locales/server.pt.yml @@ -6,37 +6,47 @@ # https://www.transifex.com/projects/p/discourse-org/ pt: + stringex: + characters: + number: "-" + i18n: + transliterate: + rule: + ț: "t" + Ț: "t" + ș: "s" + Ș: "s" dates: - short_date_no_year: "D MMM" - short_date: "D MMM, YYYY" - long_date: "D de MMMM de YYYY h:mma" - time: - formats: - short: "%d/%m/%Y" - short_no_year: "%B %-d" - date_only: "%b %-d, %Y" + short_date_no_year: "DD MMM" + short_date: "DD MMM, YYYY" + long_date: "DD de MMMM de YYYY hh:mm" title: "Discourse" topics: "Tópicos" - posts: "posts" - loading: "Carregando" - powered_by_html: 'Desenvolvido por Discourse, melhor visualizado com JavaScript ativado' - log_in: "Entrar" - via: "%{username} via %{site_name}" + posts: "mensagens" + loading: "A carregar" + powered_by_html: 'Desenvolvido por Discourse, e melhor visualizado com o JavaScript ativo' + log_in: "Iniciar Sessão" + via: "%{username} através de %{site_name}" is_reserved: "está reservado" - purge_reason: "Apagar automaticamente devido à antiguidade e por não ser verificado" + purge_reason: "Automaticamente eliminado devido a abandono, conta inativada" + disable_remote_images_download_reason: "O download remoto de imagens foi desativado por não haver espaço disponível no disco." errors: messages: too_long_validation: "está limitado a %{max} caracteres; inseriu %{length}." + invalid_boolean: "Valor lógico inválido." embed: - load_from_remote: "Houve um erro no carregamento desse post." + load_from_remote: "Ocorreu um erro no carregamento dessa mensagem." bulk_invite: - file_should_be_csv: "O ficheiro a enviar deve estar em formato csv ou txt." + file_should_be_csv: "O ficheiro a carregar deve estar em formato csv ou txt." backup: - operation_already_running: "Existe actualmente uma operação em andamento. Neste momento não é possível iniciar um novo trabalho. " - backup_file_should_be_tar_gz: "O ficheiro de backup deve ser um arquivo .tar.gz" - not_enough_space_on_disk: "Não existe espaço suficiente no disco para carregar este backup." + operation_already_running: "Existe atualmente uma operação em execução. Neste momento não é possível iniciar um novo trabalho. " + backup_file_should_be_tar_gz: "O ficheiro da cópia de segurança deve ser um arquivo .tar.gz" + not_enough_space_on_disk: "Não existe espaço suficiente no disco para carregar esta cópia de segurança." not_logged_in: "Necessita de ter sessão iniciada para fazer isso." - read_only_mode_enabled: "Este site encontra-se no modo apenas leitura. As interacções estão desctivadas." + read_only_mode_enabled: "Este sítio encontra-se no modo só de leitura. As interações estão desativadas." + too_many_replies: + one: "Pedimos desculpa mas novos utilizadores estão temporariamente limitados a 1 resposta no mesmo tópico." + other: "Pedimos desculpa mas novos utilizadores estão temporariamente limitados a %{count} respostas no mesmo tópico." embed: start_discussion: "Iniciar Discussão" continue: "Continuar Discussão" @@ -44,78 +54,82 @@ pt: one: "mais 1 resposta" other: "mais %{count} respostas" loading: "A carregar Discussão..." - permalink: "Permalink" + permalink: "Hiperligação Permanente" imported_from: "Este é um tópico de discussão de acompanhamento para a entrada original em %{link}" in_reply_to: "▶ %{username}" replies: one: "1 resposta" other: "%{count} respostas" too_many_mentions: - zero: "Desculpe, você não pode mencionar outros usuários." - one: "Desculpe, você pode mencionar apenas um outro usuário em um post." - other: "Desculpe, você pode mencionar apenas %{count} usuários em um post." + zero: "Pedimos desculpa, não pode mencionar outros utilizadores." + one: "Pedimos desculpa, pode mencionar apenas um outro utilizador numa mensagem." + other: "Pedimos desculpa, pode mencionar apenas %{count} utilizadores numa mensagem." too_many_mentions_newuser: - zero: "Desculpe, usuários novos não podem mencionar outros usuários." - one: "Desculpe, usuários novos podem mencionar apenas um outro usuário em um post." - other: "Desculpe, usuários novos podem mencionar apenas %{count} usuários em um post." + zero: "Pedimos desculpa, utilizadores novos não podem mencionar outros utilizadores." + one: "Pedimos desculpa, utilizadores novos podem mencionar apenas um outro utilizador numa mensagem." + other: "Pedimos desculpa, utilizadores novos podem mencionar apenas %{count} utilizadores numa mensagem." too_many_images: - zero: "Desculpe, usuários novos não podem colocar imagens nas postagens." - one: "Desculpe, usuários novos podem colocar apenas uma imagem nas postagens." - other: "Desculpe, usuários novos podem colocar apenas %{count} imagens nas postagens." + zero: "Pedimos desculpa, utilizadores novos não podem colocar imagens nas mensagens." + one: "Pedimos desculpa, utilizadores novos podem colocar apenas uma imagem nas mensagens." + other: "Pedimos desculpa, utilizadores novos podem colocar apenas %{count} imagens numa mensagem." too_many_attachments: - zero: "Desculpe, novos usuários não podem colocar anexos nas postagens." - one: "Desculpe, novos usuários podem colocar apenas um anexo nas suas postagens." - other: "Desculpe, novos usuários podem colocar apenas %{count} anexos nas suas postagens." + zero: "Pedimos desculpa, novos utilizadores não podem colocar anexos nas mensagens." + one: "Pedimos desculpa, novos utilizadores podem colocar apenas um anexo nas suas mensagens." + other: "Pedimos desculpa, novos utilizadores podem colocar apenas %{count} anexos nas suas mensagens." too_many_links: - zero: "Desculpe, usuários novos não podem colocar links nas postagens." - one: "Desculpe, usuários novos podem colocar apenas um link nas postagens." - other: "Desculpe, usuários novos podem colocar apenas %{count} links nas postagens." - spamming_host: "Desculpe, você não pode colocar um link para este site." - user_is_suspended: "Não é permitida a criação de posts para utilizadores suspensos." - just_posted_that: "é demasiado similar ao que postaste recentemente" + zero: "Pedimos desculpa, utilizadores novos não podem colocar hiperligações nas mensagens." + one: "Pedimos desculpa, utilizadores novos podem colocar apenas uma hiperligação nas mensagens." + other: "Pedimos desculpa, utilizadores novos podem colocar apenas %{count} hiperligações nas mensagens." + spamming_host: "Pedimos desculpa, não pode colocar uma hiperligação para esse servidor." + user_is_suspended: "Utilizadores suspensos não têm permissão para publicar." + just_posted_that: "é demasiado semelhante ao que publicaste recentemente" has_already_been_used: "já foi usado" - invalid_characters: "contém caracteres inválidos" - is_invalid: "é inválido; tenta ser um bocado mais descritivo" + invalid_characters: "contem caracteres inválidos" + is_invalid: "é inválido; tente ser um pouco mais descritivo" next_page: "próxima página →" prev_page: "← página anterior" page_num: "Página %{num}" topics_in_category: "Tópicos na categoria '%{category}'" - rss_posts_in_topic: "RSS feed de '%{topic}'" - rss_topics_in_category: "RSS feed dos tópicos da categoria '%{category}'" + rss_posts_in_topic: "Feed RSS de '%{topic}'" + rss_topics_in_category: "Feed RSS dos tópicos da categoria '%{category}'" author_wrote: "%{author} escreveu:" - num_posts: "Posts:" + num_posts: "Mensagens:" num_participants: "Participantes:" read_full_topic: "Ler tópico completo" private_message_abbrev: "MP" rss_description: - latest: "Últimos tópicos" + latest: "Tópicos recentes" hot: "Tópicos quentes" - too_late_to_edit: "Esse poste foi criada há muito tempo. Já não pode ser editado ou apagado." + too_late_to_edit: "Essa mensagem foi criada há muito tempo. Já não pode ser editada ou apagada." groups: errors: - can_not_modify_automatic: "Você não pode modificar um grupo automático" + can_not_modify_automatic: "Não pode modificar um grupo automático" default_names: everyone: "todos" - admins: "admins" + admins: "administradores" moderators: "moderadores" - staff: "staff" - trust_level_0: "trust_level_0" + staff: "pessoal" + trust_level_0: "nivel_de_confianca_0" trust_level_1: "nivel_de_confianca_1" trust_level_2: "nivel_de_confianca_2" trust_level_3: "nivel_de_confianca_3" trust_level_4: "nivel_de_confianca_4" education: until_posts: - one: "1 post" - other: "%{count} posts" - avatar: | - ### Que tal uma nova imagem para a sua conta? ⏎ - ⏎ - Você postou alguns tópicos e respostas, mas seu avatar não é tão original como você é -- é o mesmo avatar padrão que todos os novos usuários têm ⏎. - ⏎ - Já considerou **[visitar o seu perfil de usuário](%{profile_path})** e fazer o envio de uma imagem personalizada que representa você?⏎ - ⏎ - É mais fácil para acompanhar as discussões da comunidade e encontrar pessoas interessantes em conversas quando todo mundo tem um avatar exclusivo!⏎ + one: "1 mensagem" + other: "%{count} mensagens" + new-topic: "Bem-vindo a %{site_name}— **obrigado por começar uma nova conversação!** \n\n- O título descreve o seu tópico com precisão? Parece interessante?\n \n- Qual é o tema do tópico? Quem estaria interessado no mesmo? Porque é importante? Que tipo de respostas está à espera por parte da comunidade? \n\n- Inclua boas palavras de pesquisa no seu tópico para que outros possam *encontrá-lo*. Para agrupar o seu tópico com tópicos relacionados, seleccione a categoria. \n\nPara mais, [verifique as diretrizes da comunidade](/guidelines). Este painel irá aparecer apenas para o seu primeiro %{education_posts_text}.\n" + new-reply: | + Bem-vindo a %{site_name} — **obrigado por contribuir!** + + - A sua resposta melhora de alguma maneira esta conversação? + + - Seja gentil com os restantes membros da comunidade. + + - Críticas construtivas são bem-vindas, mas critique *ideias*, não pessoas. + + Para mais, [verifique as diretrizes da comunidade](/guidelines). Este painel irá aparecer apenas para o seu primeiro %{education_posts_text}. + avatar: "### Que tal uma nova imagem para a sua conta? \n\nPublicou alguns tópicos e respostas, mas o seu avatar não é tão original como você o é -- é o mesmo avatar padrão que todos os novos utilizadores têm .\n\nJá considerou **[visitar o seu perfil de utilizador](%{profile_path})** e fazer o envio de uma imagem personalizada que o representa?\n\nÉ mais fácil acompanhar as discussões da comunidade e encontrar pessoas interessantes em conversas quando todas as pessoas têm um avatar exclusivo!\n" sequential_replies: | ### Considere responder várias mensagens de uma só vez @@ -124,12 +138,26 @@ pt: Poderá editar as suas respostas anteriores, para fazer referências a outras mensagens, fazendo sobressair esse texto, selecionando o botão de resposta apropriado. É mais fácil para todos, ler tópicos que têm poucas respostas bem desenvolvidas, do que variadíssimas respostas curtas e elementares. - dominating_topic: | - ### Deixe que os outros se juntem à conversa⏎ - ⏎ - Este tópico é claramente importante para você – você postou mais de %{percent}% das respostas aqui.⏎ - ⏎ - Tem certeza de que você está fornecendo o tempo adequado para outras pessoas também poderem compartilhar seus pontos de vista?⏎ + dominating_topic: |+ + ### Deixe que os outros se juntem à conversa + + Este tópico é claramente importante para si – publicou mais de %{percent}% das respostas aqui. + + Tem a certeza de que está a fornecer o tempo adequado para que outras pessoas também possam partilhar os seus pontos de vista? + + too_many_replies: |+ + ### Chegou ao limite de respostas para este tópico + + Pedimos desculpa mas novos utilizadores estão temporariamente limitados a %{newuser_max_replies_per_topic} respostas no mesmo tópico. + + Em vez de adicionar outra resposta, por favor considere editar as suas respostas anteriores, ou visitar outros tópicos. + + reviving_old_topic: | + ### Reavivar este tópico? + + A última resposta a este tópico tem agora %{days} dias. A sua resposta irá sobressair no tópico, no topo da sua lista e notificar todos os que anteriormente estavam envolvidos nesta conversação. + + Tem a certeza que pretende continuar esta conversação antiga? activerecord: attributes: category: @@ -147,13 +175,13 @@ pt: attributes: base: warning_requires_pm: "Apenas pode anexar avisos a mensagens privadas." - too_many_users: "Apenas pode envias avisos a um utilizador de cada vez." - archetype: - cant_send_pm: "Desculpe, não podes enviar uma mensagem particular para esse utilizador." + too_many_users: "Apenas pode enviar avisos a um utilizador de cada vez." + cant_send_pm: "Pedimos desculpa, não pode enviar uma mensagem privada a esse utilizador." + no_user_selected: "Tem que selecionar um utilizador válido." user: attributes: password: - common: "é uma das 10000 senhas mais comuns. Por favor use uma senha mais segura." + common: "é uma das 10000 palavras-passe mais comuns. Por favor use uma palavra-passe mais segura." ip_address: signup_not_allowed: "O registo não é permitido através desta conta." color_scheme_color: @@ -161,40 +189,69 @@ pt: hex: invalid: "não é uma cor válida" user_profile: - no_info_me: "
o campo Sobre mim do seu perfil está em branco, quer completar?
" + no_info_me: "
o campo Sobre Mim do seu perfil está em branco, quer preenchê-lo?
" no_info_other: "
%{name} ainda não colocou nada no campo Sobre Mim
" - vip_category_name: "Lounge" - vip_category_description: "Uma categoria exclusiva para membros de nível de confiança 3 ou mais alto." + vip_category_name: "Salão" + vip_category_description: "Uma categoria exclusiva para membros de Nível de Confiança 3 ou superior." meta_category_name: "Meta" - meta_category_description: "Discussão sobre este site, a sua organização, como funciona, e como o podemos melhorar." - staff_category_name: "Staff" - staff_category_description: "Categoria privada para discussões do staff. Os tópicos estão apenas visíveis para administradores e moderadores." + meta_category_description: "Discussão sobre este sítio, a sua organização, como funciona, e como podemos melhorá-lo." + staff_category_name: "Pessoal" + staff_category_description: "Categoria privada para discussões do pessoal. Os tópicos estão apenas visíveis para administradores e moderadores." + assets_topic_body: "Este é um tópico permanente, visível apenas ao pessoal, para armazenar imagens e ficheiros usados no design do sítio. Não o apague!\n\n\nVeja como:\n\n\n1. Responda a este tópico.\n2. Carregue todas as imagens que deseja usar para os logótipos, favicons, e tudo o mais. (Use o ícone da ferramenta de carregamento neste editor de mensagens, arraste ou cole imagens).\n3. Submeta a sua resposta.\n4. Clique com o botão direito do rato nas imagens da sua nova mensagem para obter o caminho das imagens submetidas, ou carregue no ícone de edição para editar a sua mensagem e recuperar o caminho para as imagens. Copie os caminhos das imagens.\n5. Cole esses caminhos em [configurações básicas](/admin/site_settings/category/required).\n\n\nSe precisar de ativar o carregamento de diferentes tipos de ficheiros, edite `authorized_extensions` em [configurações de ficheiros](/admin/site_settings/category/files)." lounge_welcome: - title: "Bem-vindo ao Lounge" + title: "Bem-vindo ao Salão" + body: |2+ + + Parabéns! :confetti_ball: + + Se consegue ver este tópico, então é porque foi recentemente promovido a **habitual** (Nível de Confiança 3). + + Pode agora … + + * Editar o título de qualquer tópico + * Modificar a categoria de qualquer tópico + * Ter todas as suas hiperligações a serem seguidas ([nãoseguimento automático](http://en.wikipedia.org/wiki/Nofollow) é removido) + * Aceder a um salão privado de categorias, apenas visível a utilizadores com Nível de Confiança 3 ou superior + * Esconder spam com uma única sinalização + + Aqui está a [lista actual de membros habituais](/badges/3/regular). Certifique-se que diz olá. + + Obrigado por ser uma parte importante na nossa comunidade! + + (Para mais informação acerca dos Níveis de Confiança, [veja este tópico][confiança]. Por favor tenha conhecimento que apenas membros que continuam a corresponder aos requisitos ao longo do tempo, irão manter-se habituais.) + + [confiança]: https://meta.discourse.org/t/what-do-user-trust-levels-do/4924 + category: topic_prefix: "Acerca da categoria %{category}" - post_template: "%{replace_paragraph}\n\nUse os parágrados a seguir para uma descrição longa, assim como para estabelecer diretrizes ou regras da categoria.\n\nAlgumas coisas para se considerar em qualquer resposta de discussão:\n\n- Para que serve esta categoria? Porque algum usaria esta categoria para seu tópico?\n\n- Como esta categoria se difere das demais categorias já existentes?\n\n- Nós precisamos desta categoria?\n\n- Deveríamos mesclar esta categoria com outra, ou dividir esta categoria em mais categorias?\n" + replace_paragraph: "[Substituir este primeiro parágrafo, com uma breve descrição de sua nova categoria. Esta orientação será exibida na área de seleção de categoria, por isso tente mantê-la abaixo de 200 caracteres. Até editar este texto ou criar tópicos, esta categoria não aparece na página de categorias.]" + post_template: "%{replace_paragraph}\n\nUse os seguintes parágrados para uma descrição longa, assim como para estabelecer diretrizes ou regras da categoria.\n\nAlguns pontos a ter em consideração em qualquer resposta a uma discussão:\n\n- Para que serve esta categoria? Porque alguém usaria esta categoria no seu tópico?\n\n- Como é que esta categoria se difere das demais categorias já existentes?\n\n- Precisamos desta categoria?\n\n- Deveríamos juntar esta categoria com outra, ou dividir esta categoria em mais categorias?\n" + errors: + uncategorized_parent: "Sem categoria não podem ter categorias de nível superior" + self_parent: "Uma subcategoria não pode ser superior a ela própria" + depth: "Não pode juntar uma subcategoria dentro de outra" cannot_delete: - uncategorized: "Não é possível apagar Sem Categoria" - has_subcategories: "Não é possível apagar esta categoria porque contém sub-categorias." + uncategorized: "Não é possível eliminar Sem Categoria" + has_subcategories: "Não é possível eliminar esta categoria porque contém sub-categorias." topic_exists: - one: "Não é possível apagar esta categoria porque contém 1 tópico. O tópico mais antigo é %{topic_link}." - other: "Não é possível apagar esta categoria porque contém %{count} tópicos. O tópico mais antigo é %{topic_link}." - topic_exists_no_oldest: "Não é possível apagar esta categoria porque a contagem de tópicos é de %{count}." + one: "Não é possível eliminar esta categoria porque contém 1 tópico. O tópico mais antigo é %{topic_link}." + other: "Não é possível eliminar esta categoria porque contém %{count} tópicos. O tópico mais antigo é %{topic_link}." + topic_exists_no_oldest: "Não é possível eliminar esta categoria porque a contagem de tópicos é de %{count}." trust_levels: newuser: - title: "usuário novo" + title: "novo utilizador" basic: title: "utilizador básico" regular: title: "membro" leader: - title: "regular" + title: "habitual" elder: title: "líder" + change_failed_explanation: "Tentou despromover %{user_name} para '%{new_trust_level}'. Contudo o Nível de Confiança é atualmente '%{current_trust_level}'. %{user_name} irá permanecer em '%{current_trust_level}' - se deseja despromover o utilizador, bloqueie o Nível de Confiança primeiro" rate_limiter: - slow_down: "Realizou esta acção demasiadas vezes, tente novamente mais tarde" - too_many_requests: "Nós possuímos um limite diário do número de vezes que uma ação pode ser tomada. Por favor aguarde %{time_left} antes de tentar novamente." + slow_down: "Realizou esta ação demasiadas vezes, tente novamente mais tarde" + too_many_requests: "Possuímos um limite diário de número de vezes que uma ação pode ser tomada. Por favor aguarde %{time_left} antes de tentar novamente." hours: one: "1 hora" other: "%{count} horas" @@ -227,10 +284,10 @@ pt: other: "%{count}d" about_x_months: one: "1mês" - other: "%{count}mon" + other: "%{count}mês" x_months: one: "1mês" - other: "%{count}mon" + other: "%{count}mês" about_x_years: one: "1ano" other: "%{count}anos" @@ -276,416 +333,1197 @@ pt: one: "há quase 1 ano atrás" other: "há quase %{count} anos atrás" password_reset: - no_token: "Desculpe, o seu token expirou. Por favor tenta redefinir a sua password novamente." - choose_new: "Por favor escolhe uma password nova" - choose: "Por favor escolha uma senha" - update: 'Actualizar Senha' - save: 'Definir Senha' - title: 'Redifinir Senha' - success: "Sua senha foi modificado com sucesso e você está conectado agora." - success_unapproved: "Senha modificada com sucesso." + no_token: "Desculpe, o seu símbolo expirou. Por favor tente redefinir a sua palavra-passe novamente." + choose_new: "Por favor escolha uma nova palavra-passe" + choose: "Por favor escolha uma palavra-passe" + update: 'Atualizar Palavra-passe' + save: 'Definir Palavra-passe' + title: 'Redefinir Palavra-passe' + success: "A sua palavra-passe foi alterada com sucesso e está agora ligado." + success_unapproved: "Palavra-passe modificada com sucesso." continue: "Continuar para %{site_name}" change_email: confirmed: "O seu email foi atualizado." please_continue: "Continuar para %{site_name}" - error: "Houve um erro ao alterar o seu endereço de email. Talvez o endereço já esteja sendo utilizado?" + error: "Ocorreu um erro ao alterar o seu endereço de email. Talvez o endereço já esteja a ser utilizado?" activation: - action: "Activar a sua conta" - already_done: "Desculpe, este link de confirmação não está mais válido. Talvez a sua conta já esteja ativa?" + action: "Ativar a sua conta" + already_done: "Pedimos desculpa, esta hiperligação de confirmação já não está válida. Talvez a sua conta já esteja ativa?" + please_continue: "A sua nova conta foi confirmada; será redirecionado para a página principal." continue_button: "Continuar para %{site_name}" welcome_to: "Bem-vindo a %{site_name}!" - approval_required: "Um moderador tem que aprovar a sua conta para que você possa acessar este fórum. Você receberá um email quando sua conta for aprovada!" + approval_required: "Um moderador tem que aprovar a sua conta antes de poder aceder a este fórum. Irá receber um email quando a sua conta for aprovada!" post_action_types: off_topic: - title: 'Off-Topic' - long_form: 'sinalizou isto como off-topic' + title: 'Fora de Contexto' + description: 'Esta mensagem não é relevante para a discussão corrente, conforme definido pelo título e pela primeira mensagem, e provavelmente deverá ser transferida para outro tópico.' + long_form: 'sinalizou isto como fora de contexto' spam: title: 'Spam' - long_form: 'sinalizado como spam' + description: 'Esta mensagem é publicidade. Não é útil nem relevante para o tópico atual, mas apenas de natureza promocional.' + long_form: 'sinalizou isto como spam' inappropriate: title: 'Inapropriado' - long_form: 'sinalizado como inapropriado' + description: 'Esta mensagem contém conteúdo que uma pessoa sensata iria considerar ofensivo, abusivo, ou como uma violação das diretrizes da nossa comunidade.' + long_form: 'sinalizou isto como inapropriado' notify_user: - email_title: 'O seu post em "%{title}"' + title: 'Mensagem Privada @{{username}}' + description: 'Esta mensagem contém algo sobre o qual quero falar com esta pessoa diretamente e em privado. Não representa motivo para ter alguma sinalização.' + long_form: 'utilizador com mensagens privadas' + email_title: 'A sua mensagem em "%{title}"' email_body: "%{link}\n\n%{message}" notify_moderators: - email_title: 'Uma postagem em "%{title}" requer atenção do moderador' + title: "Algo Mais" + description: 'Esta mensagem requer a atenção do moderador por outro motivo não mencionado acima.' + long_form: 'sinalizou isto para obter a atenção do moderador.' + email_title: 'Uma mensagem em "%{title}" requer a atenção do moderador' email_body: "%{link}\n\n%{message}" bookmark: title: 'Adicionar Marcador' - description: 'Adicionar um marcador a esta postagem' - long_form: 'adicionou um marcador esta postagem' + description: 'Adicionar um marcador a esta mensagem' + long_form: 'adicionou um marcador a esta mensagem' like: - title: 'Curtir' - description: 'Curtir esta postagem' - long_form: 'curtiu isto' + title: 'Gostar' + description: 'Gostar desta mensagem' + long_form: 'gostou disto' vote: - title: 'Vote' - description: 'Votar nesta postagem' - long_form: 'voto computado para esta postagem' + title: 'Votar' + description: 'Votar nesta mensagem' + long_form: 'votou nesta mensagem' topic_flag_types: spam: title: 'Spam' - description: 'Este tópico é um anúncio. Não é útil ou relevante para este site, mas é promocional por natureza.' - long_form: 'denunciar isto como spam' + description: 'Este tópico é um anúncio. Não é útil ou relevante para este sítio, apenas promocional na sua natureza.' + long_form: 'sinalizou isto como spam' inappropriate: - title: 'Impróprio' - description: 'Este tópico contém conteúdo que uma pessoa razoável consideraria ofensivo, abusivo ou uma violação das directrizes da nossa comunidades.' - long_form: 'denunciar isto como impróprio' + title: 'Inapropriado' + description: 'Este tópico contém conteúdo que uma pessoa sensata consideraria ofensivo, abusivo ou como uma violação das diretrizes da nossa comunidades.' + long_form: 'sinalizou isto como inapropriado' notify_moderators: - email_title: 'O tópico "%{title}" requer a atenção da moderação' + title: "Algo Mais" + description: 'Este tópico requer a atenção geral do moderador baseado nas diretrizes, TDS, ou por outro motivo não mencionado acima.' + long_form: 'sinalizou isto para obter a atenção do moderador' + email_title: 'O tópico "%{title}" requer a atenção do moderador' email_body: "%{link}\n\n%{message}" flagging: - you_must_edit: '

O seu post foi denunciado pela comunidade. Por favor consulte as sua mensagens privadas.

' - user_must_edit: '

Este post foi denunciado pela comunidade e está temporariamente oculto.

' + you_must_edit: '

A sua mensagem foi sinalizada pela comunidade. Por favor consulte as sua mensagens privadas.

' + user_must_edit: '

Esta mensagem foi sinalizada pela comunidade e está temporariamente oculta.

' archetypes: regular: - title: "Tópico Regular" + title: "Tópico Habitual" + banner: + message: + make: "Este tópico é agora uma faixa. Aparecerá no topo de cada página até ser dispensado pelo utilizador." + remove: "Este tópico já não é uma faixa. Deixará de aparecer no topo de cada página." unsubscribed: - title: 'Desinscrito' - description: "Você se desinscreveu. Não vamos mais enviar mensagens!" - oops: "Caso não seja esta sua intenção, clique em baixo." - not_found_description: "Desculpe, não conseguimos cancelar sua inscrição. É possível que o link no seu email tenha expirado." + title: 'Subscrição Cancelada' + description: "Cancelou a sua subscrição. Não iremos contactá-lo novamente!" + oops: "Caso esta não seja a sua intenção, clique em baixo." + error: "Erro a Cancelar a Subscrição" + preferences_link: "Pode também cancelar a subscrição dos emails de resumo na sua página de preferências" + different_user_description: "Está ligado como um utilizador diferente do que aquele para o qual o resumo foi enviado. Por favor, termine a sessão e tente novamente." + not_found_description: "Pedimos desculpa, não conseguimos cancelar a sua subscrição. É possível que a hiperligação no seu email tenha expirado." resubscribe: - action: "Re-Inscrever" - title: "Re-Inscrever!" - description: "Foste re-inscrito." + action: "Re-Subscrever!" + title: "Re-Subscrito!" + description: "Foi re-inscrito." reports: visits: - title: "Visitas do Usuário" + title: "Visitas do Utilizador" xaxis: "Dia" yaxis: "Número de visitas" signups: - title: "Usuários" + title: "Utilizadores" xaxis: "Dia" - yaxis: "Número de usuários novos" + yaxis: "Número de novos utilizadores" topics: title: "Tópicos" xaxis: "Dia" - yaxis: "Número de tópicos novos" + yaxis: "Número de novos tópicos" posts: - title: "Posts" + title: "Mensagens" xaxis: "Dia" - yaxis: "Número de posts novos" + yaxis: "Número de novas mensagens" likes: - title: "Curtidas" + title: "Gostos" xaxis: "Dia" - yaxis: "Número de novas curtidas" + yaxis: "Número de novos gostos" flags: title: "Sinalizações" xaxis: "Dia" yaxis: "Número de sinalizações" bookmarks: - title: "Marcadores Adicionados" + title: "Marcadores" xaxis: "Dia" - yaxis: "Número de novos marcadores adicionados" + yaxis: "Número de novos marcadores" starred: title: "Favoritos" xaxis: "Dia" yaxis: "Número de novos tópicos favoritos" users_by_trust_level: - title: "Usuários por Nível de Confiança" + title: "Utilizadores por Nível de Confiança" xaxis: "Nível de Confiança" - yaxis: "Número de Usuários" + yaxis: "Número de Utilizadores" emails: title: "Emails Enviados" xaxis: "Dia" yaxis: "Número de Emails" user_to_user_private_messages: - title: "Usuário-a-Usuário" + title: "Utilizador-a-Utilizador" xaxis: "Dia" - yaxis: "Número de mensagens particulares" + yaxis: "Número de mensagens privadas" system_private_messages: title: "Sistema" xaxis: "Dia" - yaxis: "Número de mensagens particulares" + yaxis: "Número de mensagens privadas" moderator_warning_private_messages: title: "Alerta ao Moderador" xaxis: "Dia" - yaxis: "Número de mensagens particulares" + yaxis: "Número de mensagens privadas" notify_moderators_private_messages: title: "Notificação aos Moderadores" xaxis: "Dia" - yaxis: "Número de mensagens particulares" + yaxis: "Número de mensagens privadas" notify_user_private_messages: - title: "Avisar utilizador" + title: "Notificar o Uilizador" xaxis: "Dia" - yaxis: "Número de mensagens particulares" + yaxis: "Número de mensagens privadas" top_referrers: - title: "Top Referrers" - xaxis: "Usuário" - num_clicks: "Clicks" + title: "As Melhores Referências" + xaxis: "Utilizador" + num_clicks: "Cliques" num_topics: "Tópicos" top_traffic_sources: - title: "Top Fontes de Tráfego" + title: "As Melhores Fontes de Tráfego" xaxis: "Domínio" - num_clicks: "Clicks" + num_clicks: "Cliques" num_topics: "Tópicos" - num_users: "Usuários" + num_users: "Utilizadores" top_referred_topics: - title: "Top Tópicos Citados" + title: "Os Melhores Tópicos Referenciados" xaxis: "Tópico" - num_clicks: "Clicks" + num_clicks: "Cliques" dashboard: - rails_env_warning: "Seu servidor está rodando no modo %{env}." + rails_env_warning: "O seu servidor está a executar em modo %{env}." ruby_version_warning: "Está a utilizar uma versão do Ruby 2.0.0 que é conhecida por ter alguns problemas. Por favor atualize para o nível 247 ou posterior." - host_names_warning: "O arquivo config/database.yml está usando hostname padrão localhost. Modifique para usar o hostname do seu site." - gc_warning: 'Seu servidor está rodando a coleta de lixo do ruby com os parâmetros padrão (default ruby garbage collection parameters), os quais não irão propiciar a melhor performance. Leia este tópico sobre ajuste de performance: Tuning Ruby and Rails for Discourse.' - sidekiq_warning: 'Sidekiq não está em execução. Muitas tarefas, como envio de emails, são executadas de forma assíncrona pelo sidekiq. Por favor certifique-se de que ao menos um processo sidekiq esteja execução. Aprenda sobre Sidekiq aqui.' - queue_size_warning: 'O número de tarefas agendadas é %{queue_size}, o que é alto. Isto pode indicar um problema com o(s) processo(s) Sidekiq, ou você pode estar precisando de mais Sidekiq workers.' - memory_warning: 'Seu servidor está rodando com menos de 1 GB de memória total. Pelo menos 1 GB é quantidade de memória recomendada.' - s3_config_warning: 'O servidor está configurado para fazer upload de arquivos para o s3, mas pelo menos uma destas configurações não está definida: s3_access_key_id, s3_secret_access_key ou s3_upload_bucket. Vá até as Configurações do Site e atualize estas definições. Veja "How to set up image uploads to S3?" para saber mais.' - image_magick_warning: 'O servidor está configurado para criar miniaturas de imagens grandes, mas o ImageMagick não está instalado. Instale o ImageMagick usando seu gerenciador de pacotes preferido ou acesse para fazer download da última versão.' - default_logo_warning: "Você não personalizou as imagens do logo para seu site. Atualize logo_url, logo_small_url, e favicon_url nas Configurações do Site." - contact_email_missing: "Você não forneceu um email de contato do site. Por favor atualize o contact_email nas Configurações do Site." - contact_email_invalid: "O email de contato do site é inválido. Por favor atualize o contact_email nas Configurações do Site." - title_nag: "A configuração do título nas Configurações do Site ainda estão no valor padrão. Por favor atualize com título do seu site em Configurações do Site." - site_description_missing: "A definição site_description está em branco. Escreva uma breve descrição do fórum nas Configurações do Site." - consumer_email_warning: "Seu site está configurado para usar Gmail (ou outro serviço de email para pessoas). Gmail limita a quantidade de emails que você pode enviar. Considere o uso de um serviço de envio de emails como mandrill.com para assegurar a entregabilidade dos emails enviados." - access_password_removal: "Seu site estava usando a definição access_password, que foi removida. As definições login_required e must_approve_users foram ativadas e devem ser usadas em substituição. Você pode alterá-las em Configurações do Site. Certifique-se de aprovar os usuários na lista de Usuários Pendentes. (Esta mensagem desaparecerá depois de 2 dias.)" - site_contact_username_warning: "A definição site_contact_username está em branco. Por favor atualize-a nas Configurações do Site. Coloque um nome de usuário de um usuário administrador o qual será o remetente das mensagens do sistema." - notification_email_warning: "A definição notification_email está em branco. Por favor atualize-a nas Configurações do Site." + host_names_warning: "O ficheiro config/database.yml está a utilizar o nome do servidor local por defeito. Modifique para usar o nome do servidor do seu sítio." + gc_warning: 'O seu servidor está a utilizar a recolha de lixo do ruby com os parâmetros por defeito, os quais não irão propocionar a melhor performance. Leia este tópico sobre ajuste de performance: Afinar Ruby and Rails para o Discourse.' + sidekiq_warning: 'Sidekiq não está em execução. Muitas tarefas, como envio de emails, são executadas de forma assíncrona pelo sidekiq. Por favor certifique-se de que pelo menos um processo sidekiq está em execução. Aprenda sobre Sidekiq aqui.' + queue_size_warning: 'O número de tarefas agendadas é %{queue_size}, o que é alto. Isto pode indicar um problema com o(s) processo(s) Sidekiq, ou pode estar a precisar de mais "Sidekiq workers".' + memory_warning: 'O seu servidor está a executar com menos de 1 GB de memória total. Pelo menos 1 GB é a quantidade de memória recomendada.' + enable_google_logins_warning: "Está a usar uma versão obsoleta da autenticação do OpenID da Google. A Google irá terminar o suporte do OpenID em 20 de Abril de 2015. Comece a usar o OAuth2 da Google assim que possível. Verifique este guia para aprender mais" + both_googles_warning: "Tanto enable_google_logins e enable_google_oauth2_logins estão assinalados nas configurações do sítio. Desative enable_google_logins." + google_oauth2_config_warning: 'O servidor está configurado para permitir inscrever-se e entrar com o Google OAuth2 (enable_google_oauth2_logins), mas o id e os valores privados do cliente não estão configurados. Vá às Configurações do Sítio e atualize as definições. Veja este guia para saber mais.' + facebook_config_warning: 'O servidor está configurado para permitir inscrever-se e entrar com o Facebook (enable_facebook_logins), mas o id e os valores privados da aplicação não estão configurados. Vá às Configurações do Sítio e atualize as definições. Veja este guia para saber mais.' + twitter_config_warning: 'O servidor está configurado para permitir inscrever-se e entrar com o Twitter (enable_twitter_logins), mas a chave e os valores privados não estão configurados. Vá às Configurações do Sítio e atualize as definições. Veja este guia para saber mais.' + github_config_warning: 'O servidor está configurado para permitir inscrever-se e entrar com o GitHub (enable_github_logins), mas o id e valores privados do cliente não estão configurados. Vá às Configurações do Sítio e atualize as definições. Veja este guia para saber mais.' + s3_config_warning: 'O servidor está configurado para fazer carregamento de ficheiros para o s3, mas pelo menos uma destas configurações não está definida: s3_access_key_id, s3_secret_access_key ou s3_upload_bucket. Vá até as Configurações do Sítio e atualize estas definições. Veja "How to set up image uploads to S3?" para saber mais.' + s3_backup_config_warning: 'O servidor está configurado para carregar cópias de segurança para s3, mas pelo menos uma das seguintes configurações não está definida: s3_access_key_id, s3_secret_access_key ou s3_backup_bucket. Vá às Configurações do Sítio e atualize as definições. Veja "How to set up image uploads to S3?" para saber mais.' + image_magick_warning: 'O servidor está configurado para criar miniaturas de imagens grandes, mas o ImageMagick não está instalado. Instale o ImageMagick usando o seu gestor de pacotes preferido ou aceda a para descarregar a última versão.' + failing_emails_warning: 'Há %{num_failed_jobs} tarefas de envio de emails que falharam. Verifique o ficheiro config/discourse.conf e assegure-se que as configurações do servidor de email estão corretas. Veja as tarefas que falharam no Sidekiq.' + consumer_email_warning: "O seu sítio está configurado para usar Gmail (ou outro serviço de email)O Gmail limita a quantidade de emails que pode enviar. Considere usar um serviço de envio de emails como mandrill.com para assegurar a entregabilidade dos emails enviados." content_types: education_new_reply: - title: "Educação do Usuário Novo: Primeiras Respostas" - description: "Pop up de orientação automaticamente exibido em cima do composer quando usuário novo começa a escrever as suas duas primeiras respostas." + title: "Educação do Novo Utilizador: Primeiras Respostas" + description: "Pop up de orientação automaticamente exibido em cima do compositor quando novos utilizadores começam a escrever as suas duas primeiras respostas." education_new_topic: - title: "Educação do Usuário Novo: Primeiros Tópicos" - description: "Pop up de orientação automaticamente exibido em cima do composer quando usuário novo começa a escrever os seus dois primeiros tópicos." + title: "Educação do Novo Utilizador: Primeiros Tópicos" + description: "Pop up de orientação automaticamente exibido em cima do compositor quando novos utilizadores começam a escrever os seus dois primeiros tópicos." usage_tips: - title: "Orientação para o novo utilizador" + title: "Orientação para o Novo Utilizador" description: "Orientação e informação essencial para novos utilizadores." welcome_user: - title: "Boas-vindas: Usuário Novo" - description: "Uma mensagem particular enviada automaticamente para todos os usuários novos quando eles se registram." + title: "Bem-vindo: Novo Utilizador" + description: "Mensagem privada enviada automaticamente para todos os novos utilizadores quando estes se registam." welcome_invite: - title: "Boas-vindas: Usuário Convidado" - description: "Uma mensagem particular enviada automaticamente para todos os usuários convidados quando eles aceitam o convite de outro usuário para participar." + title: "Bem-vindo: Utilizador Convidado" + description: "Mensagem privada enviada automaticamente para todos os novos utilizadores convidados assim que aceitam o convite de outro utilizador para participar." login_required_welcome_message: - title: "Boas-vindas: Login Solicitado" - description: "Mensagem de boas-vindas que é exibida para usuários que não estão logados quando a configuração de 'login requerido' está ativada." + title: "Início de Sessão Obrigatório: Mensagem de Boas-vindas" + description: "Mensagem de boas-vindas exibida a utilizadores que não têm sessão iniciada quando a configuração 'início de sessão obrigatório' está ativa." login_required: - title: "É requerido que você entre em sua conta: Página Inicial" - description: "Texto exibido para usuários não autorizados quando é se exige entrar na conta para acessar o site." + title: "Início de Sessão Obrigatório: Página Principal" + description: "Texto exibido a utilizadores não autorizados quando se exige início de sessão no sítio." + head: + title: "Cabeçalho HTML" + description: "HTML que será inserido dentro do código ." top: title: "Topo das páginas" + description: "HTML que será adicionado no topo de cada página (depois do cabeçalho, antes da navegação ou do título do tópico)." bottom: - title: "Rodapé das páginas" + title: "Final das páginas" + description: "HTML que será adicionado antes do código ." site_settings: + censored_words: "Palavras que serão automaticamente substituídas por ■■■■" + delete_old_hidden_posts: "Eliminar automaticamente quaisquer mensagens ocultas que permaneçam escondidas por mais de 30 dias." default_locale: "Idioma padrão para esta instância do Discourse (ISO 639-1 Code)" + allow_user_locale: "Permitir aos utilizadores escolherem a sua própria língua preferencial para a interface." + min_post_length: "Tamanho mínimo permitido por mensagem, em caracteres" + min_private_message_post_length: "Tamanho mínimo permitido para mensagens privadas, em caracteres" + max_post_length: "Tamanho máximo permitido por mensagem, em caracteres" + min_topic_title_length: "Tamanho mínimo permitido por título de cada tópico, em caracteres" + max_topic_title_length: "Tamanho máximo permitido por título de cada tópico, em caracteres" + min_private_message_title_length: "Tamanho mínimo permitido por título de cada tópico nas mensagens privadas, em caracteres" + min_search_term_length: "Tamanho mínimo válido para termos de pesquisa, em caracteres" + allow_uncategorized_topics: "Permitir a criação de tópicos sem categoria." + uncategorized_description: "Descrição da categoria sem classificação. Deixar em branco para nenhuma descrição." allow_duplicate_topic_titles: "Permitir tópicos com títulos idênticos e duplicados." - unique_posts_mins: "Quantos minutos antes para um utilizador poder criar uma postagem com o mesmo conteúdo outra vez?" - title: "Breve título deste site, usado na tag título." - site_description: "Descrever este site num frase, usada na tag de descrição meta," - queue_jobs: "APENAS DESENVOLVEDORES! ATENÇÃO! Por padrão, enfileira tarefas no sidekiq. Se desativado, seu site ficará defeituoso." - favicon_url: "Um favicon para o seu site" + unique_posts_mins: "Quantos minutos antes que um utilizador possa criar uma mensagem com o mesmo conteúdo outra vez?" + educate_until_posts: "Quando um utilizador começar a escrever as primeiras (n) novas mensagens, mostrar o painel pop-up de educação do novo utilizador no editor." + queue_jobs: "PROGRAMADORES APENAS! AVISO! Por defeito, fila de trabalhos no sidekiq. Se não estiver ativa, o seu sítio irá ficar corrompido." + crawl_images: "Recuperar imagens de URLs remotos para inserir o comprimento e largura corretos." + download_remote_images_to_local: "Converter imagens remotas em imagens locais ao descarregá-las; isto previne imagens corrompidas." + download_remote_images_threshold: "Espaço mínimo necessário em disco para descarregar imagens remotas localmente (em percentagem)" + disabled_image_download_domains: "Imagens remotas não serão descarregadas destes domínios. " + ninja_edit_window: "Durante (n) segundos após a publicação da mensagem, editá-la não irá criar uma nova versão no histórico de mensagens." + post_edit_time_limit: "O autor pode editar ou eliminar a sua mensagem por um período de (n) minutos após a publicação da mesma. Definir a 0 para ser para sempre." + edit_history_visible_to_public: "Permitir que todos vejam versões anteriores de uma mensagem editada. Quando desativado, apenas membros do pessoal podem ver." + delete_removed_posts_after: "Mensagens removidas pelo autor serão automaticamente eliminadas após um período de (n) horas. Se estiver definido a 0, as mensagens serão eliminadas imediatamente." + max_image_width: "Largura máxima de miniaturas de imagens numa mensagem." + max_image_height: "Altura máxima de miniaturas de imagens numa mensagem." + category_featured_topics: "Número de tópicos visíveis por categoria na página de /categorias. Após a alteração deste valor, irá demorar até 15 minutos para que a página de categorias fique atualizada." + show_subcategory_list: "Mostrar lista de subcategorias em vez de lista de tópicos quando inserida uma categoria." + fixed_category_positions: "Se estiver marcado, irá conseguir organizar as categorias por uma ordem fixa. Se não estiver marcado, as categorias são listadas por ordem de actividade." + add_rel_nofollow_to_user_content: "Adicionar a etiqueta rel nofollow em todos os conteúdos submetidos pelo utilizador, excepto para hiperligações internas (incluindo domínios pai). Se mudar isto, terá que atualizar todas as suas mensagens com: \"rake posts:rebake\"" + exclude_rel_nofollow_domains: "Lista de domínios delimitada em que nofollow não é adicionado (tld.com irá automaticamente permitir sub.tld.com)" + post_excerpt_maxlength: "Tamanho máximo do excerto/sumário de uma mensagem." + post_onebox_maxlength: "Tamanho máximo de uma mensagem Discourse de caixa única, em caracteres." + onebox_domains_whitelist: "Lista de domínios que permitem colocar em caixa única; estes domínios devem suportar OpenGraph ou oEmbed. Teste-os em http://iframely.com/debug" + digest_logo_url: "Logótipo alternativo usado no topo do resumo de email do seu sítio. Se for deixado em branco `logo_url` será usado. ex: http://exemplo.com/logo.png" + favicon_url: "Um favicon para o seu sítio, veja http://en.wikipedia.org/wiki/Favicon" apple_touch_icon_url: "Ícone usado para dispositivos Apple. Tamanho recomendado é 144px por 144px." + notification_email: "Para: endereço de email usado ao enviar emails essenciais do sistema. O domínio especificado aqui deverá ter SPF, DKIM e registos PTR inversos configurados corretamente para a chegada do email." email_custom_headers: "A lista delimitada por barras verticais de cabeçalhos de e-mail personalizados" - enable_long_polling: "O sistema de mensagens das notificações pode fazer solicitações longas." - anon_polling_interval: "Com que frequencia os clientes não registrados podem solicitar o servidor em milisegundos" - auto_track_topics_after: "Quantos milisegundos esperar antes que um tópico seja automaticamente rastreado (0 para sempre, -1 para nunca)" - new_topic_duration_minutes: "Quantos minutos um tópico permanece considerado como novo (-1 para sempre, -2 para desde a última visita)" - flags_required_to_hide_post: "As postagens vão ser escondidos automaticamente quando o númereo de sinalizações atingir este número (0 para nunca)" - max_topics_in_first_day: "O número máximo de tópicos que é permitido a um utilizador no seu primeiro dia no Fórum." - max_replies_in_first_day: "O número máximo de mensagens que é permitido a um utilizador no seu primeiro dia no Fórum." - notify_mods_when_user_blocked: "Se um usuário for bloqueado de forma automática, enviar uma mensagem para todos os moderadores." - enable_noscript_support: "Ativar suporte a tag <noscript>" - post_menu: "A ordem dos items no menu da postagem." - track_external_right_clicks: "Rastrear cliques externos que são clicados com o botão direito (ex: abrir em nova aba) desativado por padrão, pois tem que reescrever urls, quebrando a usabilidade" - port: "Se você quiser especificar a porta na URL. Útil no modo de desenvolvimento. Deixe em branco para nada." - force_hostname: "Se você quiser especificar um hostname na URL. Útil no modo de desenvolvimento. Deixe em branco para nada." - invite_expiry_days: "Quantos dias as chaves de convite são válidas." + email_subject: "Formato de assunto personalizável para emails padrão. Veja https://meta.discourse.org/t/customize-subject-format-for-standard-emails/20801" + use_https: "Deverá o url completo do sítio (Discourse.base_url) ser http ou https? NÃO ATIVAR ISTO A NÃO SER QUE HTTPS JÁ ESTEJA CONFIGURADO E A FUNCIONAR!" + summary_score_threshold: "Pontuação mínima necessária para que uma mensagem seja incluída em 'Resumir Este Tópico'" + summary_posts_required: "Número mínimo de mensagens num tópico antes que 'Resumir Este Tópico' seja ativo." + summary_likes_required: "Número mínimo de gostos num tópico antes que 'Resumir Este Tópico' seja ativo." + summary_percent_filter: "Quando um utilizador clica em 'Resumir Este Tópico', mostrar as melhores % de mensagens" + summary_max_results: "Número máximo de mensagens devolvidas por 'Resumo deste Tópico'" + enable_private_messages: "Permitir que utilizadores de nível de confiança 1 possam criar e responder a mensagens privadas" + enable_long_polling: "O sistema de mensagens usado para notificações pode fazer solicitações longas" + long_polling_base_url: "URL base usada para solicitação ao servidor (quando um CDN serve conteúdo dinâmico, certifique-se de configurá-lo para a 'pull' original) ex: http://origem.sítio.com" + long_polling_interval: "Quantidade de tempo que um servidor deve esperar antes de notificar os clientes quando não há dados para serem enviados (apenas utilizadores ligados)" + polling_interval: "Quando não está a ocorrer uma solicitação ao servidor, com que frequência devem os clientes ligados requerer uma atualização, em milissegundos" + anon_polling_interval: "Com que frequência os clientes não registados podem fazer solicitações ao servidor, em milisegundos" + background_polling_interval: "Com que frequência deverão os clientes solicitar o servidor, em milissegundos (quando a janela está em plano de fundo)" + auto_track_topics_after: "Milissegundos antes de um tópico ser automaticamente acompanhado, os utilizadores podem substituir (0 para sempre, -1 para nunca)" + new_topic_duration_minutes: "Número de minutos que um tópico é considerado como novo, os utilizadores podem substituir (-1 para sempre, -2 para desde a última visita)" + flags_required_to_hide_post: "Número de sinalizações que fazem com que uma mensagem seja automaticamente oculta e que uma MP seja enviada para o utilizador (0 para nunca)" + cooldown_minutes_after_hiding_posts: "Número de minutos que o utilizador deve esperar antes de poder editar uma mensagem oculta devido a sinalizações por parte da comunidade" + max_topics_in_first_day: "O número máximo de tópicos que é o utilizador pode criar no seu primeiro dia no Fórum." + max_replies_in_first_day: "Número máximo de mensagens que um utilizador pode criar no seu primeiro dia no Fórum." + num_flags_to_block_new_user: "Se uma mensagem de um novo utilizador receber esta quantidade de sinalizações da parte de num_users_to_block_new_user diferentes utilizadores, esconder todas as suas mensagens e prevenir mensagens futuras. 0 para desativar." + num_users_to_block_new_user: "Se uma mensagem de um novo utilizador receber num_flags_to_block_new_user sinalizações de spam deste número de diferentes utilizadores, esconder todas as suas mensagens e prevenir mensagens futuras. 0 para desativar." + notify_mods_when_user_blocked: "Se um utilizador for bloqueado de forma automática, enviar uma mensagem a todos os moderadores." + flag_sockpuppets: "Se um novo utilizador responde a um tópico a partir do mesmo endereço IP do novo utilizador que iniciou o tópico, sinalizar ambas as mensagens como potencial spam." + traditional_markdown_linebreaks: "Utilize tradicionais quebras de linha no Markdown, que requer dois espaços no final para uma quebra de linha." + post_undo_action_window_mins: "Número de minutos durante o qual os utilizadores têm permissão para desfazer ações numa mensagem (gostos, sinalizações, etc)." + must_approve_users: "O pessoal deverá aprovar todas as contas dos novos utilizadores antes destes terem permissão para aceder ao sítio." + ga_tracking_code: "Código de acompanhamento do Google Analytics (ga.js), ex: UA-12345678-9; ver http://google.com/analytics" + ga_domain_name: "Nome de domínio da Google Analytics (ga.js), ex: omeusitio.com; ver http://google.com/analytics" + ga_universal_tracking_code: "Código de acompanhamento do Google Universal Analytics (analytics.js), ex: UA-12345678-9; ver http://google.com/analytics" + ga_universal_domain_name: "Nome de domínio do Google Universal Analytics (analytics.js), ex: omeusitio.com; ver http://google.com/analytics" + enable_escaped_fragments: "Ir à API do Google Ajax-Crawling se nenhum webcrawler for detetado. Ver https://support.google.com/webmasters/answer/174992?hl=en" + enable_noscript_support: "Ativar o suporte básico para pesquisas em motores de pesquisa através da etiqueta noscript" + allow_moderators_to_create_categories: "Permitir que os moderadores criem novas categorias" + cors_origins: "Permitidos compartilhamentos de recursos de origem-cruzada (CORS). Cada origem deve incluir http:// ou https://. A variável de ambiente DISCOURSE_ENABLE_CORS tem que estar configurada a verdadeiro para ativar o CORS." + post_menu: "Determine que itens irão aparecer no menu de mensagens, e em que ordem. Exemplo\ngostar|editar|sinalizar|eliminar|partilhar|marcar|responder" + post_menu_hidden_items: "Os elementos do menu a serem escondidos por defeito no menu de mensagens a não ser que se clique na elipse de expansão." + share_links: "Determinar que elementos aparecem no diálogo de partilha, e em que ordem." + track_external_right_clicks: "Acompanhar cliques externos que são clicados com o botão direito (ex: abrir num novo separador) desativado por padrão pois este reescreve URLs" + send_welcome_message: "Enviar a todos os novos utilizadores uma mensagem privada de boas-vindas com um guia de inicio rápido." + suppress_reply_directly_below: "Não mostrar a contagem de respostas expansível quando há apenas uma única resposta diretamente abaixo desta publicação." + suppress_reply_directly_above: "Não mostrar em-resposta-a expansível quando há apenas uma única resposta diretamente acima desta publicação." + suppress_reply_when_quoting: "Não mostraR em-resposta-a expansível numa mensagem quando esta cita uma resposta." + max_reply_history: "Número máximo de respostas a serem expandidas quando se expande em-resposta-a" + experimental_reply_expansion: "Esconder respostas intermédias ao expandir uma resposta-a (experimental)" + topics_per_period_in_top_summary: "Número de tópicos principais mostrados no resumo padrão de tópicos principais." + topics_per_period_in_top_page: "Número de tópicos principais mostrados em 'Mostrar Mais' tópicos principais expandido." + redirect_users_to_top_page: "Redirecionar automaticamente os utilizadores novos e ausentes por períodos longos para o topo da página." + show_email_on_profile: "Mostrar o email do utilizador no seu perfil (apenas visível para si próprio e para o pessoal)" + email_token_valid_hours: "Os símbolos para palavra-passe esquecida /conta ativada são válidos por (n) horas." + email_token_grace_period_hours: "Os símbolos para palavra-passe esquecida /conta ativada são válidos por um período de carência de (n) horas após serem recuperados" + enable_badges: "Ativar o sistema de distintivos" + allow_index_in_robots_txt: "Especificar em robots.txt que este sítio permite ser indexado pelos motores de pesquisa." + email_domains_blacklist: "Lista de domínios de email que os utilizadores não podem usar para registo de contas. Exemplo: mailinator.com trashmail.net" + email_domains_whitelist: "Lista de domínios de email que os utilizadores DEVEM usar para registar contas. AVISO: Utilizadores com domínios de email diferentes dos listados não serão permitidos!" + forgot_password_strict: "Não informar os utilizadores da existência da conta quando estes utilizam o diálogo de password esquecida." + version_checks: "Fazer o ping do Discourse Hub para atualização de versões e mostrar mensagens sobre novas versões no painel /admin" + new_version_emails: "Enviar um email para o endereço contact_email quando uma nova versão do Discourse estiver disponível." + port: "PROGRAMADOR APENAS! AVISO! Utilize esta porta HTTP em vez da porta 80. Deixar em branco para usar a porta padrão 80." + force_hostname: "PROGRAMADOR APENAS! AVISO! Especifique um nome de servidor no URL. Deixe em branco para utilizar o valor por defeito." + invite_expiry_days: "Durante quantos dias as chaves de convite são válidas." + invite_passthrough_hours: "Quanto tempo pode um utilizador utilizar uma chave de convite previamente recuperada, em horas" + invite_only: "O registo público está desativado, todos os novos utilizadores devem ser explicitamente convidados por outros membros ou pelo pessoal." + login_required: "Requerer autenticação para ler conteúdo neste sítio, não permitir acesso anónimo." + min_username_length: "Tamanho mínimo do nome de utilizador, em caracteres. AVISO: QUAISQUER UTILIZADORES COM NOMES MAIS PEQUENOS QUE ISTO SERÃO PROIBIDOS DE ACEDER AO SÍTIO." + max_username_length: "Tamanho máximo do nome de utilizador, em caracteres. AVISO: QUAISQUER UTILIZADORES COM NOMES MAIORES QUE ISTO SERÃO PROIBIDOS DE ACEDER AO SÍTIO." + min_password_length: "Tamanho mínimo da palavra-passe." + block_common_passwords: "Não permitir palavras-passe que estejam nas 10,000 palavras-passe mais comuns." + enable_sso_provider: "Implementa o protocolo SSO para o Discourse em /session/sso_provider endpoint, requer que sso_secret esteja configurado" + sso_url: "URL da inscrição única" + sso_overrides_email: "Substitui o email local por um email externo de um SSO (AVISO: discrepâncias podem ocorrer devido a normalização de emails locais)" + sso_overrides_username: "Substitui nomes de utilizador locais por nomes de utilizadores externos de um SSO (AVISO: discrepâncias podem ocorrer diferenças no nome de utilizador length/requirements)" + sso_overrides_name: "Substitui o nome local por um nome externo de um SSO (AVISO: discrepâncias podem ocorrer devido à normalização de nomes locais)" + sso_overrides_avatar: "Substitui o avatar do utilizador com avatares externos de um SSO. Se ativo, é altamente recomendada a desativação de allow_uploaded_avatars" + enable_local_logins: "Ativar contas com nome de utilizador e senha de forma local (Nota: isto tem que estar ativo para que os convites funcionem)" + allow_new_registrations: "Permitir registo de novos utilizadores. Desmarcar isto para prevenir que alguém crie uma nova conta." + enable_google_logins: "(obsoleto) Ativa a autenticação Google. Este é um método de autenticação OpenID que o Google deixou de atualizar e passou a estar obsoleto. Novas instalações NÃO irão funcionar com isto. Utilize o Google Oauth2 como alternativa. Instalações existentes deverão passar a utilizar o Google Oauth2 antes de 20 de Abril de 2015." enable_yahoo_logins: "Ativar autenticação pelo Yahoo" + enable_google_oauth2_logins: "Ativar autenticação Google Oauth2. Este é o método que permite a autenticação que a Google atualmente suporta. Requer chave e segredo." + google_oauth2_client_id: "ID do cliente da sua aplicação Google." + google_oauth2_client_secret: "Segredo do cliente da sua aplicação Google." enable_twitter_logins: "Ativar autenticação pelo Twitter, requer twitter_consumer_key e twitter_consumer_secret" - twitter_consumer_key: "consumer key registrada em dev.twitter.com (usada para fazer autenticação via twitter)" - twitter_consumer_secret: "consumer secret registrada em dev.twitter.com (usada para fazer autenticação via twitter)" + twitter_consumer_key: "Chave de consumidor para autenticação pelo Twitter, registada em http://dev.twitter.com" + twitter_consumer_secret: "Segredo do Consumidor para a autenticação pelo Twitter, registada em http://dev.twitter.com" enable_facebook_logins: "Ativar autenticação pelo Facebook, requer facebook_app_id e facebook_app_secret" - facebook_app_id: "app id, registrado em https://developers.facebook.com/apps (used for facebook auth)" - facebook_app_secret: "app secret, registrado em https://developers.facebook.com/apps (used for facebook auth)" + facebook_app_id: "Id App usado para autenticação pelo Facebook, registado em https://developers.facebook.com/apps" + facebook_app_secret: "Segredo App para autenticação com o Facebook, registado em https://developers.facebook.com/apps" enable_github_logins: "Ativar autenticação via Github, requer github_client_id e github_client_secret" - github_client_id: "Client id para autenticação via Github, registrado em https://github.com/settings/applications" - github_client_secret: "Client secret para autenticação via Github, registrado em https://github.com/settings/applications" - active_user_rate_limit_secs: "Qual a frequencia de atualização do campo 'última vez visto em', em segundos." - previous_visit_timeout_hours: "Quanto tempo uma visita dura antes de considerarmos como 'última visita', em horas." - min_trust_to_create_topic: "O nível de confiança mínimo necessário para criar um novo tópico." + github_client_id: "Id do cliente para autenticação pelo Github, registado em https://github.com/settings/applications" + github_client_secret: "Segredo do Cliente para autenticação pelo Github, registado em https://github.com/settings/applications" + allow_restore: "Permitir o restauro, que pode substituir TODOS os dados do sítio. Deixar a falso a menos que planeie restaurar uma cópia de segurança" + maximum_backups: "Valor máximo de cópias de segurança a serem guardadas em disco. Cópias de Segurança antigas são automaticamente eliminadas." + backup_daily: "Criar automaticamente uma cópia de segurança local, uma vez por dia." + enable_s3_backups: "Carregar cópias de segurança para S3 quando completo. IMPORTANTE: requer credenciais S3 válidas inseridas nas configurações dos ficheiros." + s3_backup_bucket: "Balde remoto para guardar cópias de segurança. AVISO: Certifique-se que este é um balde privado." + active_user_rate_limit_secs: "Qual a frequência de atualização do campo 'última vez visto em', em segundos." + verbose_localization: "Mostrar extensas dicas de localização na IU" + previous_visit_timeout_hours: "Quanto tempo dura uma visita antes de a considerarmos como 'visita anterior', em horas." + rate_limit_create_topic: "Após a criação de um tópico, os utilizadores devem esperar (n) segundos antes de criarem um novo tópico." + rate_limit_create_post: "Após a publicação, os utilizadores devem esperar (n) segundos antes de criarem outra mensagem." + rate_limit_new_user_create_topic: "Após a criação de um tópico, os novos utilizadores devem esperar (n) segundos antes de criarem um novo tópico." + rate_limit_new_user_create_post: "Após a publicação, os novos utilizadores devem esperar (n) segundos antes de criarem novas mensagens." + max_likes_per_day: "Número máximo de gostos por utilizador por dia." + max_flags_per_day: "Número máximo de sinalizações por utilizador por dia." + max_bookmarks_per_day: "Número máximo de marcadores por utilizador por dia." + max_edits_per_day: "Número máximo de edições por utilizador por dia." + max_topics_per_day: "Número máximo de tópicos que um utilizador pode criar por dia." + max_private_messages_per_day: "Número máximo de mensagens privadas que os utilizadores podem criar por dia." + suggested_topics: "Número de tópicos sugeridos mostrados no final de um tópico." + limit_suggested_to_category: "Apenas mostrar tópicos da categoria atual nos tópicos sugeridos." + clean_up_uploads: "Remover carregamentos orfãos não referenciados para prevenir alojamento ilegal. AVISO: poderá querer criar uma cópia de segurança da diretoria /uploads antes de ativar esta configuração." + clean_orphan_uploads_grace_period_hours: "Período de carência (em horas) antes de um carregamento orfão ser removido." + purge_deleted_uploads_grace_period_days: "Período de carência (em dias) antes de um carregamento eliminado ser apagado." + purge_unactivated_users_grace_period_days: "Período de carência (em dias) antes de um utilizador que não ativou a sua conta ser eliminado." + enable_s3_uploads: "Coloca os carregamentos no armazenamento Amazon S3. IMPORTANTE: requer credenciais S3 válidas (tanto id de chave de acesso como a chave de acesso secreta)." + s3_use_iam_profile: 'Utiliza AWS EC2 IAM para recuperar chaves. NOTA: a ativação irá substituir as configurações de "id da chave de acesso s3" e a "chave de acesso secreta s3".' + s3_upload_bucket: "Nome do balde da Amazon S3 que contém os ficheiros carregados. AVISO: tem que ser minúsculo, sem pontos e sem sublinhados." + s3_access_key_id: "Id da chave de acesso Amazon S3 que será usada para carregar imagens." + s3_secret_access_key: "Chave de acesso secreta Amazon S3 que será usada para carregar imagens." + s3_region: "Nome da região Amazon S3 que será usada para carregar imagens." + enable_flash_video_onebox: "Ativar a incorporação de hiperligações swf e flv (Adobe Flash) em caixas únicas. AVISO: pode introduzir riscos de segurança." + default_invitee_trust_level: "Nível de Confiança padrão (0-4) para utilizadores convidados." + default_trust_level: "Nível de Confiança padrão (0-4) para todos os novos utilizadores." + tl1_requires_topics_entered: "Quantos tópicos um novo utilizador deve introduzir antes de ser promovido para o Nível de Confiança 1." + tl1_requires_read_posts: "Quantas mensagens um novo utilizador deve ler antes de ser promovido para o Nível de Confiança 1." + tl1_requires_time_spent_mins: "Quantos minutos um novo utilizador deve ler antes de ser promovido para o Nível de Confiança 1." + tl2_requires_topics_entered: "Quantos tópicos um utilizador deve introduzir antes de ser promovido para o Nível de Confiança 2." + tl2_requires_read_posts: "Quantas mensagens um utilizador deve ler antes de ser promovido para o Nível de Confiança 2." + tl2_requires_time_spent_mins: "Quantos minutos um utilizador deve ler antes de ser promovido para o Nível de Confiança 2." + tl2_requires_days_visited: "Durante quantos dias um utilizador deve visitar o sítio antes de ser promovido para o Nível de Confiança 2." + tl2_requires_likes_received: "Quantos gostos um utilizador deve receber antes de ser promovido para o Nível de Confiança 2." + tl2_requires_likes_given: "Quantos gostos um utilizador deve atribuir antes de ser promovido para o Nível de Confiança 2." + tl2_requires_topic_reply_count: "Quantos tópicos um utilizador deve responder antes de ser promovido para o Nível de Confiança 2." + tl3_requires_days_visited: "Número mínimo de dias que o utilizador necessita de ter visitado o sítio nos últimos 100 dias para se qualificar à promoção para o Nível de Confiança 3. (0 a 100)" + tl3_requires_topics_replied_to: "Número mínimo de tópicos que o utilizador necessita de ter respondido nos últimos 100 dias para se qualificar à promoção para o Nível de Confiança 3. (0 ou mais)" + tl3_requires_topics_viewed: "Percentagem de tópicos criados nos últimos 100 dias que o utilizador precisa de ter visto para se qualificar à promoção para o Nível de Confiança 3. (0 a 100)" + tl3_requires_posts_read: "Percentagem de mensagens criadas nos últimos 100 dias que o utilizador precisa de ter visto para se qualificar à promoção para o Nível de Confiança 3. (0 a 100)" + tl3_requires_topics_viewed_all_time: "Número mínimo total de tópicos que o utilizador deve ter visto para se qualificar ao Nível de Confiança 3." + tl3_requires_posts_read_all_time: "Número mínimo total de mensagens que o utilizador deve ter lido para se qualificar ao Nível de Confiança 3." + tl3_requires_max_flagged: "O utilizador não deverá ter mais do que x mensagens marcadas por x diferentes utilizadores nos últimos 100 dias para se qualificar à promoção para o nível de confiança 3, onde x é o valor definido. (0 ou superior)" + tl3_promotion_min_duration: "Número mínimo de dias em que a promoção para Nível de Confiança 3 dura antes que o utilizador possa ser despromovido para o Nível de Confiança 2." + tl3_requires_likes_given: "Número mínimo de gostos que o utilizador deve ter atribuído nos últimos 100 dias para se qualificar à promoção para o Nível de Confiança 3." + tl3_requires_likes_received: "Número mínimo de gostos que o utilizador deve ter recebido nos últimos 100 dias para se qualificar à promoção para o Nível de Confiança 3." + tl3_links_no_follow: "Não remover rel=nofollow das hiperligações publicadas por utilizadores com Nível de Confiança 3." + min_trust_to_create_topic: "O Nível de Confiança mínimo necessário para criar um novo tópico." + min_trust_to_edit_wiki_post: "O nível mínimo de confiança necessário para editar mensagens marcadas como wiki." + newuser_max_links: "Quantas hiperligações um novo utilizador pode adicionar a uma mensagem." + newuser_max_images: "Quantas imagens um novo utilizador pode adicionar a uma mensagem." + newuser_max_attachments: "Quantos anexos um novo utilizador pode adicionar a uma mensagem." + newuser_max_mentions_per_post: "Número máximo de notificações @nome que um novo utilizador pode usar numa mensagem." + newuser_max_replies_per_topic: "Número máximo de respostas que um novo utilizador pode fazer num único tópico até alguém lhe responder." + max_mentions_per_post: "Número máximo de notificações @nome que qualquer pessoa pode usar numa mensagem." + create_thumbnails: "Criar imagens miniatura e lightbox que são demasiado largas para caber numa mensagem." + email_time_window_mins: "Esperar (n) minutos antes de enviar quaisquer emails de notificação, para dar aos utilizadores a hipótese de editarem e finalizarem as suas mensagens." + email_posts_context: "Quantas respostas prévias a serem incluídas como contexto em emails de notificação." + flush_timings_secs: "Com que frequência o servidor é alimentado com dados de sincronização, em segundos." + max_word_length: "Tamanho máximo permitido de palavras, em caracteres, no título de um tópico." + title_min_entropy: "Entropia mínima (caracteres únicos, contagem não-inglesa para mais) necessária para o título de um tópico." + body_min_entropy: "Entropia mínima (caracteres únicos, contagem não-inglesa para mais) necessária para o corpo de uma mensagem." title_fancy_entities: "Converter caracteres ASCII comuns em entidades HTML nos títulos dos tópicos, ala SmartyPants http://daringfireball.net/projects/smartypants/" + min_title_similar_length: "Tamanho mínimo do título antes de ser verificado por tópicos semelhantes." + min_body_similar_length: "Tamanho mínimo do corpo de uma mensagem antes de ser verificado por tópicos semelhantes." + category_colors: "Lista de valores hexadecimais das cores permitidas nas categorias." + max_image_size_kb: "Tamanho máximo da imagem carregada em kB. Este deverá ser configurado em nginx (client_max_body_size) / apache ou também proxy." + max_attachment_size_kb: "Tamanho máximo dos anexos carregados em kB. Este deverá ser configurado em nginx (client_max_body_size) / apache ou também proxy." + authorized_extensions: "Lista de extensões permitidas para carregamento (utilizar '*' para ativar todos os tipos de ficheiros)" + max_similar_results: "Quantos tópicos semelhantes a serem exibidos acima do editor ao compor um novo tópico. A comparação é baseada no título e no corpo." title_prettify: "Prevenir erros comuns em títulos, incluindo caps-lock ligado, primeira letra minúscula, excesso de ! e ?, pontos extras no final, etc." - faq_url: "Se você possui um FAQ hospedado em outro local e que você queira usar, forneça a URL completa aqui." - tos_url: "Se você tem um documento de Termos de Serviço hospedado em algum outro local que você queira usar, forneça a URL completa aqui." - privacy_policy_url: "Se você tem um documento de Política de Privacidade hospedado em algum outro local que você queira usar, forneça a URL completa aqui." - newuser_spam_host_threshold: "Quantas vezes um usuário pode postar um link para o mesmo site dentro de`newuser_spam_host_posts` posts antes de ser considerado spam." - delete_all_posts_max: "Número máximo de postagens que podem ser apagadas de uma vez com o botão Remover Todos Postagens. Se um usuário tiver mais postagens do que este número, as postagens não poderão ser todas removidas de uma vez e o usuário não poderá ser removido." - username_change_period: "O número de dias após o registro para que as contas possam mudar o seu nome de usuário (0 para não permitir a mudança de nome de usuário)." - email_editable: "Permitir que os usuários alterem o seu endereço de e-mail após o registro." - default_digest_email_frequency: "Quantas vezes os usuários recebem emails de resumo por padrão. Eles podem alterar essa configuração em suas preferências." - enable_mobile_theme: "Os dispositivos móveis usam um tema mobile-friendly, com a possibilidade de mudar para o site completo. Desative isso se você quiser usar um estilo personalizado que é totalmente responsivo." + topic_views_heat_low: "Após todas estas visualizações, o campo de visitas fica ligeiramente destacado." + topic_views_heat_medium: "Após todas estas visualizações, o campo de visitas fica moderadamente destacado." + topic_views_heat_high: "Após todas estas visualizações, o campo de visitas fica fortemente destacado." + cold_age_days_low: "Após todos estes dias de conversação, a data da última atividade fica ligeiramente destacada." + cold_age_days_medium: "Após todos estes dias de conversação, a data da última atividade fica moderadamente destacada." + cold_age_days_high: "Após todos estes dias de conversação, a data da última atividade fica fortemente destacada." + history_hours_low: "Uma mensagem editada dentro destas horas tem o indicador de edição ligeiramente destacado." + history_hours_medium: "Uma mensagem editada dentro destas horas tem o indicador de edição moderadamente destacado." + history_hours_high: "Uma mensagem editada dentro destas horas tem o indicador de edição fortemente destacado." + topic_post_like_heat_low: "Depois da proporção gostos:mensagem exceder este rácio, o campo de contagem da mensagem irá ficar ligeiramente destacado." + topic_post_like_heat_medium: "Depois da proporção gostos:mensagem exceder este rácio, o campo de contagem da mensagem irá ficar moderadamente destacado." + topic_post_like_heat_high: "Depois da proporção gostos:mensagem exceder este rácio, o campo de contagem da mensagem irá ficar fortemente destacado." + faq_url: "Se possui uma FAQ alojada noutro local e que queira usar, forneça a URL completa aqui." + tos_url: "Se tem um documento de Termos de Serviço alojado noutro local e que queira usar, forneça a URL completa aqui." + privacy_policy_url: "Se tem um documento de Política de Privacidade alojado noutro local e que queira usar, forneça a URL completa aqui." + newuser_spam_host_threshold: "Quantas vezes um novo utilizador pode publicar uma hiperligação no mesmo servidor dentro das suas mensagens `newuser_spam_host_posts` antes de ser considerada como spam." + white_listed_spam_host_domains: "Lista de domínios excluídos dos testes de spam. Novos utilizadores nunca irão ser restritos da criação de mensagens com hiperligações a esses domínios." + staff_like_weight: "Quanto é o fator de ponderação extra a dar aos gostos provenientes do pessoal." + levenshtein_distance_spammer_emails: "Ao fazer a correspondência de e-mails de spam, o número de caracteres de diferença que ainda permitirá uma correspondência difusa." + max_new_accounts_per_registration_ip: "Se já há (n) contas com nível de confiança 0 a partir deste IP (e nenhum é um membro do pessoal ou em nível de confiança 2 ou superior), parar de aceitar novos registos a partir desse IP." + min_ban_entries_for_roll_up: "Ao clicar no botão Agrupar, irá criar uma nova entrada de sub-rede banida se houver pelo menos (N) entradas." + max_age_unmatched_emails: "Eliminar entradas de email não encontradas após (N) dias." + max_age_unmatched_ips: "Eliminar entradas IP não encontradas após (N) dias." + num_flaggers_to_close_topic: "Número mínimo de sinalizações únicas que são necessárias para automaticamente pausar um tópico para intervenção." + num_flags_to_close_topic: "Número mínimo de sinalizações ativas que são necessárias para automaticamente pausar um tópico para intervenção." + reply_by_email_enabled: "Ativar respostas aos tópicos por email." + reply_by_email_address: "Modelo para endereços emails recebidos com função resposta por email, por exemplo: %{reply_key}@resposta.exemplo.com ou resposta+%{reply_key}@exemplo.com" + disable_emails: "Impedir que Discourse envie qualquer tipo de emails" + strip_images_from_short_emails: "Remover imagens de emails cujo tamanho seja inferior a 2800 Bytes" + short_email_length: "Comprimento de email curto, em Bytes" + pop3_polling_enabled: "Solicitação através de POP3 para respostas de emails" + pop3_polling_ssl: "Utilize SSL ao ligar a um servidor POP3. (Recomendado)" + pop3_polling_period_mins: "Período em minutos entre a verificação da conta POP3 para o email. NOTA: requer reinicialização." + pop3_polling_port: "Porto para resgatar uma conta POP3." + pop3_polling_host: "Servidor para solicitações de email via POP3." + pop3_polling_username: "Nome de utilizador para a conta POP3 para resgatar emails." + pop3_polling_password: "Palavra-passe para a conta POP3 solicitar emails ao servidor." + email_in: "Permitir que utilizadores publiquem novos tópicos através de email (requer pop3). Configurar os endereços no separador \"Configurações\" de cada categoria." + email_in_min_trust: "Nível de Confiança mínimo que um utilizador necessita de ter para poder publicar novos tópicos por email." + email_prefix: "A [etiqueta] usada no assunto dos emails. Se não estiver configurada, será 'Título' por defeito." + email_site_title: "Título do sítio usado como remetente de emails. Se não for configurado, será 'título' por defeito. Se o seu 'título' contém caracteres que não são permitidos na string do remetente de email, utilize esta configuração." + minimum_topics_similar: "Quantos tópicos precisam de existir antes de serem apresentados tópicos semelhantes ao compor novos tópicos." + relative_date_duration: "Número de dias após uma publicação durante o qual as datas das mensagens serão mostradas como relativas(7d) em vez de absolutas(20 Fev)." + delete_user_max_post_age: "Não permitir eliminação de utilizadores cuja primeira mensagem é mais antiga que (x) dias." + delete_all_posts_max: "Número máximo de mensagens que podem ser eliminadas de uma única vez com o botão Eliminar Todas as Mensagens. Se um utilizador tem mais mensagens do que este número, estas não podem ser eliminadas de uma única vez e o utilizador não pode ser eliminado." + username_change_period: "Número de dias após o registo em que as contas podem modificar o seu nome de utilizador (0 para não permitir a modificação do nome de utilizador)" + email_editable: "Permitir que os utilizadores alterem o seu endereço de email após o registo." + logout_redirect: "Localização de redirecionamento do navegador após encerramento da sessão EX: (http://algumsítio.com/terminarsessao)" + allow_uploaded_avatars: "Permitir que utilizadores carreguem avatars personalizados." + allow_animated_avatars: "Permitir que os utilizadores usem avatars com imagens animadas gifs. AVISO: executar a tarefa avatars:atualizar após mudança de configuração." + allow_animated_thumbnails: "Gera miniaturas de imagens ou gifs animados." + automatically_download_gravatars: "Descarregar Gravatars para os utilizadores após criação de conta ou mudança de email." + digest_topics: "Número máximo de tópicos a serem apresentados no resumo do email." + digest_min_excerpt_length: "Tamanho mínimo do excerto da mensagem no resumo do email, em caracteres." + default_digest_email_frequency: "Por defeito, quantas vezes os utilizadores recebem emails de resumo. Os utilizadores podem alterar esta configuração nas suas preferências." + default_external_links_in_new_tab: "Abrir hiperligações externas num novo separador. Os utilizadores podem alterar isto nas suas preferências." + detect_custom_avatars: "Se deve ou não haver uma verificação de que os utilizadores carregaram avatars personalizados." + max_daily_gravatar_crawls: "Número máximo de vezes que o Discourse irá verificar o Gravatar para avatars personalizados, por dia" + public_user_custom_fields: "Lista de campos personalizados para um utilizador e que podem ser exibidos publicamente." + allow_profile_backgrounds: "Permitir que os utilizadores carreguem fundos de perfil." + sequential_replies_threshold: "Número de mensagens que um utilizador tem que fazer em linha num tópico antes de ser relembrado acerca de demasiadas respostas sequenciais." + enable_mobile_theme: "Os dispositivos móveis usam um tema mobile-friendly, com a possibilidade de mudar para o sítio completo. Desative isto se quer usar um estilo personalizado que é totalmente responsivo." + dominating_topic_minimum_percent: "Que percentagem de mensagens um utilizador tem que fazer num tópico antes de ser relembrado sobre dominar demasiado um tópico." + suppress_uncategorized_badge: "Não mostrar distintivos para tópicos sem categoria nas listagens de tópicos." + global_notice: "Exibir um banner global URGENTE, EMERGÊNCIA a todos os visitantes, mudar para branco para esconder (HTML permitido)." + disable_edit_notifications: "Desativa notificações de edição do sistema quando 'download_remote_images_to_local' está ativo." + display_name_on_posts: "Mostrar o nome completo de um utilizador nas suas mensagens em adição ao seu @nome-de-utilizador." + invites_per_page: "Convites por defeito exibidos na página do utilizador." + short_progress_text_threshold: "Após o número de mensagens num tópico passar acima deste número, a barra de progresso irá apenas mostrar o número da mensagem atual. Se alterar a largura da barra de progresso, poderá ter que mudar este valor." + default_code_lang: "Linguagem de programação por defeito a aplicar no destaque da sintaxe em blocos de código GitHub (lang-auto, ruby, python, etc.)" + warn_reviving_old_topic_age: "Quando alguém começa a responder a um tópico em que a última resposta é mais antiga que estes dias, um aviso será exibido. Desativar ao configurar para 0." + autohighlight_all_code: "Forçar o destaque do código a todos os blocos de código pré-formatados mesmo quando não se especifica a linguagem." + embeddable_host: "Servidor que pode incorporar os comentários deste fórum Discourse." + feed_polling_enabled: "INCORPORAR APENAS: incorporar feeds RSS/ATOM como mensagens." + feed_polling_url: "INCORPORAR APENAS: URL dos feeds de RSS/ATOM para embutir." + embed_by_username: "Nome de utilizador Discourse do utilizador que cria tópicos embebidos." + embed_username_key_from_feed: "Chave para recuperar o nome de utilizador discourse do feed." + embed_truncate: "Truncar mensagens embutidas." + embed_category: "Categoria de tópicos embutidos." + embed_post_limit: "Número máximo de mensagens a serem incorporadas." + embed_whitelist_selector: "Seletor CSS para elementos permitidos em incorporações." + embed_blacklist_selector: "Seletor CSS para elementos que foram removidos de incorporações." + notify_about_flags_after: "Se houver sinalizações que não tenham sido tratadas após tantas horas, envie um email para contact_email. Configurar a 0 para desativar." + enable_cdn_js_debugging: "Permitir que /logs exiba erros próprios ao adicionar permissões de origem-cruzada em todos os js incluídos." + show_create_topics_notice: "Se o sítio tem menos de 5 tópicos públicos, mostrar um aviso pedindo aos administradores a criação de mais tópicos." + vacuum_db_days: "Executar VACUUM FULL ANALYZE para reclamar espaço na Base de Dados após a migração (configurar a 0 para desativar)" + prevent_anons_from_downloading_files: "Previna que utilizadores anónimos descarreguem anexos. AVISO: isto irá fazer com que quaisquer atributos (que não sejam imagens) publicados como anexos não funcionem." + enable_emoji: "Ativar emoji" + emoji_set: "Como gostaria de ter o seu emoji?" + errors: + invalid_email: "Endereço de email inválido." + invalid_username: "Não existe nenhum utilizador com esse nome de utilizador." + invalid_integer_min_max: "O valor deve estar entre %{min} e %{max}." + invalid_integer_min: "O valor deve ser %{min} ou superior." + invalid_integer_max: "O valor não pode ser superior a %{max}." + invalid_integer: "O valor deve ser um inteiro." + regex_mismatch: "O valor não coincide com o formato exigido." + invalid_string: "Valor inválido." + invalid_string_min_max: "Deve ser entre %{min} e %{max} caracteres." + invalid_string_min: "Deve ser pelo menos %{min} caracteres." + invalid_string_max: "Não deve ser mais que %{max} caracteres." notification_types: - mentioned: "%{display_username} mencionou-te em %{link}" - liked: "%{display_username} curtiu sua postagem em %{link}" - replied: "%{display_username} repondeu à sua postagem em %{link}" - quoted: "%{display_username} citou a sua postagem em %{link}" - edited: "%{display_username} editou a sua postagem em %{link}" - posted: "%{display_username} postou em %{link}" - moved_post: "%{display_username} moveu a sua postagem para %{link}" - private_message: "%{display_username} enviou-te uma mensagem particular: %{link}" + mentioned: "%{display_username} mencionou-o em %{link}" + liked: "%{display_username} gostou da sua mensagem %{link}" + replied: "%{display_username} repondeu à sua mensagem em %{link}" + quoted: "%{display_username} citou a sua mensagem em %{link}" + edited: "%{display_username} editou a sua mensagem em %{link}" + posted: "%{display_username} publicou em %{link}" + moved_post: "%{display_username} moveu a sua mensagem para %{link}" + private_message: "%{display_username} enviou-te uma mensagem privada: %{link}" invited_to_private_message: "%{display_username} convidou-te para uma conversa privada: %{link}" invitee_accepted: "%{display_username} aceitou o seu convite" + linked: "%{display_username} hiperligou-o em %{link}" + granted_badge: "Ganhou %{link}" search: + within_post: "#%{post_number} por %{username}" types: category: 'Categorias' + topic: 'Resultados' user: 'Utilizadores' - original_poster: "Postador original" - most_posts: "Maior parte das postagens" - most_recent_poster: "Maior parte das Postagens Recentes" - frequent_poster: "Postador frequente" + sso: + not_found: "Não foi possível encontrar ou criar uma conta, contacte a administração do sítio" + account_not_approved: "A conta está pendente de aprovação, irá receber uma notificação por email assim que for aprovada." + unknown_error: "Erro ao atualizar a informação, contacte a administração do sítio" + timeout_expired: "Tempo para início de sessão de conta expirado, por favor tente entrar novamente" + original_poster: "Participante Original" + most_posts: "Maior parte das mensagens" + most_recent_poster: "Participante Mais Recente" + frequent_poster: "Participante Frequente" + redirected_to_top_reasons: + new_user: "Bem-vindo à nossa comunidade! Estes são os tópicos populares mais recentes." + not_seen_in_a_month: "Bem-vindo novamente! Não o vemos há algum tempo. Estes são os tópicos mais populares desde que esteve ausente." move_posts: new_topic_moderator_post: - one: "Eu movi uma postagem para este novo tópico: %{topic_link}" - other: "Eu movi %{count} postagens para este novo tópico: %{topic_link}" + one: "Eu movi uma mensagem para um novo tópico: %{topic_link}" + other: "Eu movi %{count} mensagens para um novo tópico: %{topic_link}" existing_topic_moderator_post: - one: "Eu movi uma postagem para este tópico: %{topic_link}" - other: "Eu movi %{count} postagens para este tópico: %{topic_link}" + one: "Eu movi uma mensagem para um tópico existente: %{topic_link}" + other: "Eu movi %{count} mensagens para um tópico existente: %{topic_link}" + change_owner: + post_revision_text: "Direito de posse transferido de %{old_user} para %{new_user}" + emoji: + errors: + name_already_exists: "Pedimos desculpa, o nome '%{name}' já está a ser usado por outro emoji." + error_while_storing_emoji: "Pedimos desculpa, ocorreu um erro enquanto o seu emoji estava a ser guardado." topic_statuses: - archived_enabled: "Este tópico está agora arquivado. Está congelado e não pode ser alterado de qualquer forma." + archived_enabled: "Este tópico está agora arquivado. Está congelado e não pode ser alterado de qualquer maneira." archived_disabled: "Este tópico foi agora desarquivado. Já não está congelado, e pode ser alterado." closed_enabled: "Este tópico está agora fechado. Novas respostas não são aceites." closed_disabled: "Este tópico está agora aberto. Novas respostas serão aceites." - autoclosed_disabled: "Este tópico está aberto agora. Novas respostas estão permitidas." - pinned_disabled: "Este tópico deixou de estár afixado. Não irá mais aparecer no topo das suas categorias." + autoclosed_enabled_days: + one: "Este tópico foi automaticamente encerrado após 1 dia. Novas respostas não são permitidas." + other: "Este tópico foi automaticamente encerrado após %{count} dias. Novas respostas não são permitidas." + autoclosed_enabled_hours: + one: "Este tópico foi automaticamente encerrado após 1 hora. Novas respostas não são permitidas." + other: "Este tópico foi automaticamente encerrado após %{count} horas. Novas respostas não são permitidas." + autoclosed_enabled_minutes: + one: "Este tópico foi automaticamente fechado após 1 minuto. Novas respostas não são permitidas." + other: "Este tópico foi automaticamente fechado após %{count} minutos. Novas respostas não são permitidas." + autoclosed_enabled_lastpost_days: + one: "Este tópico foi automaticamente fechado 1 dia após a última resposta. Novas respostas não são permitidas." + other: "Este tópico foi automaticamente fechado %{count} dias após a última resposta. Novas respostas não são permitidas." + autoclosed_enabled_lastpost_hours: + one: "Este tópico foi automaticamente fechado 1 hora após a última resposta. Novas respostas não são permitidas." + other: "Este tópico foi automaticamente fechado %{count} horas após a última resposta. Novas respostas não são permitidas." + autoclosed_enabled_lastpost_minutes: + one: "Este tópico foi automaticamente fechado 1 minuto após a última resposta. Novas respostas não são permitidas." + other: "Este tópico foi automaticamente fechado %{count} minutos após a última resposta. Novas respostas não são permitidas." + autoclosed_disabled: "Este tópico está aberto agora. Novas respostas são permitidas." + autoclosed_disabled_lastpost: "Este tópico está agora aberto. Novas respostas são permitidas." + pinned_enabled: "Este tópico está agora fixado. Irá aparecer no topo da sua categoria até ser desafixado pelo pessoal para todas as pessoas, ou por utilizadores individuais para si próprios." + pinned_disabled: "Este tópico já não está afixado. Não irá aparecer novamente no topo da sua categoria." + pinned_globally_enabled: "Este tópico está globalmente fixado. Irá aparecer no topo da sua categoria e em todas as listas de tópicos até ser desafixado pelo pessoal para todas as pessoas, ou por utilizadores individuais para si próprios." + pinned_globally_disabled: "Este tópico está agora desafixado. Não irá aparecer no topo da sua categoria." + visible_enabled: "Este tópico está agora listado. Será exibido na lista de tópicos." + visible_disabled: "Este tópico deixou de estar listado. Não será exibido em qualquer lista de tópicos. A única maneira de aceder a este tópico é através de hiperligação direta." login: - not_approved: "A sua conta ainda não foi aprovada. Vais ser notificado por email assim que estiver pronto para fazeres log in." - incorrect_username_email_or_password: "Usuário, email ou senha incorretos" - wait_approval: "Obrigado por se registar. Você será notificado por email quando a sua conta for aprovada." - active: "A sua conta está ativa e pronta." - not_activated: "Ainda não podes fazer log in. Enviamos um email de ativação para você. Por favor siga as instruções no email para ativar a sua conta." - suspended: "Não pode iniciar sessão até %{date}" - suspended_with_reason: "Não pode iniciar sessão até %{date}, pois encontra-se ativa uma suspensão com o seguinte motivo: %{reason}" + not_approved: "A sua conta ainda não foi aprovada. Será notificado por email assim que estiver pronto para iniciar sessão." + incorrect_username_email_or_password: "Utilizador, email ou palavra-passe incorretos" + wait_approval: "Obrigado por se inscrever. Será notificado por email quando a sua conta for aprovada." + active: "A sua conta está ativa e pronta para ser utilizada." + activate_email: "

Está quase no final! Enviámos um email de ativação para %{email}. Por favor siga as instruções no email para ativar a sua conta.

Se não receber o email, verifique a sua pasta de spam, ou tente autenticar-se novamente para enviar outro email de ativação.

" + not_activated: "Ainda não pode iniciar sessão. Enviámos um email de ativação para si. Por favor siga as instruções no email para ativar a sua conta." + not_allowed_from_ip_address: "Não pode autenticar-se como %{username} a partir desse endereço IP." + suspended: "Não pode iniciar sessão até %{date}." + suspended_with_reason: "Não pode iniciar sessão até %{date}. Motivo da sua suspensão: %{reason}" errors: "%{errors}" not_available: "Não disponível. Tente %{suggestion}?" - something_already_taken: "Algo deu errado, talvez o nome de usuário ou o email já estejam registrados. Tente o link Esqueci minha Senha." - omniauth_error: "Desculpe, Houve um erro ao autorizar a sua conta %{strategy}. Talvez você não tenha aprovado a autorização?" - omniauth_error_unknown: "Algo deu errado no processamento do seu login, por favor tente novamente." + something_already_taken: "Algo de errado ocorreu, talvez o nome de utilizador ou o email já estejam registados. Tente a hiperligação Esqueci a minha palavra-passe." + omniauth_error_unknown: "Algo ocorreu ao processar o seu início de sessão, por favor tente novamente." + new_registrations_disabled: "Neste momento não é permitido novo registo de contas." + password_too_long: "As palavras-passe estão limitadas a 200 caracteres." + missing_user_field: "Não completou todos os campos de utilizador." + close_window: "A autenticação está completa. Feche esta janela para continuar." user: + no_accounts_associated: "Sem contas associadas" username: + short: "deve ter pelo menos %{min} caracteres" + long: "não deve ter mais que %{max} caracteres" + characters: "pode incluir apenas números, letras e sublinhados" unique: "tem que ser único" - blank: "tem que ser preenchido" - must_begin_with_alphanumeric: "tem de começar com uma letra ou um número" + blank: "tem que estar preenchido" + must_begin_with_alphanumeric: "tem que começar com uma letra ou com um número" email: not_allowed: "este provedor de emails não é permitido. Por favor utilize outro endereço de email." blocked: "não é permitido." ip_address: blocked: "está bloqueado." + invite_mailer: + subject_template: "%{invitee_name} convidou-o a juntar-se a '%{topic_title}' em %{site_domain_name}" + text_body_template: | + %{invitee_name} convidou-o para um debate + + > **%{topic_title}** + > + > %{topic_excerpt} + + em + + > %{site_title} -- %{site_description} + + Se está interessado, clique na hiperligação abaixo: + + %{invite_link} + + Este convite é de um utilizador confiável, por isso pode responder ao debate imediatamente. + invite_forum_mailer: + subject_template: "%{invitee_name} convidou-o a juntar-se a %{site_domain_name}" + text_body_template: | + %{invitee_name} convidou-o a juntar-se + + > **%{site_title}** + > + >%{site_description} + + Se está interessado, clique na hiperligação abaixo: + + %{invite_link} + + Este convite é de um utilizador confiável, por isso não precisa de se autenticar. + invite_password_instructions: + subject_template: "Configurar a palavra-passe para a sua conta %{site_name}" + text_body_template: | + Obrigado por ter aceite o seu convite para %{site_name} -- bem-vindo! + + Para entrar novamente, clique na seguinte hiperligação para escolher uma palavra-passe: + %{base_url}/users/password-reset/%{email_token} test_mailer: subject_template: "[%{site_name}] Teste de entrega de email" + new_version_mailer: + subject_template: "[%{site_name}] Nova versão Discourse, atualização disponível" + text_body_template: | + Uma nova versão de [Discourse](http://www.discourse.org) está disponível. + + A sua versão: %{installed_version} + Nova versão: **%{new_version}** + + Pode desejar: + + - Ver o que há de novo em [GitHub changelog](https://github.com/discourse/discourse/commits/master). + + - Atualizar visitando [%{base_url}/admin/upgrade](%{base_url}/admin/upgrade), e pressionando o botão "Atualizar". + + - Visitar [meta.discourse.org](http://meta.discourse.org) para notícias, debates, e suporte para o Discourse. + new_version_mailer_with_notes: + subject_template: "[%{site_name}] atualização disponível" + text_body_template: | + Uma nova versão de [Discourse](http://www.discourse.org) está disponível. + + A sua versão: %{installed_version} + Nova versão: **%{new_version}** + + Pode desejar: + + - Ver o que há de novo em [GitHub changelog](https://github.com/discourse/discourse/commits/master). + + - Atualizar visitando [%{base_url}/admin/upgrade](%{base_url}/admin/upgrade), e pressionando o botão "Atualizar". + + - Visitar [meta.discourse.org](http://meta.discourse.org) para notícias, debates, e suporte para o Discourse. + + ### Notas de lançamento + + %{notes} + flags_reminder: + flags_were_submitted: + one: "Estas sinalizações foram submetidas há 1 hora atrás." + other: "Estas sinalizações foram submetidas há %{count} horas atrás." + please_review: "Por favor examine-os." + post_number: "mensagem" + how_to_disable: 'Pode desativar ou mudar a frequência deste email notificativo através de "notify about flags after" nas configurações.' + subject_template: + one: "1 sinalização à espera de ser tratada" + other: "%{count} sinalizações à espera de serem tratadas" + flags_dispositions: + agreed: "Obrigado por nos informar. Concordamos que existe um problema e estamos a analisá-lo." + agreed_and_deleted: "Obrigado por nos informar. Concordamos que existe um problema e removemos a mensagem." + disagreed: "Obrigado por nos informar. Estamos a analisá-lo." + deferred: "Obrigado por nos informar. Estamos a analisá-lo." + deferred_and_deleted: "Obrigado por nos informar. Removemos a mensagem." + temporarily_closed_due_to_flags: "O tópico está temporariamente fechado devido a um grande número de sinalizações da comunidade." system_messages: post_hidden: - subject_template: "%{site_name} Aviso: Postagem escondida devido a Sinalização pela Comunidade" + subject_template: "Mensagem oculta devido a sinalizações da comunidade" + text_body_template: | + Olá, + + Esta é uma mensagem automática de %{site_name} para informá-lo que a sua mensagem foi oculta. + + %{base_url}%{url} + + %{flag_reason} + + Múltiplos membros da comunidade sinalizaram esta mensagem antes de ser escondida, por isso considere como poderá rever a sua mensagem para refletir o seu feedback. **Pode editar a sua mensagem após %{edit_delay} minutos, e esta será automaticamente mostrada.** Isto irá aumentar o seu nível de confiança. + + Contudo, se a mensagem for escondida pela comunidade uma segunda vez, irá manter-se escondida até ser tratada pelo pessoal – e poderão ainda ocorrer ações, incluindo uma possível suspensão da sua conta. + + Para orientação adicional, por favor consulte as [diretrizes da comunidade](%{base_url}/guidelines). welcome_user: - subject_template: "Bem-vindo em %{site_name}!" + subject_template: "Bem-vindo a %{site_name}!" welcome_invite: - subject_template: "Bem-vindo em %{site_name}!" + subject_template: "Bem-vindo a %{site_name}!" + backup_succeeded: + subject_template: "Cópia de segurança completa corretamente" + text_body_template: "A cópia de segurança foi feita corretamente.\nVisite a [admin > secção de cópias de segurança](/admin/backups) para descarregar a sua nova cópia de segurança." + backup_failed: + subject_template: "A cópia de segurança falhou" + text_body_template: | + A cópia de segurança falhou. + + Aqui está o registo do log: + + ``` + %{logs} + ``` + restore_succeeded: + subject_template: "Restauração completa corretamente" + text_body_template: "A restauração foi bem sucedida." + restore_failed: + subject_template: "A restauração falhou" + text_body_template: | + A restauração falhou. + + Aqui está o registo: + + ``` + %{logs} + ``` + bulk_invite_succeeded: + subject_template: "Convites de utilizadores em massa processados com sucesso" + text_body_template: "O seu ficheiro de convites de utilizadores em massa foi processado, %{sent} convites enviados." + bulk_invite_failed: + subject_template: "Convite de utilizadores em massa processado com erros" + text_body_template: | + O seu ficheiro de convite de utilizadores em massa foi processado, %{sent} convites enviados com %{failed} erro(s). + + Aqui está o registo do log: + + ``` + %{logs} + ``` + csv_export_succeeded: + subject_template: "Exportação de dados completa" + csv_export_failed: + subject_template: "A exportação dos dados falhou" + text_body_template: "Pedimos desculpa mas a sua exportação de dados falhou. Por favor verifique os registos do log ou contacte um membro do pessoal." + email_reject_trust_level: + subject_template: "Problema de email -- Nível de Confiança Insuficiente" + text_body_template: | + Pedimos desculpa mas a sua mensagem de email para %{destination} (titled %{former_title}) não funcionou. + + A sua conta não tem o nível de confiança necessário para publicar novos tópicos para este endereço de email. Se acredita que isto é um erro, contacte um membro do pessoal. + email_reject_no_account: + subject_template: "Problema de email -- Conta Desconhecida" + text_body_template: | + Pedimos desculpa mas a sua mensagem de email %{destination} (titled %{former_title}) não funcionou. + + Não há nenhuma conta de utilizador conhecida com este endereço de email. Tente enviar a partir de um endereço de email diferente, ou contacte um membro do pessoal. + email_reject_empty: + subject_template: "Problema de email -- Sem Conteúdo" + text_body_template: | + Pedimos desculpa mas a sua mensagem de email para %{destination} (titled %{former_title}) não funcionou. + + Não conseguimos encontrar nenhum conteúdo no email. Certifique-se que a sua resposta está no topo do email -- não podemos processar respostas em linha. + + Se está a ter este erro e se _realmente_ colocou conteúdo, tente novamente com conteúdo HTML incluído no seu email (e não apenas texto simples). + email_reject_parsing: + subject_template: "Problema de email -- Conteúdo não reconhecido" + text_body_template: | + Pedimos desculpa mas a sua mensagem de email para %{destination} (titled %{former_title}) não funcionou. + + Não conseguimos encontrar nenhuma resposta no email fornecido. **Certifique-se que a sua resposta está no topo do email** -- não conseguimos processar respostas em linha. + email_reject_post_error: + subject_template: "Problema de email -- Erro de publicação" + text_body_template: | + Pedimos desculpa mas a sua mensagem de email para %{destination} (titled %{former_title}) não funcionou. + + Algumas causas possíveis são: formatação complexa, mensagem demasiado grande, mensagem demasiado pequena. Por favor tente novamente, ou publique através do sítio se isto continuar. + email_reject_post_error_specified: + subject_template: "Problema de email -- Erro de publicação" + text_body_template: | + Pedimos desculpa mas a sua mensagem de email para %{destination} (titled %{former_title}) não funcionou. + + Motivo: + + %{post_error} + + Se conseguir corrigir o problema, tente novamente. + email_reject_reply_key: + subject_template: "Problema de email -- Chave de Resposta Desconhecida" + text_body_template: | + Pedimos desculpa mas a sua mensagem de email para %{destination} (titled %{former_title}) não funcionou. + + A chave de resposta fornecida é inválida ou desconhecida, por isso não sabemos a que resposta corresponde este email. Contacte um membro do pessoal. + email_reject_destination: + subject_template: "Problema de email -- Endereço Para: Desconhecido" + text_body_template: | + Pedimos desculpa mas a sua mensagem de email para %{destination} (titled %{former_title}) não funcionou. + + Nenhum dos endereços de destino é reconhecido. Por favor, certifique-se que o endereço do sítio está na linha Para: (não em Cc: ou em Bcc:), e que está a enviar para o endereço de email correto fornecido pelo pessoal. + email_reject_topic_not_found: + subject_template: "Problema de email -- Tópico Não Encontrado" + text_body_template: | + Pedimos desculpa mas a sua mensagem de email para %{destination} (titled %{former_title}) não funcionou. + + O tópico não foi encontrado, poderá ter sido eliminado. Se acredita que isto é um erro, contacte um membro do pessoal. + email_reject_topic_closed: + subject_template: "Problema de email -- Tópico Encerrado" + text_body_template: | + Pedimos desculpa mas a sua mensagem de email para %{destination} (titled %{former_title}) não funcionou. + + O tópico está fechado. Se acredita que isto é um erro, contacte um membro do pessoal. + email_error_notification: + subject_template: "Problema de email -- Erro de autenticação POP" + text_body_template: | + Ocorreu um erro de autenticação ao recuperar emails do servidor POP. + + Por favor certifique-se que configurou corretamente as credenciais POP em [configurações do sítio](%{base_url}/admin/site_settings/category/email). too_many_spam_flags: subject_template: "Nova conta bloqueada" + text_body_template: | + Olá, + + Esta é uma mensagem automática de %{site_name} para informá-lo que as suas mensagens foram automaticamente eliminadas por terem sido sinalizadas por parte da comunidade. + + Como medida de precaução, a sua nova conta foi bloqueada de criar novas respostas ou tópicos até que um membro do pessoal possa revê-la. + + Para orientação adicional, por favor consulte as [diretrizes da comunidade](%{base_url}/guidelines). blocked_by_staff: subject_template: "Conta bloqueada" + text_body_template: | + Olá, + + Esta é uma mensagem automática de %{site_name} para informá-lo de que a sua conta foi bloqueada por um membro do pessoal. + + Para orientação adicional, por favor consulte as [diretrizes da comunidade](%{base_url}/guidelines). + user_automatically_blocked: + subject_template: "Novo utilizador %{username} bloqueado devido a sinalizações da comunidade." + text_body_template: | + Esta é uma mensagem automática. + + O novo utilizador [%{username}](%{base_url}%{user_url}) foi automaticamente bloqueado devido a múltiplos utilizadores terem sinalizado a(s) mensagen(s) de %{username}. + + Por favor [reveja as sinalizações](%{base_url}/admin/flags). Se %{username} foi incorretamente bloqueado de publicar, carregue no botão de desbloqueio em [a página de administração para este utilizador](%{base_url}%{user_url}). + + Este limite pode ser alterado através de `block_new_user` nas configurações do sítio. + spam_post_blocked: + subject_template: "Novas mensagens do novo utilizador %{username} foram bloqueadas devido a hiperligações repetidas" + text_body_template: | + Esta é uma mensagem automática. + + O novo utilizador [%{username}](%{base_url}%{user_url}) tentou criar múltiplas mensagens com hiperligações para %{domains}, mas estas foram bloqueadas para evitar spam. O utilizador ainda pode criar novas mensagens sem hiperligação a %{domains}. + + Por favor [reveja o utilizador](%{base_url}%{user_url}). + + Isto pode ser modificado através de `newuser_spam_host_threshold` e `white_listed_spam_host_domains`nas configurações do sítio. unblocked: subject_template: "Conta desbloqueada" text_body_template: | Olá, - Esta é uma mensagem automática de %{site_name} para informar que sua conta foi desbloqueada após avaliação da staff. + Esta é uma mensagem automática de %{site_name} para informar que a sua conta foi desbloqueada após avaliação da staff. - Agora você pode responder e criar novos tópicos de novo. + Pode agora voltar a responder e criar novos tópicos. pending_users_reminder: subject_template: - one: "1 usuário aguardando aprovação" - other: "%{count} usuários aguardando aprovação" - unsubscribe_link: "Se você deseja se desinscrever destes emails, visite suas [preferências do usuário](%{user_preferences_url})." + one: "1 utilizador espera a aprovação" + other: "%{count} utilizadores esperam a aprovação" + text_body_template: | + Há registos de novos utilizadores à espera de serem aprovados (ou rejeitados) antes de poderem aceder a este fórum. + + [Por favor reveja-os na secção de administração](%{base_url}/admin/users/list/pending). + download_remote_images_disabled: + subject_template: "Descarregamento de imagens remotas desativado" + text_body_template: "A configuração `download_remote_images_to_local` foi desativada porque o limite de espaço em disco em `download_remote_images_threshold` foi alcançado." + unsubscribe_link: "Para cancelar a subscrição destes emails, visite as suas [preferências de utilizador](%{user_preferences_url})." + subject_re: "Re:" + subject_pm: "[MP]" user_notifications: previous_discussion: "Respostas Anteriores" unsubscribe: - title: "Desinscrever" - description: "Não está interessado em receber estes emails? Não tem problema! Clique em baixo para se desinscrever instantaneamente:" - reply_by_email: "Para responder, responda este email ou visite %{base_url}%{url} no seu navegador." + title: "Cancelar a Subscrição" + description: "Não está interessado em receber estes emails? Não há problema! Clique em baixo para cancelar a subscrição instantaneamente:" + reply_by_email: "Para responder, responda a este email ou visite %{base_url}%{url} no seu navegador." visit_link_to_respond: "Para responder visite %{base_url}%{url} no seu navegador." - posted_by: "Postado por %{username} em %{post_date}" - user_replied: + posted_by: "Publicado por %{username} em %{post_date}" + user_invited_to_private_message_pm: + subject_template: "[%{site_name}] %{username} convidou-o para uma mensagem privada '%{topic_title}'" text_body_template: | - %{username} respondeu à sua postagem no tópico '%{topic_title}' de %{site_name}: + %{username} convidou-o para uma mensagem privada '%{topic_title}' em %{site_name}: - --- + Por favor visite a hiperligação para visualizar este tópico: %{base_url}%{url} + user_replied: + subject_template: "[%{site_name}] %{topic_title}" + text_body_template: | %{message} + %{context} + --- %{respond_instructions} user_quoted: + subject_template: "[%{site_name}] %{topic_title}" text_body_template: | - %{username} citou você no tópico '%{topic_title}' de %{site_name}: - - --- %{message} + %{context} + --- %{respond_instructions} user_mentioned: + subject_template: "[%{site_name}] %{topic_title}" text_body_template: | - %{username} mencionou você no tópico '%{topic_title}' de %{site_name}: - - --- %{message} + %{context} + --- %{respond_instructions} user_posted: + subject_template: "[%{site_name}] %{topic_title}" text_body_template: | - %{username} postou no tópico '%{topic_title}' de %{site_name}: + %{message} + + %{context} --- + %{respond_instructions} + user_posted_pm: + subject_template: "[%{site_name}] [MP] %{topic_title}" + text_body_template: | %{message} + %{context} + --- %{respond_instructions} digest: - new_activity: "Nova atividade nos seus tópicos e postagens:" + why: "Um breve sumário de %{site_link} desde a sua última visita em %{last_seen_at}" + subject_template: "[%{site_name}] Resumo para %{date}" + new_activity: "Nova atividade nos seus tópicos e mensagens:" + top_topics: "Mensagens populares" + other_new_topics: "Tópicos populares" + unsubscribe: "O resumo é enviado de %{site_link} quando não o vemos há algum tempo. Para anular a subscrição %{unsubscribe_link}." click_here: "clique aqui" from: "resumo de %{site_name}" read_more: "Leia Mais" + more_topics: "Houve %{new_topics_since_seen} outros novos tópicos." + more_topics_category: "Mais novos tópicos:" + posts: + one: "1 mensagem" + other: "%{count} mensagens" forgot_password: - subject_template: "[%{site_name}] Redefinição de senha" + subject_template: "[%{site_name}] Repor palavra-passe" text_body_template: | - Alguém pediu para redefinir a sua senha em [%{site_name}](%{base_url}). + Alguém pediu para redefinir a sua palavra-passe em [%{site_name}](%{base_url}). - Se não foi você, por favor ignore este email. + Se este pedido não veio da sua parte, por favor ignore este email. - Para escolher a sua nova senha clique no link a seguir: + Para escolher a sua nova palavra-passe clique na seguinte hiperligação: + %{base_url}/users/password-reset/%{email_token} + set_password: + subject_template: "[%{site_name}] Configurar Palavra-passe" + text_body_template: | + Alguém pediu para adicionar uma palavra-passe à sua conta em [%{site_name}](%{base_url}). Como alternativa, pode entrar usando qualquer serviço online suportado (Google, Facebook, etc) que estiver associado a este endereço de email validado. + + Se não fez este pedido, pode ignorar este email. + + Clique na hiperligação seguinte para escolher uma palavra-passe. + %{base_url}/users/password-reset/%{email_token} + account_created: + subject_template: "[%{site_name}] A Sua Nova Conta" + text_body_template: | + Uma nova conta foi criada para si em %{site_name} + + Clique na hiperligação seguinte para escolher uma palavra-passe para a sua nova conta. %{base_url}/users/password-reset/%{email_token} authorize_email: - subject_template: "[%{site_name}] Confirma o seu novo endereço de email" + subject_template: "[%{site_name}] Confirme o seu novo endereço de email" text_body_template: | - Confirma o seu endereço de email novo para %{site_name} clicando no seguinte link: + Confirme o seu novo endereço de email para %{site_name} ao clicar na seguinte hiperligação: %{base_url}/users/authorize-email/%{email_token} signup_after_approval: - subject_template: "Você foi aprovado no %{site_name}!" + subject_template: "Foi aprovado no %{site_name}!" signup: subject_template: "[%{site_name}] Ativar a sua nova conta" text_body_template: | - Ben-vindo ao %{site_name}! + BeM-vindo ao %{site_name}! - Clique no seguinte link para confirmar e aticar a sua nova conta: + Clique na seguinte hiperligação para confirmar e ativar a sua nova conta: %{base_url}/users/activate-account/%{email_token} - Se não conseguires clicar no link em cima, tenta copia-lo e cola-lo na barra de endereções do seu browser de internet. - deleted: 'removido' + Se a hiperligação não for clicável, tente copiá-la e colá-la na barra de endereço do seu navegador de internet. + page_not_found: + title: "A página que solicitou não existe ou é privada." + popular_topics: "Popular" + recent_topics: "Recente" + see_more: "Mais" + search_title: "Pesquisar neste sítio" + search_google: "Google" + login_required: + welcome_message: | + #[Bem-vindo a %{title}](#bem-vindo) + É necessário ter uma conta. Por favor crie uma conta ou inicie a sessão para continuar. + terms_of_service: + title: "Termos de Serviço" + signup_form_message: 'Li e aceito os Termos de Serviço.' + deleted: 'eliminado' upload: - unauthorized: "Desculpe-nos, o arquivo que você está tentando enviar não é autorizado (extensões autorizadas: %{authorized_extensions})." + edit_reason: "cópias locais de imagens descarregadas" + unauthorized: "Pedimos desculpa, o ficheiro que está a tentar carregar não está autorizado (extensões autorizadas: %{authorized_extensions})." pasted_image_filename: "Imagem colada" - attachments: - too_large: "Desculpe, o arquivo que você está tentando enviar é muito grande (tamanho máximo é %{max_size_kb}%kb)." + store_failure: "Falha ao armazenar o carregamento #%{upload_id} para o utilizador #%{user_id}." images: - too_large: "Desculpe, a imagem que você está tentando enviar é muito grande (tamanho máximo é %{max_size_kb}%kb), por favor redimensione-a e tente novamente." - fetch_failure: "Desculpe, houve um erro ao transferir a imagem." - unknown_image_type: "Desculpe, mas o arquivo que você tentou enviar não parece ser uma imagem." - size_not_found: "Desculpe, mas não conseguimos determinar o tamanho da imagem. É possível que seu arquivo de imagem esteja corrompido?" + fetch_failure: "Pedimos desculpa, ocorreu um erro ao transferir a imagem." + unknown_image_type: "Pedimos desculpa, mas o ficheiro que tentou carregar não parece ser uma imagem." + size_not_found: "Pedimos desculpa, mas não conseguimos determinar o tamanho da imagem. É possível que seu ficheiro de imagem esteja corrompido?" flag_reason: - sockpuppet: "Um novo utilizador criou um tópico, que um outro novo utilizador com o mesmo IP respondeu. Veja as configurações do sítio em flag_sockpuppets" + sockpuppet: "Um novo utilizador criou um tópico, no qual um outro novo utilizador com o mesmo IP respondeu. Veja as configurações de flag_sockpuppets" + spam_hosts: "Este novo utilizador tentou criar múltiplas mensagens com hiperligações ao mesmo domínio. Verifique as configurações do sítio em newuser_spam_host_threshold." + email_log: + no_user: "Não foi possível encontrar o utilizador com o id %{user_id}" + suspended_not_pm: "O utilizador está suspenso, sem mensagens privadas" + seen_recently: "O utilizador foi visto recentemente" + post_not_found: "Não foi possível encontrar a mensagem com o id %{post_id}" + notification_already_read: "A notificação sobre o qual este email se trata já foi lida" + topic_nil: "post.topic está vazio" + post_deleted: "a mensagem foi eliminada pelo autor" + user_suspended: "o utilizador foi suspenso" + already_read: "o utilizador já leu esta mensagem" + message_blank: "a mensagem está em branco" + message_to_blank: "message.to está em branco" + text_part_body_blank: "text_part.body está em branco" + body_blank: "corpo está em branco" + color_schemes: + base_theme_name: "Base" + guidelines: "Diretrizes" + privacy: "Privacidade" + edit_this_page: "Editar esta página" + csv_export: + boolean_yes: "Sim" + boolean_no: "Não" + static_topic_first_reply: | + Edite a primeira mensagem neste tópico para alterar os conteúdos da página %{page_name}. + guidelines_topic: + title: "FAQ/Diretrizes" + tos_topic: + title: "Termos de Serviço" + body: "Os seguintes termos e condições governam todo o uso do sítio %{company_domain} e todo o conteúdo, serviços e produtos disponíveis no ou através do sítio, incluindo mas não limitado a, %{company_domain} Software do Fórum, %{company_domain} Suporte do Fórum e serviço de alojamento %{company_domain} (\"Alojamento\"), (em conjunto, o Sítio). O Sítio é propriedade e operado por %{company_full_name} (\"%{company_name}\"). O Sítio oferecido está sujeito à sua aprovação sem nenhuma modificação de todos os termos e condições contidos aqui e todas as outras regras de operação políticas (incluindo, sem limitação, %{company_domain}’s [Política de Privacidade](/privacy) e [Diretrizes da Comunidade](/faq)) e procedimentos que possam ser publicados de tempos em tempos neste Sítio por %{company_name} (coletivamente, the \"Acordo\").\n\nPor favor leia este Acordo com cuidado antes de aceder ou usar o Sítio. Ao aceder ou usar qualquer parte do Sítio, concorda em estar limitado pelos termos e condições deste acordo. Se não concorda com todos os termos e condições deste acordo, então não deve aceder ao Sítio ou usar qualquer um dos serviços. Se estes termos e condições são considerados uma oferta de %{company_name}, a sua aceitação é expressamente limitada a estes termos. O Sítio está disponível apenas a indíviduos que têm pelo menos 13 anos de idade.\n\n\n\n## [1. A Sua Conta %{company_domain} ](#1)\n\nSe criar uma conta\ + \ no Sítio, será responsável por manter a segurança da sua conta e será totalmente responsável por todas as atividades que ocorrerem sob a sua conta. Terá que notificar imediatamente %{company_name} de qualquer uso não autorizado da sua conta ou qualquer outra falha de segurança. %{company_name} não será passível de quaisquer atos ou omissões da sua parte, incluindo danos de qualquer tipo que incorram como resultado de tais atos ou omissões.\n\n\n\n## [2.Responsabilidade dos Participantes](#2)\n\nSe publicar material no Sítio, publicar hiperligações no Sítio, ou de outra maneira disponibilizar (ou permitir que qualquer terceira parte o faça) material no Sítio (qualquer material, “Conteúdo”), será inteiramente responsável pelo conteúdo de, e qualquer dano resultante de tal Conteúdo. Esse é o caso independentemente de o Conteúdo em questão constituir texto, gráficos, ficheiros áudio, ou programas de computadores. Ao tornar o Conteúdo disponível, está a representar e garantir que:\n\n* o descarregamento, cópia e uso do Conteúdo não irá infringir os direitos do proprietário, incluindo mas não limitado aos direitos de autor, patentes, marcas registadas ou direitos de segredos de troca, de qualquer terceira parte;\n* se o seu empregado tem direitos sob a propriedade intelectual que criou, terá que (i) ou ter recebido permissões do seu empregado para publicar ou tornar o Conteúdo disponível, incluindo mas não limitado\ + \ a qualquer software, ou (ii) ter garantido uma renúncia do seu empregado a todos os direitos no e para o Conteúdo;\n* está de acordo com licenças de qualquer terceira-parte relacionada com o Conteúdo, e fez todas as coisas necessárias para passar corretamente quaisquer termos requeridos aos utilizadores finais;\n* o Conteúdo não contém ou instala quaisquer vírus, minhocas ou malware, cavalos de Tróia ou outro conteúdo destrutivo;\n* o Conteúdo não é spam, não é gerado por máquina, e não contém conteúdo indesejado sem ética ou comercial com intuito de conduzir o tráfico de sítios terceiros ou aumentar a posição de sítios terceiros em motores de pesquisa, ou ainda outros atos ilegais (tais como phishing) ou destinatários enganadores relativamente à origem do material (tais como spoofing);\n* o Conteúdo não é pornográfico, nem contém ameaças ou incita violência, e não viola os direitos de privacidade ou publicidade de qualquer terceira parte;\n* o conteúdo não está a ser anunciado através de quaisquer mensagens eletrónicas tais como hiperligações de spam ou grupos de notícias, listas de email, blogs e sítios, e similares métodos promocionais não solicitados;\n* o seu conteúdo não está nomeado de nenhuma maneira que leve os seus leitores a pensarem que é outra pessoa ou empresa; e \n* terá, no caso do Conteúdo incluir código de computador, categorizado e/ou descrito o tipo, natureza, uso e efeito dos materiais, quer tenha sido\ + \ pedido para fazê-lo por %{company_name} ou pelo contrário.\n\n\n\n## [3. Licença do Conteúdo do Utilizador](#3)\n\nAs contribuições dos utilizadores estão licenciadas sob a [Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License](http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_US). Sem qualquer limitação de qualquer dessas representações ou garantias, %{company_name} tem o direito (embora não tenha a obrigação) de, em critério exclusivo de %{company_name} (i) recusar ou remover qualquer conteúdo que, na sensata opinião de %{company_name} viola qualquer política de %{company_name} ou é de alguma maneira prejudicial ou censurável, ou (ii) terminar ou negar acesso ao Sítio e ao uso do mesmo a qualquer indivíduo ou entidade por alguma razão, em critério exclusivo de %{company_name}. %{company_name} não terá obrigação de fornecer uma compensação de quaisquer montantes previamente pagos.\n\n\n\n## [4. Pagamento e Renovação](#4)\n\n### Termos Gerais\n\nAtualizações ou serviços opcionais pagos podem estar disponíveis no Sítio. Ao utilizar uma atualização ou serviço opcional pago, está a concordar em pagar a %{company_name} as taxas de subscrição mensais ou anuais indicadas. Os pagamentos serão cobrados numa base pré-paga no dia em que utilizar o serviço ou a atualização e irá cobrir o uso desse serviço ou atualização por um período de subscrição mensal ou anual tal como\ + \ indicado. Estas taxas não são reembolsáveis.\n\n### Renovação Automática\n\nA não ser que notifique %{company_name} antes do final do período da subscrição que quer cancelar, a sua subscrição será automaticamente renovada e você autoriza-nos a colecionar as taxas anuais ou mensais (assim como outras taxas) usando qualquer cartão de crédito ou outro mecanismo de pagamento que tivermos no seu registo. As subscrições podem ser canceladas em qualquer altura.\n\n\n\n## [5. Serviços](#5)\n\n### Serviços de Alojamento, Suporte\n\nServiços opcionais de Alojamento e Suporte podem ser fornecidos por %{company_name} sob os termos e condições para cada serviço. Ao inscrever-se numa conta com serviços de Alojamento ou Suporte, irá concordar e agir sob tais termos e condições.\n\n\n\n## [6. Responsabilidade dos Visitantes do Sítio](#6)\n\n%{company_name} não reviu, nem pode rever, todo o material, incluindo programas de computador, publicado no Sítio, e não pode portanto ser responsável pelo conteúdo do mesmo, pelo seu uso ou efeitos. Ao operar o Sítio, %{company_name} não representa ou implica que corrobora com o material publicado, ou que acredita que o material seja preciso, útil e não prejudicial. Será responsável por tomar as precauções necessárias para proteger-se a si próprio e o seu sistema computacional de vírus, minhocas, cavalos de Tróia e outros conteúdos destrutivos. O Sítio pode ter conteúdo\ + \ ofensivo, indecente assim como também imprecisões técnicas, erros tipográficos entre outros erros. O Sítio pode também ter material que viola os direitos de privacidade e publicidade, ou que infringe a propriedade intelectual e outros direitos do proprietário, de terceiras partes, ou o descarregamento, cópia ou uso que é sujeito a termos e condições adicionais, estabelecidos ou não.\n\n%{company_name} está isenta de qualquer responsabilidade de qualquer dano resultante do uso por parte dos visitantes do Sítio, ou de qualquer descarregamento feito por esses visitantes do conteúdo lá publicado.\n\n\n\n## [7. Conteúdo Publicado Noutros Sítios](#7)\n\nNão revimos nem podemos rever todo o material, incluindo programas de computador, disponibilizado através de sítios e páginas em %{company_domain} no qual existem hiperligações e que se hiperligam a %{company_domain}. %{company_name} não tem qualquer controlo dos sítios e páginas não-%{company_domain}, e não é responsável pelo seu conteúdo ou o seu uso. Ao clicar num sítio ou página não--%{company_domain}, %{company_name} não representa ou implica que corrobora com tais sítios ou páginas. Você é responsável por tomar ações necessárias para proteger-se a si próprio ou o seu sistema computacional de vírus, minhocas, cavalos de Tróia e outros conteúdos destrutivos. %{company_name} renuncia qualquer responsabilidade por qualquer dano resultante do uso de sítios e páginas\ + \ não-%{company_domain}.\n\n\n\n## [8. Violação de Direitos de Autor e Política DCMA](#8)\n\nAssim como %{company_name} pede aos outros para respeitarem os direitos de propriedade intelectual, a mesma respeita os direitos de propriedade intelectual dos outros. Se acredita que material localizado em ou com hiperligação para, por %{company_domain} viola os seus direitos de autor, e se este sítio reside nos EUA, encorajamo-lo a notificar a %{company_name} em concordância com a Política (“DMCA”) [Digital Millennium Copyright Act](http://en.wikipedia.org/wiki/Digital_Millennium_Copyright_Act) da %{company_name}.\n%{company_name} irá responder por tais notícias, incluindo tal como requerido ou apropriado por remover o material ilícito ou desativando as hiperligações para o material ilícito.\n%{company_name} irá terminar o acesso a e o uso do Sítio se, sob circunstâncias apropriadas, o visitante é determinado como sendo um infrator repetido dos direitos de autor e de outros direitos de propriedade intelectual de %{company_name} ou outros. No caso de tal terminação, %{company_name} não terá qualquer obrigação de fornecer uma compensação de qualquer valor previamente pago a %{company_name}.\n\n\n\n## [9. Propriedade Intelectual](#9)\n\nEste Acordo não transfere totalmente de %{company_name} para si qualquer propriedade intelectual de %{company_name} ou de terceiras partes, e todos os direitos, títulos\ + \ e interesse na e para a propriedade irá permanecer (entre as partes) apenas com %{company_name}. %{company_name}, %{company_domain}, o logótipo %{company_domain} e todos as outras marcas, marcas de serviço, gráficos, logótipos usados na ligação com %{company_domain}, ou o Sítio é marca registada de %{company_name} ou licenciadores de %{company_name}. Outras marcas, marcas de serviço, gráficos e logótipos usados na hiperligação com o Sítio podem ser marcas de terceiras partes. O seu uso do Sítio não lhe concede direitos ou licença para reproduzir ou de outra forma usar quaisquer marcas de %{company_name} ou de terceiras partes.\n\n\n\n## [10. Anúncios](#10)\n\n%{company_name} reserva-se ao direito de mostrar anúncios no seu conteúdo a não ser que tenha comprado a Atualização Ad-free ou uma conta de Serviços.\n\n\n\n## [11. Atribuição](#11)\n\n%{company_name} reserva-se ao direito de mostrar hiperligações de atribuição tais como ‘Patrocinado por %{company_domain},’ e atribuição de fontes ao conteúdo do rodapé ou da sua ferramenta. Os créditos do rodapé e a ferramenta de %{company_domain} não poderão ser removidas independentemente das atualizações compradas.\n\n\n\n## [12. Alterações](#12)\n\n%{company_name} reserva-se ao direito, por critério exclusivo, de modificar ou substituir qualquer parte deste Acordo. É sua responsabilidade verificar periodicamente o Acordo e\ + \ verificar as suas alterações. O seu uso ou acesso contínuo ao Sítio seguido da publicação de qualquer alteração a este Acordo constitui aceitação a essas mudanças. %{company_name} pode também, no futuro, oferecer novos serviços e/ou funcionalidades através do Sítio (incluindo, o lançamento de novas ferramentas e recursos). Estas novas funcionalidades e/ou serviços devem ser sujeitos aos termos e condições deste Acordo.\n\n\n\n## [13. Terminação](#13)\n\n%{company_name} pode terminar o seu acesso a todas ou alguma parte do Sítio em qualquer altura, com ou sem motivo, com ou sem aviso, com efeitos imediatos. Se desejar terminar este Acordo ou a sua conta %{company_domain} (se tiver uma), pode simplesmente deixar de usar o Sítio. Todas as provisões para este Acordo que pela sua natureza deverão sobreviver à terminação, incluindo, sem limitações, provisões de propriedade, isenções de garantia, indemnizações e limitações de responsabilidade.\n\n\n\n## [14. Isenção de Garantias](#14)\n\nO Sítio é fornecido “tal como está”. %{company_name} e os seus fornecedores e licenciadores ficam aqui isentos de garantias de qualquer tipo, expressas ou implícitas, sem limitação, as garantias de comerciabilidade, adequadas a uma finalidade específica. Nem %{company_name} nem os seus fornecedores e licenciadores, fazem garantias de que o Sítio está livre de erros ou que o acesso seja contínuo e ininterrupto.\ + \ Se está a ler isto, aqui está [uma ajuda] (http://www.newyorker.com/online/blogs/shouts/2012/12/the-hundred-best-lists-of-all-time.html). Compreende que descarregou de, ou de outra maneira obteve conteúdo ou serviços através do Sítio a seu próprio risco e descrição.\n\n\n\n## [15. Limitação da Responsabilidade](#15)\n\nEm nenhum evento %{company_name} ou os seus fornecedores ou licenciadores, serão responsáveis em respeito a qualquer assunto deste acordo sob qualquer contrato, negligência, estrita responsabilidade ou outras teorias legais para: (i) quaisquer danos especiais, de incidentes ou consequenciais; (ii) o custo de procura de produtos ou serviços substitutos; (iii) por interrupção do uso, perda ou corrupção dos dados; ou (iv) por quaisquer valores que excedem as taxas pagas por si a %{company_name} sob este acordo durante um período doze (12) meses anterior à causa da ação. %{company_name} não deverá ter qualquer responsabilidade de qualquer falha ou atraso devido a matérias para além do seu razoável controlo. O disposto neste artigo não se aplica nas medidas proibidas pela lei aplicável.\n\n\n\n## [16. Representações Gerais e Garantia](#16)\n\nRepresenta e garante que (i) o seu uso do Sítio irá estar em estrita concordância com a [Política de Privacidade](/privacy) e das [Diretrizes da Comunidade](/guidelines) da %{company_name}, com este Acordo e com as leis e regulações aplicáveis\ + \ (incluindo sem limitação de quaisquer leis ou regulações locais no seu país, estado, cidade ou outras áreas governamentais, relativamente à conduta online e ao conteúdo aceitável, e incluindo quaisquer leis aplicáveis relativamente à transmissão de dados técnicos exportados de qualquer país em que o sítio reside ou do país em que você reside) e (ii) o seu uso do Sítio não irá infringir os direitos de propriedade intelectual de qualquer terceira parte.\n\n\n\n## [17. Indemnização](#17)\n\nConcorda em indemnizar e não prejudicar %{company_name}, os seus contratados, e os seus licenciadores, os seus respetivos diretores, empregados e agentes de e contra quaisquer reivindicações e despesas, incluindo taxas de advogados, decorrentes do seu uso do Sítio, incluindo mas não limitado à sua violação deste Acordo.\n\n\n\n## [18. Diversos](#18)\n\nEste Acordo constitui o acordo integral entre si e %{company_name} em relação ao assunto em questão, e poderá ser modificado apenas por uma emenda escrita assinada por um executivo autorizado da %{company_name}, ou pela publicação por parte da %{company_name}, de uma versão revista. Excepto o definido na lei, se houver, disposição em contrário, o presente Acordo, qualquer acesso ou uso do sítio será governado pelas leis do estado da Califórnia, E.U.A, excluindo aos conflitos das disposições da lei, e a receita própria por qualquer disputa emergente de ou\ + \ relativa a qualquer do mesmo será o tribunal federal e estatal localizado em São Francisco, Califórnia. \n\nExceto para os pedidos de medida cautelar ou equitativa ou reclamações relativas aos direitos de propriedade intelectual (que podem ser introduzidos em qualquer tribunal competente, sem a prestação de uma caução), qualquer litígio decorrente do presente acordo será resolvido de acordo com as Regras de Arbitragem de \"Judicial Arbitration and Mediation Service, Inc.” (“JAMS”) por três árbitros apontados em concordância com tais Regras. A arbitragem deverá ter lugar em São Francisco, Califórnia, em língua Inglesa e a decisão arbitral pode ser forçada em qualquer tribunal. A parte prevalecente em qualquer ação ou processo para executar este Acordo terá direito a custos e honorários dos advogados. Se qualquer parte deste Acordo for considerada inválida ou inexequível, essa parte será interpretada de forma a refletir a intenção original das partes e as porções restantes permanecerão em pleno vigor e efeito. A renúncia por qualquer das partes de qualquer termo ou condição deste Acordo ou qualquer violação dos mesmos, em qualquer instância, não vai renunciar a tal termo ou condição ou qualquer violação subsequente. Pode atribuir os seus direitos sob este acordo a qualquer parte que consente a, e concorda em ficar vinculado pelos seus termos e condições; %{company_name} pode assignar os seus direitos sob este Acordo sem condição.\ + \ O presente Acordo será vinculativo e reverterá em benefício das partes e dos seus sucessores.\n\nEste documento é CC-BY-SA. Foi atualizado pela última vez em 31 de Maio de 2013.\n\nOriginalmente adaptado de [Termos de Serviço WordPress](http://en.wordpress.com/tos/).\n" + privacy_topic: + title: "Política de Privacidade" + body: | + + + ## [Que informações reunimos?](#reunião) + + Reunimos informação sobre si assim que se regista no nosso sítio e juntamos dados quando participa no fórum ao ler, escrever e avaliar o conteúdo aí partilhado. + + Ao inscrever-se no nosso sítio, pode ser-lhe pedido para inserir o seu nome e endereço de email. Pode contudo, visitar o nosso sítio sem se registar. O seu endereço de email será verificado por um email contendo uma hiperligação única. Se essa hiperligação for visitada, sabemos que tem controlo do seu endereço de email. + + Ao inscrever-se e publicar, recordamos o endereço IP de onde a publicação é originária. Podemos também reter logs de servidores que incluem endereços IP de todos os pedidos feitos ao servidor. + + + + ## [Para que usamos as suas informações?](#usar) + + Todas as informações que reunimos sobre si podem ser usadas numa das seguintes maneiras: + + * Para personalizar a sua experiência — a sua informação ajuda-nos a responder melhor às suas necessidades individuais. + * Para melhorar o nosso sítio — continuamos a lutar por melhorar as ofertas do nosso sítio com base na informação e feedback que recebemos de si. + * Para melhorar o serviço ao cliente — a sua informação ajuda-nos a responder mais eficazmente aos pedidos de serviço ao cliente e pedidos de suporte. + *Para enviar emails periodicamente — O endereço de email que nos fornece pode ser usado para lhe enviar informação, notificações que peça sobre mudanças em tópicos ou em resposta ao seu nome de utilizador, respostas a inquéritos e/ou outros pedidos ou questões. + + + + ## [Como protegemos a sua informação?](#protect) + + Implementamos uma variedade de medidas de precaução para manter as suas informações pessoais em segurança quando insere, submete ou acede à sua informação pessoal. + + + + ## [Qual é a sua política de retenção de dados?](#retenção-dados) + + Iremos fazer um esforço de boa fé para: + + * Reter logs de servidores contendo o endereço IP de todos os pedidos para este servidor não mais que 90 dias. + * Reter endereços IP associados a utilizadores registados e às suas publicações não mais que 5 anos. + + + + ## [Usamos cookies?](#cookies) + + Sim. Os cookies são pequenos ficheiros que um sítio ou um fornecedor de serviços transfere para o disco do seu computador através do navegador de Internet (se assim o permitir). Estes cookies permitem que o sítio reconheça o seu navegador e, se tiver uma conta registada, associá-lo a essa. + + Usamos cookies para entender e guardar as suas preferências para visitas futuras e compilar dados agregados sobre o tráfego do sítio e a interação com o mesmo para lhe podermos oferecer as melhores experiências e ferramentas no futuro. Podemos contratar fornecedores de serviços para nos ajudarem a entender melhor os visitantes do nosso sítio. Estes fornecedores de serviços não têm permissão para usar a informação reunida em nosso nome excepto para nos ajudar a conduzir e melhorar o nosso negócio. + + + + ## [Divulgamos informação com partes exteriores?](#divulgar) + + Não vendemos, trocamos ou de outra maneira transferimos a sua informação pessoal para partes exteriores. Isto não inclui terceiras partes confiáveis que nos ajudam a operar sítio, conduzir o nosso negócio, ou servi-lo, enquanto estas partes acordarem em manter a informação confidencial. Podemos também partilhar a sua informação quando acreditamos que é apropriado para estar de acordo com a lei, reforçar as políticas do nosso sítio ou proteger os nossos, ou de outros, direitos, propriedades ou segurança. Contudo, informação não-pessoal do visitante pode ser fornecida a outras partes para marketing, publicidade ou outros usos. + + + + ## [Hiperligações de Terceiras Partes](#terceira-parte) + + Ocasionalmente, à nossa descrição, podemos incluir ou oferecer serviços ou produtos de terceiros no nosso sítio. Estes sítios de terceiros têm políticas de privacidade separadas e independentes. Por este motivo, não temos responsabilidade no conteúdo ou atividades destes sítios hiperligados. No entanto, procuramos proteger a integridade do nosso sítio e damos as boas-vindas a qualquer feedback sobre estes sítios. + + + + ## [Cumprimento da Proteção da Privacidade Online Infantil](#coppa) + + O nosso sítio, produtos e serviços são direcionados a pessoas com pelo menos 13 anos de idade. Se este servidor está nos EUA e tem menos de 13 anos de idade, como parte dos requisitos do COPPA ([Proteção da Privacidade Online Infantil](http://en.wikipedia.org/wiki/Children)), não utilize este sítio. + + + + ## [Política de Privacidade Online](#online) + + Estas políticas de privacidade online aplicam-se apenas à informação reunida através do nosso sítio e não à informação reunida offline. + + + + ## [O seu consentimento](#consentimento) + + Ao utilizar o nosso sítio, está a consentir a política de privacidade do nosso sítio. + + + + ## [Mudanças à nossa Política de Privacidade](#mudanças) + + Se decidirmos alterar a nossa política de privacidade, iremos publicar as mudanças nesta página. + + Este documento é CC-BY-SA. Foi atualizado pela última vez em 31 de Maio de 2013. diff --git a/config/locales/server.pt_BR.yml b/config/locales/server.pt_BR.yml index 9763af7bf8..63d3b9c1a3 100644 --- a/config/locales/server.pt_BR.yml +++ b/config/locales/server.pt_BR.yml @@ -6,6 +6,9 @@ # https://www.transifex.com/projects/p/discourse-org/ pt_BR: + stringex: + characters: + number: "-" i18n: transliterate: rule: @@ -17,11 +20,6 @@ pt_BR: short_date_no_year: "D MMM" short_date: "D MMM, YYYY" long_date: "D de MMMM de YYYY h:mma" - time: - formats: - short: "%d/%m/%Y" - short_no_year: "%B %-d" - date_only: "%b %-d, %Y" title: "Discourse" topics: "Tópicos" posts: "postagens" @@ -30,13 +28,50 @@ pt_BR: log_in: "Entrar" via: "%{username} via %{site_name}" is_reserved: "está reservado" - purge_reason: "Automaticamente excluído devido a ser velho e não verificado" + purge_reason: "A conta não verificada, foi excluída." disable_remote_images_download_reason: "Download de imagens remotas foi desativado porque não havia espaço suficiente em disco disponível." errors: + format: '%{attribute} %{message}' messages: too_long_validation: "está limitado para %{max} caracteres; você entrou com %{length}." invalid_boolean: "Boleano inválido." - taken: "já foi tomado" + taken: "já foi escolhido" + accepted: tem que ser aceito + blank: não pode estar em branco + present: deve estar em branco + confirmation: "não combina com %{attribute}" + empty: não pode estar vazio + equal_to: deve ser igual a %{count} + even: precisa ser par + exclusion: está reservado + greater_than: precisar ser maior que %{count} + greater_than_or_equal_to: precisa ser maior ou igual à %{count} + inclusion: não está incluído na lista + invalid: é inválido + less_than: precisa ser menor que %{count} + less_than_or_equal_to: deve ser menor ou igual à %{count} + not_a_number: não é um número + not_an_integer: deve ser um inteiro + odd: deve ser ímpar + record_invalid: 'A validação falhou: %{errors}' + restrict_dependent_destroy: + one: "Não foi possível excluir o registro porque um %{record} dependente existe" + many: "Não foi possível excluir o registro porque %{record} dependentes existem" + too_long: + one: é muito longo (o máximo é 1 caractere) + other: é muito longo (o máximo é %{count} caracteres) + too_short: + one: é muito curto (o mínimo é 1 caractere) + other: é muito curto (o mínimo é %{count} caracteres) + wrong_length: + one: tem o tamanho errado (precisa ter 1 caractere) + other: tem o tamanho errado (precisa ser %{count} caracteres) + other_than: "precisa ser outro que não %{count}" + template: + body: 'Houve problemas com os seguintes campos:' + header: + one: 1 erro impediu este %{model} de ser salvo + other: '%{count} erros impediram este %{model} de ser salvo' embed: load_from_remote: "Houve um erro ao carregar essa mensagem." bulk_invite: @@ -104,6 +139,7 @@ pt_BR: latest: "Últimos tópicos" hot: "Tópicos quentes" too_late_to_edit: "Essa mensagem foi criada há muito tempo. Ela não pode mais ser editada ou apagada." + excerpt_image: "imagem" groups: errors: can_not_modify_automatic: "Você não pode modificar um grupo automático" @@ -157,7 +193,7 @@ pt_BR: ⏎ Tem certeza de que você está fornecendo o tempo adequado para outras pessoas também poderem compartilhar seus pontos de vista?⏎ too_many_replies: "# # # Você está no limite de resposta para este tópico \n \nLamentamos, mas novos usuários estão temporariamente limitados a %{newuser_max_replies_per_topic} respostas no mesmo tópico. \n \nEm vez de adicionar outra resposta, por favor, considere editar suas respostas anteriores ou visitar outros tópicos. \n" - reviving_old_topic: "# # # Reviver este tópico?\n \nA última resposta a este tópico foi a %{dias} atrás. Sua resposta vai levar o tópico para o topo da sua lista e avisar a qualquer um anteriormente envolvido na conversa. \n \nTem certeza de que quer continuar esta velha conversa? \n" + reviving_old_topic: "# # # Reviver este tópico?\n \nA última resposta a este tópico foi a %{days} atrás. Sua resposta vai levar o tópico para o topo da sua lista e avisar a qualquer um anteriormente envolvido na conversa. \n \nTem certeza de que quer continuar esta velha conversa? \n" activerecord: attributes: category: @@ -176,8 +212,8 @@ pt_BR: base: warning_requires_pm: "Você só pode anexar avisos em mensagens privadas." too_many_users: "Você só pode enviar avisos para um usuário de cada vez." - archetype: - cant_send_pm: "Desculpe, não podes enviar uma mensagem particular para esse utilizador." + cant_send_pm: "Desculpe, você não pode enviar uma mensagem privada para este usuário." + no_user_selected: "Você deve escolher um usuário válido." user: attributes: password: @@ -247,6 +283,7 @@ pt_BR: title: "regular" elder: title: "líder" + change_failed_explanation: "Você tentou rebaixar %{user_name} para '%{new_trust_level}'. No entanto o nível de confiança dele já é '%{current_trust_level}'. %{user_name} permanecerá em '%{current_trust_level}' - se você deseja rebaixar um usuário, tranque o nível de confiança primeiro" rate_limiter: slow_down: "Você executou esta ação muitas vezes, tente novamente mais tarde" too_many_requests: "Nós possuímos um limite diário do número de vezes que uma ação pode ser tomada. Por favor aguarde %{time_left} antes de tentar novamente." @@ -347,26 +384,35 @@ pt_BR: activation: action: "Ativar sua conta" already_done: "Desculpe, este link de confirmação não está mais válido. Talvez a sua conta já esteja ativa?" + please_continue: "Sua conta agora está confirmada; você vai ser redirecionado para a página inicial." continue_button: "Continuar no %{site_name}" welcome_to: "Bem-vindo a %{site_name}!" approval_required: "Um moderador tem que aprovar a sua conta para que você possa acessar este fórum. Você receberá um email quando sua conta for aprovada!" post_action_types: off_topic: title: 'Off-Topic' + description: 'Essa mensagem não é relevante para a discussão definida pelo título e primeira mensagem, e deveria ser movida para outro local.' long_form: 'sinalizou isto como off-topic' spam: title: 'Spam' description: 'Este post é claramente uma propaganda. Não é útil nem relevante para esta conversa, mas promocional por natureza.' long_form: 'sinalizado como spam' + email_title: '"%{title}" foi sinalizado como spam' + email_body: "%{link}\n\n%{message}" inappropriate: title: 'Inapropriado' description: 'Este post contém conteúdo que uma pessoa razoável consideraria ofensivo, abusivo, ou uma violação das nossas diretrizes da comunidade.' long_form: 'sinalizado como inapropriado' notify_user: + title: 'Mensagem privada @{{username}}' description: 'Este post contém algo que eu quero falar com essa pessoa diretamente e em particular sobre. Não lançar uma flag.' + long_form: 'usuário de mensagem privada' email_title: 'Sobre a sua postagem "%{title}"' email_body: "%{link}\n\n%{message}" notify_moderators: + title: "Algo mais" + description: 'Essa mensagem requer atenção da moderação por outra razão não listada acima.' + long_form: 'sinalizar isso para atenção da moderação' email_title: 'Uma postagem em "%{title}" requer atenção do moderador' email_body: "%{link}\n\n%{message}" bookmark: @@ -391,7 +437,9 @@ pt_BR: description: 'Este tópico contém um conteúdo que uma pessoa razoável consideraria ofensivo, abusivo, ou violação de nossas diretrizes da comunidade.' long_form: 'sinalizar como impróprio' notify_moderators: + title: "Algo mais" description: 'Este tópico requer atenção moderadora geral, com base na diretrizes, TOS , ou por outro motivo não listados acima.' + long_form: 'sinalizar isso para atenção da moderação' email_title: 'O tópico "%{title}" requer atenção do moderador' email_body: "%{link}\n\n%{message}" flagging: @@ -492,6 +540,45 @@ pt_BR: title: "Top Tópicos Citados" xaxis: "Tópico" num_clicks: "Clicks" + page_view_anon_reqs: + title: "Anônimo" + xaxis: "Dia" + yaxis: "Visualizações de Página Anônimas" + page_view_logged_in_reqs: + title: "Logado" + xaxis: "Dia" + yaxis: "Visualizações de Página de Logados" + page_view_crawler_reqs: + title: "Web Crawlers" + xaxis: "Dia" + yaxis: "Visualizações de Página de Web Crowlers" + page_view_total_reqs: + title: "Total" + xaxis: "Dia" + yaxis: "Visualizações de Página Totais" + http_background_reqs: + title: "Background" + xaxis: "Dia" + http_2xx_reqs: + title: "Status 2xx (OK)" + xaxis: "Dia" + yaxis: "Solicitações bem-sucedidas (Status 2xx)" + http_3xx_reqs: + title: "HTTP 3xx (Redirect)" + xaxis: "Dia" + yaxis: "Solicitações de redirecionamento (Status 3xx)" + http_4xx_reqs: + title: "HTTP 4xx (Client Error)" + xaxis: "Dia" + yaxis: "Erros de Cliente (Status 4xx)" + http_5xx_reqs: + title: "HTTP 5xx (Server Error)" + xaxis: "Dia" + yaxis: "Erros de Servidor (Status 5xx)" + http_total_reqs: + title: "Total" + xaxis: "Dia" + yaxis: "Solicitações totais" dashboard: rails_env_warning: "Seu servidor está rodando no modo %{env}." ruby_version_warning: "Você está executando uma versão do Ruby 2.0.0, que é conhecido por ter problemas. Atualize para o patch 247 ou superior." @@ -510,15 +597,14 @@ pt_BR: s3_backup_config_warning: 'O servidor está configurado para fazer upload de arquivos para o s3, mas pelo menos uma destas configurações não está definida: s3_access_key_id, s3_secret_access_key ou s3_backup_bucket. Vá até as Configurações e atualize estas definições. Veja "How to set up image uploads to S3?" para saber mais.' image_magick_warning: 'O servidor está configurado para criar miniaturas de imagens grandes, mas o ImageMagick não está instalado. Instale o ImageMagick usando seu gerenciador de pacotes preferido ou acesse para fazer download da última versão.' failing_emails_warning: 'Há %{num_failed_jobs} processos de e-mail que falharam. Verifique se o seu arquivo config/discourse.conf e garanta que as configurações do servidor de e-mail estão corretas. Veja os processos com falha no Sidekiq.' - default_logo_warning: "Você não personalizou as imagens do logo para seu site. Atualize logo_url, logo_small_url, e favicon_url nas Configurações do Site." - contact_email_missing: "Você não forneceu um email de contato do site. Por favor atualize o contact_email nas Configurações do Site." - contact_email_invalid: "O email de contato do site é inválido. Por favor atualize o contact_email nas Configurações do Site." - title_nag: "A configuração do título nas Configurações do Site ainda estão no valor padrão. Por favor atualize com título do seu site em Configurações do Site." - site_description_missing: "A definição site_description está em branco. Escreva uma breve descrição do fórum nas Configurações do Site." + default_logo_warning: "Defina os logotipos gráficos para o seu site. Atualize logo_url, logo_small_url, e favicon_url nas Configurações do Site." + contact_email_missing: "Digite um email de contato do site para que você possa ser encontrado para assuntos urgentes sobre o seu site. Atualize-o nas Configurações do Site." + contact_email_invalid: "O email de contato do site é inválido. Atualize-o nas Configurações do Site." + title_nag: "Digite o nome do seu site. Atualize o título nas Configurações do Site." + site_description_missing: "Digite uma frase de descrição do seu site que aparecerá em resultados de busca. Atualize site_description nas Configurações do Site." consumer_email_warning: "Seu site está configurado para usar Gmail (ou outro serviço de email para pessoas). Gmail limita a quantidade de emails que você pode enviar. Considere o uso de um serviço de envio de emails como mandrill.com para assegurar a entregabilidade dos emails enviados." - access_password_removal: "Seu site estava usando a definição access_password, que foi removida. As definições login_required e must_approve_users foram ativadas e devem ser usadas em substituição. Você pode alterá-las em Configurações do Site. Certifique-se de aprovar os usuários na lista de Usuários Pendentes. (Esta mensagem desaparecerá depois de 2 dias.)" - site_contact_username_warning: "A definição site_contact_username está em branco. Por favor atualize-a nas Configurações do Site. Coloque um nome de usuário de um usuário administrador o qual será o remetente das mensagens do sistema." - notification_email_warning: "A definição notification_email está em branco. Por favor atualize-a nas Configurações do Site." + site_contact_username_warning: "Digite o nome de uma membro da equipe do site próximo por quem enviar mensagens particulares automáticas importantes. Atualize site_contact_username nas Configurações do Site." + notification_email_warning: "Emails de notificação não estão sendo enviados por um endereço de email válido no seu domínio; entrega de email será errática e não confiável. Por favor configure notification_email com um endereço de email local válido nas Configurações do Site." content_types: education_new_reply: title: "Educação do Usuário Novo: Primeiras Respostas" @@ -549,6 +635,7 @@ pt_BR: description: "HTML que será adicionado no topo de cada página (após o cabeçalho, antes da navegação ou o título do tópico)." bottom: title: "Rodapé das páginas" + description: "HTML que será adicionado antes da tag " site_settings: censored_words: "Palavras que serão substituídos automaticamente por ■■■■" delete_old_hidden_posts: "Auto-apagar todas as mensagens ocultas que ficar oculta por mais de 30 dias." @@ -566,62 +653,116 @@ pt_BR: allow_duplicate_topic_titles: "Permitir tópicos com títulos duplicados, idênticos." unique_posts_mins: "Quantos minutos antes para um utilizador poder criar uma postagem com o mesmo conteúdo outra vez?" educate_until_posts: "Quando o usuário começa a digitar suas primeiras (n) novos posts, mostrar o novo painel pop-up de educação do usuário no compositor." - title: "Breve título deste site, utilizado na tag title." - site_description: "Descreva este site em uma frase, usada na meta tag description." + title: "O nome deste site, como usado na tag de título." + site_description: "Descreva este site com uma frase, como usado na tag de meta description." + contact_email: "Endereço de email do responsável de contato chave para este site. Usado para notificações críticas tais como sinalizações não controladas, assim como também pela forma de contato em /about para assuntos urgentes." queue_jobs: "APENAS DESENVOLVEDORES! ATENÇÃO! Por padrão, enfileira tarefas no sidekiq. Se desativado, seu site ficará defeituoso." crawl_images: "Recupere imagens de URLs remotas para inserir as dimensões de largura e altura corretos." download_remote_images_to_local: "Converta imagens remotas para imagens locais, transferindo-as; isto evita imagens quebradas." + download_remote_images_threshold: "Espaço mínimo necessário para download das imagens ( em % )" + disabled_image_download_domains: "Imagens hospedadas nestes domínios nunca serão baixadas. \nLista delimitada por |" + ninja_edit_window: "Pelos próximos (n) segundos após a postagem, as edições não terão histórico." post_edit_time_limit: "O autor pode editar ou apagar o seu post para (n) minutos após a postagem. Defina como 0 para sempre." + edit_history_visible_to_public: "Permitir que todos vejam as versões anteriores de uma postagem editada. Quando desativado, somente membros da equipe podem ver o histórico de alterações." + delete_removed_posts_after: "Postagens removidas pelo autor serão automaticamente deletadas depois de (n) horas. Se o valor definido for 0, as postagens serão deletadas imediatamente." max_image_width: "Largura máxima para imagens em um post" max_image_height: "Altura máxima para imagens em um post" category_featured_topics: "Número de tópicos exibidos por categoria na página /categories. Depois de alterar este valor, levará até 15 minutos para a página de categorias atualizar-se." show_subcategory_list: "Mostrar lista de subcategorias ao invés de lista de tópicos ao entrar em uma categoria." fixed_category_positions: "Se selecionado, você será capaz de organizar as categorias em uma ordem fixa. Se nada for feito, as categorias serão listadas em ordem de atividade." + add_rel_nofollow_to_user_content: "Adicionar \"nofollow\" para todo conteúdo incluído por um usuário, exceto links internos (incluindo sub domínios). Para que as mudanças façam efeito é necessário rodar \"rake posts:rebake\"." exclude_rel_nofollow_domains: "Lista de domínios separados por barras verticais (|) onde o nofollow não é adicionado (tld.com irá automaticamente disponibilizar sub.tld.com também) " post_excerpt_maxlength: "Limite de caracteres para o resumo de um post." - logo_url: "O logo à esquerda e acima para seu site eg: http://example.com/logo.png" + post_onebox_maxlength: "Tamanho máximo para um Post em caracteres" + onebox_domains_whitelist: "Uma lista de domínios para os quais permitir oneboxing; estes domínios devem suportar OpenGraph ou oEmbed. Teste-os em http://iframely.com/debug" + logo_url: "A imagem de logo no topo esquerdo do seu site; se deixado em branco, o título do size será mostrado." digest_logo_url: "O logo alternativo usado no topo do resumo de e-mails do seu site. Se deixar em branco `logo_url` será usado. eg: http://example.com/logo.png" - logo_small_url: "O logo à esquerda e acima pequeno para seu site, visto quando se faz rolagem. eg: http://example.com/logo-small.png" + logo_small_url: "A pequena imagem de logo no topo à esquerda do seu site, vista quando rolando a página abaixo. Se deixado em branco, um símbolo de homepage será mostrado." favicon_url: "Um favicon para o seu site" + mobile_logo_url: "A imagem posicionada fixada no topo à esquerda do site móvel. Se deixado em branco, o texto de título do site será usado." apple_touch_icon_url: "Ícone usado para dispositivos Apple. Tamanho recomendado é 144px por 144px." + notification_email: "O endereço de email utilizado quando enviado emails do sistema. O domínio especificado deve ter registros SPF, DKIM e PTR, devidamente configurados para que os email funcionem corretamente." email_custom_headers: "A lista delimitada por barras verticais de cabeçalhos de e-mail personalizados" + email_subject: "Customizar o formato do assunto dos emails padrões. \nRef https://meta.discourse.org/t/customize-subject-format-for-standard-emails/20801" use_https: "Deverá a URL completa para o site (Discourse.base_url) ser http ou https? Não ative isso a MENOS QUE O HTTPS ESTEJA CONFIGURADO E FUNCIONANDO!" + summary_score_threshold: "A pontuação mínima requerida para uma postagem ser incluída no \"Resuma este tópico\" " summary_posts_required: "Mensagens mínimas em um tópico antes de 'Resumir este tópico' ficar habilitado" summary_likes_required: "Curtidas mínimas em um tópico antes de 'Resumir este tópico, ficar habilitado" summary_percent_filter: "Quando um usuário clicar em 'Resumor este tópico', mostrar os melhores % mensagens" + summary_max_results: "Máximo número de posts quando resumidos por Categoria " + enable_private_messages: "Permitir usuários de nível 1 a criar e responder mensagens privadas." enable_long_polling: "O sistema de mensagens das notificações pode fazer solicitações longas." + long_polling_base_url: "URL Utilizada para \"long polling\" ( Quando um CDN for configurado, tenha certeza que essa configuração seja a padrão) ex: http://origin.site.com" + long_polling_interval: "Tempo que o servidor deve aguardar antes de responder quando não existe nenhum dado para ser enviado. (apenas usuários logados)" + polling_interval: "Com que frequencia os clientes podem solicitar o servidor em milisegundos" anon_polling_interval: "Com que frequencia os clientes não registrados podem solicitar o servidor em milisegundos" + background_polling_interval: "Com que frequência os clientes podem solicitar o servidor em milisegundos ( em segundo plano )" auto_track_topics_after: "Quantos milisegundos esperar antes que um tópico seja automaticamente rastreado (0 para sempre, -1 para nunca)" new_topic_duration_minutes: "Quantos minutos um tópico permanece considerado como novo (-1 para sempre, -2 para desde a última visita)" flags_required_to_hide_post: "As postagens vão ser escondidos automaticamente quando o númereo de sinalizações atingir este número (0 para nunca)" cooldown_minutes_after_hiding_posts: "Número de minutos que um usuário deve esperar antes de poder editar uma publicação que foi ocultada devido a sinalização da comunidade." max_topics_in_first_day: "O número máximo de tópicos que um usuário tem permissão para criar em seu primeiro dia no site" max_replies_in_first_day: "O número máximo de respostas que um usuário tem permissão para criar em seu primeiro dia no site" + num_flags_to_block_new_user: "Se os posts de um novo usuário receber várias sinalizações de spam de num_users_to_block_new_user diferentes usuários, esconder todos os posts deste usuário e prevenir postagem futura. 0 para desabilitar." + num_users_to_block_new_user: "Se os posts de um novo usuário receber num_flags_to_block_new_user sinalizações de spam deste tanto de usuários diferentes, esconder todos os seus posts e prevenir posts futuros. 0 oara desabilitar." notify_mods_when_user_blocked: "Se um usuário for bloqueado de forma automática, enviar uma mensagem para todos os moderadores." flag_sockpuppets: "Se um novo usuário responder um tópico usando o mesmo endereço de IP do novo usário que começou o tópico, sinalize as duas publicações como um potencial spam." + traditional_markdown_linebreaks: "Use quebras de linhas tradicionais em Markdown, as quais requerem dois espaços à direita para uma quebra de linha." post_undo_action_window_mins: "Número de minutos permitidos aos usuários para desfazerem uma ação recente em uma publicação (curtir, sinalizar, etc)" must_approve_users: "Gerenciadores deverão aprovar novas contas de usuários antes das mesmas serem permitidas o acesso ao site." ga_tracking_code: "Código de monitoramento do Google analytics (ga.js), ex: UA-12345678-9; veja http://google.com/analytics" ga_domain_name: "Nome do domínio para o Google analytics (ga.js), ex: mysite.com; veja http://google.com/analytics" ga_universal_tracking_code: "Código de monitoramento Google Universal Analytics (analytics.js), ex: UA-12345678-9; veja http://google.com/analytics" ga_universal_domain_name: "Nome de domínio do Google Universal Analytics (analytics.js), ex: meusite.com; veja http://google.com/analytics" + enable_escaped_fragments: "Recuar para a Google's Ajax-Crawling API se nenhum webcrawler é detectado. Vide https://support.google.com/webmasters/answer/174992?hl=en" enable_noscript_support: "Ativar suporte a tag <noscript>" allow_moderators_to_create_categories: "Permitir a criação de categorias por moderadores" - top_menu: "Determine quais itens aparecem na navegação da homepage e em que ordem. Exemplo latest|new|unread|starred|categories|top|read|posted" + cors_origins: "Origens permitidas para pedidos de origem-cruzada (CORS). Cada origem deve incluir http:// ou https://. A variável de ambiente DISCOURSE_ENABLE_CORS deve ser definida como true para habilitar CORS." + top_menu: "Determina quais items aparecem na navegação da homepage, e em qual ordem. Exemplo latest|new|unread|categories|top|read|posted|bookmarks" post_menu: "A ordem dos items no menu da postagem." + post_menu_hidden_items: "Os items de menu a ser escondidos por padrão no menu de postagem, a menos que uma elipse de expansão seja clicada." share_links: "Determine quais itens aparecem na caixa de diálogo de compartilhamento, e em qual ordem." track_external_right_clicks: "Rastrear cliques externos que são clicados com o botão direito (ex: abrir em nova aba) desativado por padrão, pois tem que reescrever urls, quebrando a usabilidade" + site_contact_username: "Um nome de usuário da staff para enviar todas as mensagens particulares em nome dele. Se deixado em branco, a conta padrão System será usada." + send_welcome_message: "Enviar a todos os novos usuários uma mensagem particular de boas vindas com um guia de início rápido." + suppress_reply_directly_below: "Não mostrar a contagem de respostas expansível em um post quando há apenas uma única resposta diretamente abaixo deste post." + suppress_reply_directly_above: "Não mostrar a em-resposta-à expansível em um post quando há apenas uma única resposta diretamente acima deste post." + suppress_reply_when_quoting: "Não mostrar a em-resposta-à expansível em um post quando o post cita a resposta." + max_reply_history: "Número máximo de respostas para expandir quando expandindo em-resposta-à" + experimental_reply_expansion: "Esconder respostas intermediárias quando expandindo uma resposta à (experimental)" + topics_per_period_in_top_summary: "Número de melhores tópicos mostrados no sumário padrão de melhores tópicos." + topics_per_period_in_top_page: "Número de melhores tópicos mostrados no 'Exibir Mais' melhores tópicos quando expandido." + redirect_users_to_top_page: "Automaticamente redirecionar usuários novos e há muito ausentes para a página melhores." + show_email_on_profile: "Mostrar o email de um usuário em seus perfis (apenas visível a eles mesmos e staff)" + email_token_valid_hours: "Tokens de Esqueceu senha / ativar conta são válidas por (n) horas." + email_token_grace_period_hours: "Tokens de esqueceu senha / ativar conta ainda serão válidas por um período extra de (n) horas depois de serem resgatados." enable_badges: "Habilita o sistema de emblemas (experimental)" + allow_index_in_robots_txt: "Especificar no robots.txt que este site é permitido de ser indexado por sistemas de busca na web." + email_domains_blacklist: "Uma lista de domínios de email com os quais os usuários não são permitidos de registrar contas. Exemplo: mailinator.com trashmail.net" + email_domains_whitelist: "Uma lista de domínios de email com os quais os usuários DEVEM registrar contas. AVISO: Usuários com domínios de email outros que aqueles listados não serão permitidos!" + forgot_password_strict: "Não informar usuários da existência de uma conta quando eles usam o diálogo de esquecimento de senha." + log_out_strict: "Quando deslogando, deslogar TODAS as sessões do usuário em todos os dispositivos" version_checks: "Pingar Discourse Hub para atualizações de versão e exibir mensagens de versão no Painel em /admin" + new_version_emails: "Enviar um email para o endereço contact_email quando uma nova versão do Discourse estiver disponível." port: "Se você quiser especificar a porta na URL. Útil no modo de desenvolvimento. Deixe em branco para nada." force_hostname: "Se você quiser especificar um hostname na URL. Útil no modo de desenvolvimento. Deixe em branco para nada." invite_expiry_days: "Quantos dias as chaves de convite são válidas." + invite_passthrough_hours: "Quando tempo um usuário pode usar uma chave de convite previamente usada para logar, em horas" + invite_only: "Cadastro público está desabilitado, todos os novos usuários devem ser explicitamente convidados por outros membros ou equipe." + login_required: "Exigir autenticação para ler conteúdo neste site, desabilitar acesso anônimo." + min_username_length: "Tamanho mínimo de nome de usuário em caracteres. AVISO: QUAISQUER USUÁRIOS EXISTENTES COM NOMES MENORES DO QUE ISSO SERÃO INCAPACITADOS DE ACESSAR O SITE." + max_username_length: "Tamanho máximo de nome de usuário em caracteres. AVISO: QUAISQUER USUÁRIOS EXISTENTES COM NOMES MAIORES DO QUE ISSO SERÃO INCAPACITADOS DE ACESSAR O SITE." min_password_length: "Comprimento mínimo da senha." + block_common_passwords: "Não permitir senhas que estiverem entre as 10,000 senhas mais comuns." + enable_sso: "Habilitar single sign on através de um site externo (AVISO: pode impedir que qualquer um se logue se não for adequadamente configurado quando habilitado; além disso desabilita convites)" + enable_sso_provider: "Implementar o protocolo Discourse SSO no endpoint /session/sso_provider , exige que sso_secret esteja definido" sso_url: "URL destino do logon único" - sso_secret: "Cadeia secreta usada para criptografar/descriptografar informações SSO, certifique-se que tenha 10 caracteres ou mais" + sso_secret: "String secreta usada para autenticar criptograficamente informação de SSO, esteja certo de que possui 10 ou mais caracteres" sso_overrides_email: "Substitui e-mail local com site externo de e-mail do servidor SSO (ATENÇÃO: discrepâncias podem ocorrer devido à normalização de e-mails locais)" sso_overrides_username: "Substitui nome de usuário local com nome de usuário do site externo do servidor SSO (ATENÇÃO: discrepâncias podem ocorrer devido a diferenças nos requerimentos de comprimento de nome de usuário)" sso_overrides_name: "Substitui nome local com o nome do site externo do servidor SSO (ATENÇÃO: discrepâncias podem ocorrer devido à normalização de nomes locais)" + sso_overrides_avatar: "Substitui o avatar do usuário pelo avatar do site externo de SSO. Se habilitado, desabilitar allow_uploaded_avatars é altamente recomendado" + allow_new_registrations: "Permitir registro de novos usuários. Desmarque isso para prevenir quem quer que seja de criar uma nova conta." enable_google_logins: "(obsoleto) Habilitar a autenticação do Google. Este é o método de autenticação OpenID que o Google está abandonando. Novas instalações não vai funcionar com isso. Use o Google Oauth2 ao invés. Instalações existentes devem passar para Google Oauth2 até 20 de Abril de 2015." enable_yahoo_logins: "Ativar autenticação pelo Yahoo" enable_google_oauth2_logins: "Ativar autentincação Google Oauth2. Esse é o método de autenticação que o Google atualmente suporta. Necessita de uma chave e chave secreta." @@ -638,44 +779,184 @@ pt_BR: github_client_secret: "Client secret para autenticação via Github, registrado em https://github.com/settings/applications" allow_restore: "Permitir restauração, que pode substituir todos os dados do site! Deixe falsa, a menos que você pretenda restaurar um backup" maximum_backups: "A quantidade máxima de backups para manter no disco. Backups mais antigos são excluídos automaticamente" + backup_daily: "Criar automaticamente um backup do site uma vez ao dia." + enable_s3_backups: "Fazer upload dos backups para o S3 quando completado. IMPORTANTE: exige credenciais válidas do S3 configuradas nas Configurações de Arquivos." s3_backup_bucket: "O repositório remoto para realizar backups. AVISO: Certifique-se de que é um repositório privado." active_user_rate_limit_secs: "Qual a frequencia de atualização do campo 'última vez visto em', em segundos." + verbose_localization: "Mostrar dicas estendidas de localização na UI" previous_visit_timeout_hours: "Quanto tempo uma visita dura antes de considerarmos como 'última visita', em horas." + rate_limit_create_topic: "Após criar um tópico, os usuários devem aguardar (n) segundos antes de criar um outro tópico." + rate_limit_create_post: "Após postar, os usuários devem aguardar (n) segundos antes de criar um outro post." + rate_limit_new_user_create_topic: "Após criar um tópico, novos usuários devem aguardar (n) segundos antes de criar um outro tópico." + rate_limit_new_user_create_post: "Após postar, novos usuários devem aguardar (n) segundos antes de criar um outro post." + max_likes_per_day: "Número máximo de likes por usuário por dia." + max_flags_per_day: "Número máximo de sinalizações por usuário por dia." + max_bookmarks_per_day: "Número máximo de novos favoritos por usuário por dia." + max_edits_per_day: "Número máximo de edições que um usuário pode fazer por dia." + max_topics_per_day: "Número máximo de postagens que um usuário pode criar por dia." + max_private_messages_per_day: "Número máximo de mensagens privadas que os usuários podem criar por dia." + max_invites_per_day: "Número máximo de convites que um usuário pode enviar por dia." suggested_topics: "Número de tópicos sugeridos mostrados no final de um tópico." limit_suggested_to_category: "Mostrar apenas tópicos da categoria atual em tópicos sugeridos." clean_up_uploads: "Remover envios sem referência para evitar hospedagem ilegal. AVISO: você pode querer fazer um backup do seu diretório de /uploads antes de habilitar essa configuração." clean_orphan_uploads_grace_period_hours: "Carência (em horas) antes de um upload órfão ser removido." purge_deleted_uploads_grace_period_days: "Carência (em dias) antes que um upload excluído seja apagado." - purge_inactive_users_grace_period_days: "Carência (em dias) antes de um usuário que não tenha ativado sua conta seja deletado." + purge_unactivated_users_grace_period_days: "Período extra (em dias) antes que um usuário que não ativou sua conta seja deletado." + enable_s3_uploads: "Colocar uploads no armazenamento da Amazon S3. IMPORTANTE: exige credenciais válidas da S3 (ambos id da chave de acesso & segredo da chave de acesso)." + s3_upload_bucket: "Nome do bucket da Amazon S3 no qual os arquivos serão enviados. AVISO: precisa ser em minúsculas, sem pontos, sem underscores." + s3_access_key_id: "A id da chave de acesso da Amazon S3 que será usada para fazer upload de imagens." + s3_secret_access_key: "O segredo da chave de acesso da Amazon S3 que será usada para fazer upload de imagens." + s3_region: "O nome da região da Amazon S3 que será usada para fazer upload de imagens." + enable_flash_video_onebox: "Habilitar o embutir de links swf e flv (Adobe Flash) em oneboxes. AVISO: pode introduzir riscos de segurança." + default_invitee_trust_level: "Nível de confiança (0-4) padrão para usuários convidados." + default_trust_level: "Nível de confiança (0-4) padrão para todos os novos usuários." + tl1_requires_topics_entered: "Quantos tópicos um usuário novo deve entrar antes de ser promovido a nível de confiança 1." + tl1_requires_read_posts: "Quantas postagens um usuário novo deve ler antes de ser promovido a nível de confiança 1." + tl1_requires_time_spent_mins: "Quantos minutos um usuário novo deve ler postagens antes de ser promovido a nível de confiança 1." + tl2_requires_topics_entered: "Quantos tópicos um usuário deve entrar antes de ser promovido a nível de confiança 2." + tl2_requires_read_posts: "Quantas postagens um usuário deve ler antes de ser promovido a nível de confiança 2." + tl2_requires_time_spent_mins: "Quantos minutos um usuário deve ler posts antes de ser promovido a nível de confiança 2." + tl2_requires_days_visited: "Quantos dias um usuário deve visitar o site antes de ser promovido a nível de confiança 2." + tl2_requires_likes_received: "Quantos likes um usuário deve receber antes de ser promovido a nível de confiança 2." + tl2_requires_likes_given: "Quantos likes um usuário deve dar antes de ser promovido a nível de confiança 2." + tl2_requires_topic_reply_count: "Quantos tópicos um usuário deve responder antes de ser promovido a nível de confiança 2." + tl3_requires_days_visited: "Número mínimo de dias que um usuário precisa ter visitado o site nos últimos 100 dias para se qualificar a promoção ao nível de confiança 3. (0 até 100)" + tl3_requires_topics_replied_to: "Número mínimo de tópicos que um usuário precisa ter respondido o site nos últimos 100 dias para se qualificar a promoção ao nível de confiança 3. (0 ou mais)" + tl3_requires_topics_viewed: "O percentual de tópicos criados nos últimos 100 dias que um usuário precisa ter visto para se qualificar a promoção ao nível de confiança 3. (0 até 100)" + tl3_requires_posts_read: "O percentual de posts criados nos últimos 100 dias que um usuário precisa ter visto para se qualificar a promoção ao nível de confiança 3. (0 até 100)" + tl3_requires_topics_viewed_all_time: "O mínimo total de tópicos que um usuário precisa ter visto para se qualificar a promoção ao nível de confiança 3." + tl3_requires_posts_read_all_time: "O mínimo total de postagens que um usuário precisa ter lido para se qualificar a promoção ao nível de confiança 3." + tl3_requires_max_flagged: "Usuário precisa não ter mais que x postagens sinalizadas por x usuários diferentes nos últimos 100 dias para se qualificar à promoção ao nível de confiança 3, onde x é o valor dessa configuração. (0 ou mais)" + tl3_promotion_min_duration: "O número mínimo de dias que uma promoção ao nível 3 dura antes que um usuário possa ser rebaixado novamente ao nível de confiança 2." + tl3_requires_likes_given: "O número mínimo de likes que devem ser dados nos últimos 100 dias para qualificar para promoção ao nível de confiança 3." + tl3_requires_likes_received: "O número mínimo de likes que devem ser recebidos nos últimos 100 dias para se qualificar para promoção ao nível de confiança 3." + tl3_links_no_follow: "Não remover rel=nofollow de links postados por usuários do nível de confiança 3." min_trust_to_create_topic: "O nível de confiança mínimo necessário para criar um novo tópico." min_trust_to_edit_wiki_post: "O nível de confiança mínimo necessário para editar uma postagem marcada como wiki." + newuser_max_links: "Quandos links um usuário novo pode adicionar a um post." + newuser_max_images: "Quantas imagens um usuário novo pode adicionar a um post." + newuser_max_attachments: "Quantos arquivos anexos um usuário novo pode adicionar a um post." + newuser_max_mentions_per_post: "Número máximo de notificações @nome que um usuário novo pode usar em um post." + newuser_max_replies_per_topic: "Número máximo de respostas que um usuário novo pode fazer em um único tópico até que alguém os responda." + max_mentions_per_post: "Número máximo de notificações @nome que quem quer que seja pode usar em um post." + create_thumbnails: "Criar thumbnails e lightbox para imagens que são muito largas para caber em uma postagem." + email_time_window_mins: "Aguardar (n) minutos antes de enviar quaisquer emails de notificação, para dar aos usuários uma chance de editarem e finalizarem seus posts." + email_posts_context: "Quantas respostas prévias incluir como contexto em emails de notificação." + max_word_length: "O tamanho máximo permitido de uma palavra, em caracteres, num título de tópico." title_fancy_entities: "Converter caracteres ASCII comuns em entidades HTML nos títulos dos tópicos, ala SmartyPants http://daringfireball.net/projects/smartypants/" + min_title_similar_length: "O tamanho mínimo de um título antes que ele seja checado por tópicos semelhantes." + min_body_similar_length: "O tamanho mínimo do corpo de conteúdo de um post antes que ele seja checado por tópicos semelhantes." + category_colors: "Uma lista de valores hexadecimais de cor permitidos para categorias." + category_style: "Estilo visual para emblemas de categoria." + max_image_size_kb: "O tamanho máximo de upload de imagem em kB. Isso precisa ser configurado no nginx (client_max_body_size) / apache ou também proxy." + max_attachment_size_kb: "O tamanho máximo de upload de arquivos anexos em kB. Isso precisa ser configurado no nginx (client_max_body_size) / apache ou também proxy." + authorized_extensions: "Uma lista de extensões de arquivo permitidas para upload (use '*' para permitir todos os tipos de arquivos)" + max_similar_results: "Quantos tópicos semelhantes exibir acima do editor quando compondo um novo tópico. A comparação é baseada no título e no corpo." title_prettify: "Prevenir erros comuns em títulos, incluindo caps-lock ligado, primeira letra minúscula, excesso de ! e ?, pontos extras no final, etc." + topic_views_heat_low: "Após este tanto de visualizações, o campo de visualizações é levemente realçado." + topic_views_heat_medium: "Após este tanto de visualizações, o campo de visualizações é moderadamente realçado." + topic_views_heat_high: "Após este tanto de visualizações, o campo de visualizações é fortemente realçado." + cold_age_days_low: "Após este tanto de dias de conversa, a última data de atividade é levemente realçada." + cold_age_days_medium: "Após este tanto de dias de conversa, a última data de atividade é moderadamente realçada." + cold_age_days_high: "Após este tanto de dias de conversa, a última data de atividade é fortemente realçada." + history_hours_low: "Uma postagem editada dentro deste tanto de horas tem o indicador de edição levemente realçado." + history_hours_medium: "Uma postagem editada dentro deste tanto de horas tem o indicador de edição moderadamente realçado." + history_hours_high: "Uma postagem editada dentro deste tanto de horas tem o indicador de edição fortemente realçado." + topic_post_like_heat_low: "Após a proporção likes:post exceder esta proporção, o campo de contagem de posts é levemente realçado." + topic_post_like_heat_medium: "Após a proporção likes:post exceder esta proporção, o campo de contagem de posts é moderadamente realçado." + topic_post_like_heat_high: "Após a proporção likes:post exceder esta proporção, o campo de contagem de posts é fortemente realçado." faq_url: "Se você possui um FAQ hospedado em outro local e que você queira usar, forneça a URL completa aqui." tos_url: "Se você tem um documento de Termos de Serviço hospedado em algum outro local que você queira usar, forneça a URL completa aqui." privacy_policy_url: "Se você tem um documento de Política de Privacidade hospedado em algum outro local que você queira usar, forneça a URL completa aqui." newuser_spam_host_threshold: "Quantas vezes um usuário pode postar um link para o mesmo site dentro de`newuser_spam_host_posts` posts antes de ser considerado spam." + white_listed_spam_host_domains: "Uma lista de domínios excluídas do teste de spam. Usuários novos nunca serão restringidos de criar posts com links para estes domínios." + staff_like_weight: "Qual o fator de ênfase extra a dar para likes da staff." + levenshtein_distance_spammer_emails: "Ao equiparar emails de spammers, o número da diferença de caracteres que ainda assim irá permitir uma equiparação imprecisa." + max_new_accounts_per_registration_ip: "Se já houver (n) contas com o nível de confiança 0 deste IP (e nenhum for membro da staff ou tiver NC2 ou mais alto), parar de aceitar novas assinaturas daquele IP." + min_ban_entries_for_roll_up: "Ao clicar no botão Combinar, criará uma entrada de banimento de subrede se houverem ao menos (N) entradas." + max_age_unmatched_emails: "Excluir entradas de emails filtrados não combinadas após (N) dias." + max_age_unmatched_ips: "Excluir entradas de IPs filtrados não combinadas após (N) dias." + num_flaggers_to_close_topic: "Número mínimo de usuários sinalizadores únicos que são exigidos para que um tópico seja automaticamente pausado por intervenção" + num_flags_to_close_topic: "Número mínimo de sinalizações ativas que são exigidas para que um tópico seja automaticamente pausado por intervenção" + reply_by_email_enabled: "Habilitar responder tópicos via email" reply_by_email_address: "Template de resposta por email, por exemplo: %{reply_key}@reply.example.com or replies+%{reply_key}@example.com" + disable_emails: "Impedir o Discourse de enviar qualquer tipo de emails." + strip_images_from_short_emails: "Arrancar imagens de emails cujo tamanho seja menor que 2800 Bytes" + short_email_length: "Tamanho de email curto em Bytes" + pop3_polling_enabled: "POLLar via POP3 por respostas via email." + pop3_polling_ssl: "Usar SSL ao conectar ao servidor POP3. (Recomendado)" + pop3_polling_period_mins: "O período em minutos para checar a conta de POP3 por emails. NOTA: exige reinício." + pop3_polling_port: "A porta em que conectar a uma conta POP3." + pop3_polling_host: "O host no qual conectar via POP3 para pegar emails." + pop3_polling_username: "O nome de usuário para a conta POP3 na qual pegar emails." + pop3_polling_password: "A senha para a conta POP3 na qual pegar emails." + email_in: "Permitir usuários de postar novos tópicos via email (requer pegar emails via POP3). Configure os endereços na aba \"Configurações\" de cada categoria." + email_in_min_trust: "O nível de confiança mínimo que um usuário necessita ter para ser permitido a postar novos tópicos via email." + email_prefix: "A [etiqueta] usada no assunto dos emails. Ele será por padrão 'título' se não configurada." + email_site_title: "O título do site usado como o enviador dos emails do site. Por padrão será 'título' se não for configurado. Se o seu 'título' contiver caracteres que não são permitidos em strings de enviador de emails, utilize esta configuração." + minimum_topics_similar: "Quantos tópicos devem existir antes que tópicos semelhantes sejam apresentados ao compor novos tópicos." + relative_date_duration: "Número de dias após postar quando datas de postagem serão mostradas como relativas (7d) ao invés de absolutas (20 Fev)." + delete_user_max_post_age: "Não permitir deletar usuários cujo primeiro post for mais antigo do que (x) dias." delete_all_posts_max: "Número máximo de postagens que podem ser apagadas de uma vez com o botão Remover Todos Postagens. Se um usuário tiver mais postagens do que este número, as postagens não poderão ser todas removidas de uma vez e o usuário não poderá ser removido." username_change_period: "O número de dias após o registro para que as contas possam mudar o seu nome de usuário (0 para não permitir a mudança de nome de usuário)." email_editable: "Permitir que os usuários alterem o seu endereço de e-mail após o registro." + logout_redirect: "Local para onde redirecionar o navegador após o logout EX: (http://somesite.com/logout)" + allow_uploaded_avatars: "Permitir usuários de fazer upload de avatares personalizados." + allow_animated_avatars: "Permitir usuários de usar gifs animados como avatares. AVISO: execute o avatars:refresh rake test após mudar essa configuração." + allow_animated_thumbnails: "Gera thumbnails animados a partir de gifs animados." + automatically_download_gravatars: "Fazer download de Gravatars dos usuários ao criar conta ou mudança de email." + digest_topics: "O número máximo de tópicos a serem mostrados no resumo via email." + digest_min_excerpt_length: "O excerto mínimo de post no resumo via email, em caracteres." default_digest_email_frequency: "Quantas vezes os usuários recebem emails de resumo por padrão. Eles podem alterar essa configuração em suas preferências." + suppress_digest_email_after_days: "Suprimir emails de resumo para usuários não vistos no site há mais do que (n) dias." + disable_digest_emails: "Desabilitar emails de resumo para todos os usuários." default_external_links_in_new_tab: "Abrir links externos em uma nova guia. Os usuários podem mudar isso em suas preferências." + detect_custom_avatars: "Se ou não deve se checar que os usuários fizeram upload de avatares personalizados." + max_daily_gravatar_crawls: "Número máximo de vezes que o Discourse irá checar o Gravatar por avatares personalizados em um dia" public_user_custom_fields: "Uma lista de permissões de campos personalizados para um usuário que pode ser mostrado publicamente." + allow_profile_backgrounds: "Permitir usuários de fazerem upload de backgrounds para o perfil" + sequential_replies_threshold: "Número de posts que um usuário precisa fazer em seguida num tópico antes de ser relembrado sobre muitas respostas sequenciais." enable_mobile_theme: "Os dispositivos móveis usam um tema mobile-friendly, com a possibilidade de mudar para o site completo. Desative isso se você quiser usar um estilo personalizado que é totalmente responsivo." + dominating_topic_minimum_percent: "Qual o percentual de postagens que um usuário precisa fazer em um tópico antes de ser relembrado sobre ser excessivamente dominante em um tópico." + suppress_uncategorized_badge: "Não mostrar emblema para assuntos não categorizados em listas de tópicos" global_notice: "Mostrar um banner global URGENTE, de EMERGÊNCIA para todos os visitantes, mude para branco para esconder (HTML permitido)." + disable_edit_notifications: "Desabilitar modificação de notificações pelo sistema quando 'download_remote_images_to_local' estiver ativado." + enable_names: "Mostrar o nome completo do usuário em seu perfil, cartão de usuário, e emails. Desabilitar para esconder o nome completo em todos os lugares." + display_name_on_posts: "Também exibir o nome completo do usuário em suas mensagens" + invites_per_page: "Mostrar convites padrões no página de usuário" short_progress_text_threshold: "Após o número de mensagens em um tópico for acima deste número, a barra de progresso mostrará apenas o número da mensagem atual. Se você alterar a largura da barra de progresso, você pode precisar alterar este valor." default_code_lang: "Realce de sintaxe padrão da linguagem de programação aplicada a blocos de código GitHub (lang-auto, Ruby, Python, etc)" + warn_reviving_old_topic_age: "Quando alguém começa a responder a um tópico mais velho do que este número de dias, um aviso será exibido para desencorajar o usuário de reviver uma velha discussão. Desabilite definindo para 0." + autohighlight_all_code: "Aplicar código destacando todos os blocos de código pré-formatados, mesmo quando não for específica o idioma" + embeddable_host: "Host que pode incorporar os comentários deste fórum do Discourse" + feed_polling_enabled: "Se um feed RSS / ATOM são importados como mensagens" + feed_polling_url: "URL do feed RSS / ATOM para importar" + embed_by_username: "Nome de usuário Discourse para o usuário que cria os tópicos" + embed_truncate: "Cortar os posts embutidos." + embed_category: "Categoria dos posts embutidos." embed_post_limit: "Número máximo de respostas para embutir" + embed_whitelist_selector: "Seletor CSS para os elementos que são permitidos em embeds." + embed_blacklist_selector: "Selector CSS para elementos que são removidos dos embeds." notify_about_flags_after: "Se houver sinalizações sem ações após muitas horas, envia um e-mail para contact_email. Ajuste para 0 para desligar." + enable_cdn_js_debugging: "Permitir /logs de mostrar os devidos erros ao adicionar permissões de crossorigin em todos os includes de js." show_create_topics_notice: "Se o site tem menos de 5 tópicos públicos, mostrar um aviso pedindo para os administradores criarem alguns tópicos." + vacuum_db_days: "Executar VACUUM FULL ANALYZE para recuperar o espaço do DB após migrações (definir como 0 para desabilitar)" + prevent_anons_from_downloading_files: "Impedir que usuários anônimos façam download de arquivos anexados. AVISO: isso irá impedir quaisquer componentes do site que não sejam imagens, tendo sido postados como anexos, de funcionar." + enable_emoji: "Habilitar emoji" + emoji_set: "Como você gostaria do seu emoji?" errors: invalid_email: "Endereço de email inválido" invalid_username: "Não há nenhum usuário com esse nome de usuário." + invalid_integer_min_max: "O valor deve estar entre %{min} e %{max}." + invalid_integer_min: "O valor deve ser %{min} ou maior." + invalid_integer_max: "O valor não pode ser maior que %{max}." + invalid_integer: "O valor deve ser um inteiro." + regex_mismatch: "O valor não está de acordo com o formato exigido." invalid_string: "Valor inválido." invalid_string_min_max: "Precisa estar entre %{min} e %{max} caracteres." invalid_string_min: "Precisa ter no mínimo %{min} caracteres." invalid_string_max: "Deve ser não mais que %{max} caracteres." + invalid_reply_by_email_address: "O valor deve conter '%{reply_key}' e ser diferente do email de notificação." notification_types: mentioned: "%{display_username} mencionou-te em %{link}" liked: "%{display_username} curtiu sua postagem em %{link}" @@ -695,6 +976,11 @@ pt_BR: category: 'Categorias' topic: 'Resultados' user: 'Utilizadores' + sso: + not_found: "Não foi possível procurar ou criar conta, contate o administrador do site" + account_not_approved: "A conta está pendente de aprovação, você receberá uma notificação por email assim que for aprovada" + unknown_error: "Erro ao fazer update de informação, contate o administrador do site" + timeout_expired: "O tempo de login ultrapassou o limite, por favor tente logar de novo" original_poster: "Postador original" most_posts: "Maior parte das postagens" most_recent_poster: "Maior parte das Postagens Recentes" @@ -711,12 +997,35 @@ pt_BR: other: "Eu movi %{count} postagens para este tópico: %{topic_link}" change_owner: post_revision_text: "Autoria transferida de %{old_user} para %{new_user}" + emoji: + errors: + name_already_exists: "Desculpe, o nome '%{name}' já está sendo usado por outro emoji." + error_while_storing_emoji: "Desculpe, houve um erro ao armazenar o emoji." topic_statuses: archived_enabled: "Este tópico está agora arquivado. Está congelado e não pode ser alterado de qualquer forma." archived_disabled: "Este tópico foi agora desarquivado. Já não está congelado, e pode ser alterado." closed_enabled: "Este tópico está agora fechado. Novas respostas não são aceitas." closed_disabled: "Este tópico está agora aberto. Novas respostas serão aceitas." + autoclosed_enabled_days: + one: "Este tópico foi automaticamente fechado após 1 dia. Novas respostas não são mais permitidas." + other: "Este tópico foi automaticamente fechado após %{count} dias. Novas respostas não são mais permitidas." + autoclosed_enabled_hours: + one: "Este tópico foi fechado automaticamente após 1 hora. Novas respostas não são mais permitidas." + other: "Este tópico foi fechado automaticamente após %{count} horas. Novas respostas não são mais permitidas." + autoclosed_enabled_minutes: + one: "Este tópico foi fechado automaticamente após 1 minuto. Novas respostas não são mais permitidas." + other: "Este tópico foi fechado automaticamente após %{count} minutos. Novas respostas não são mais permitidas." + autoclosed_enabled_lastpost_days: + one: "Este tópico foi fechado automaticamente 1 dia depois da úlima resposta. Novas respostas não são mais permitidas." + other: "Este tópico foi fechado automaticamente %{count} dias depois da úlima resposta. Novas respostas não são mais permitidas." + autoclosed_enabled_lastpost_hours: + one: "Este tópico foi fechado automaticamente 1 hora depois da úlima resposta. Novas respostas não são mais permitidas." + other: "Este tópico foi fechado automaticamente %{count} horas depois da úlima resposta. Novas respostas não são mais permitidas." + autoclosed_enabled_lastpost_minutes: + one: "Este tópico foi fechado automaticamente após 1 minuto após a última resposta. Novas respostas não são mais permitidas." + other: "Este tópico foi fechado automaticamente após %{count} minutos depois da última resposta. Novas respostas não são mais permitidas." autoclosed_disabled: "Este tópico está aberto agora. Novas respostas estão permitidas." + autoclosed_disabled_lastpost: "Este tópico está aberto agora. Novas respostas estão permitidas." pinned_enabled: "Este tópico está agora afixado. Irá aparecer no topo das suas categorias" pinned_disabled: "Este tópico deixou de estár afixado. Não irá mais aparecer no topo das suas categorias." pinned_globally_enabled: "Esse tópico agora está fixado globalmente. Ele irá aparecer no topo dessa categoria e de todas as listagens de tópicos até que seja desfixado pela equipe para todo mundo, ou para usuários individuais por eles mesmo." @@ -736,7 +1045,7 @@ pt_BR: errors: "%{errors}" not_available: "Não disponível. Tente %{suggestion}?" something_already_taken: "Algo deu errado, talvez o nome de usuário ou o email já estejam registrados. Tente o link Esqueci minha Senha." - omniauth_error: "Desculpe, Houve um erro ao autorizar a sua conta %{strategy}. Talvez você não tenha aprovado a autorização?" + omniauth_error: "Desculpe, houve um erro ao autorizar a sua conta. Talvez você não aprovou a autorização?" omniauth_error_unknown: "Algo deu errado no processamento do seu login, por favor tente novamente." new_registrations_disabled: "Novas contas não são permitidas neste momento." password_too_long: "Senhas são limitadas a 200 caracteres." @@ -776,53 +1085,29 @@ pt_BR: Este convite é de um usuário de nossa confiança, assim você poderá participar da conversa imediatamente. invite_forum_mailer: subject_template: "%{invitee_name} convidou você para participar de %{site_domain_name}" + text_body_template: | + %{invitee_name} convidou você para participar + + > **%{site_title}** + > + > %{site_description} + + Se você estiver interessado, clique no link abaixo: + + %{invite_link} + + Este convite vem de um usuário confiável, então você não precisa se logar. invite_password_instructions: subject_template: "Defina uma senha para a sua %{site_name} conta" + text_body_template: | + Obrigado por aceitar seu convite para %{site_name} -- bem-vindo! + + Para se logar novamente, clique no link para escolher uma senha: + %{base_url}/users/password-reset/%{email_token} test_mailer: subject_template: "[%{site_name}] Teste de entrega de email" - text_body_template: | - Este é um e-mail de teste do - - [**%{base_url}**][0] - - A entregabilidade de email é complicado. Aqui estão algumas coisas importantes que você deve verificar em primeiro lugar: - - - Be *sure* to set the `notification email` from: address correctly in your site settings. **The domain specified in the "from" address of the emails you send is the domain your email will be validated against**. - - Tenha *certeza* que definiu o `notification email` para: endereço corretamente em suas configurações do site. **O domínio especificado no endereço "De" dos e-mails enviadas é o domínio seu e-mail será validado contra**. - - - Saiba como ver a fonte raw do e-mail no seu cliente de e-mail, para que você possa examinar os cabeçalhos de e-mail em busca de pistas importantes. No Gmail, é a opção "Mostrar original" no menu drop-down no canto superior direito de cada e-mail. - - - **IMPORTANTE:** Será que o seu ISP tem um registro DNS reverso inserido para associar os nomes de domínio e endereços IP que você enviou os e-mails a partir dele? [Teste o seu registro PTR Reverso][2] aqui. ISe o seu ISP não entrou com o registro adequado ponteiro do DNS reverso, é muito improvável que algum de seu e-mail será entregue. - - - O [registro SPF][8] do seu domínio esta correto? [Teste o seu registro SPF][1] aqui. Note-se que TXT é o tipo de registro oficial correto para SPF. - - - O [registro DKIM][3] do seu domínio esta correto? Isto irá melhorar significativamente a capacidade de entrega de e-mail. [Teste o seu registro DKIM][7] aqui. - - - Se você executar o seu próprio servidor de email, certifique-se de que os IPs de seu servidor de email [não esta em qualquer lista negra de e-mail][4]. Também verifique se ele está definitivamente esta enviando um nome de host totalmente qualificado que resolve em DNS em sua mensagem HELO. Se não, isso fará com que seu e-mail para ser rejeitada por muitos serviços de correio. - - (A maneira *fácil* é criar uma conta gratuita no [Mandrill][MD] ou [Mailgun][mg] ou [MailJet][mj], que tem planos generosos de discussão gráris que será bom para pequenas comunidades. Você ainda precisará configurar os registos SPF e DKIM no seu DNS, embora!) - - Esperamos que você tenha recebido este e-mail de teste de entregabilidade OK! - - Boa sorte, - - Seus amigos em [Discourse](http://www.discourse.org) - - [0]: %{base_url} - [1]: http://www.kitterman.com/spf/validate.html - [2]: http://mxtoolbox.com/ReverseLookup.aspx - [3]: http://www.dkim.org/ - [4]: http://whatismyipaddress.com/blacklist-check - [5]: %{base_url}/unsubscribe - [7]: http://dkimcore.org/tools/dkimrecordcheck.html - [8]: http://www.openspf.org/SPF_Record_Syntax - [md]: http://mandrill.com - [mg]: http://www.mailgun.com/ - [mj]: http://www.mailjet.com/pricing - - ---- - - Deve haver um rodapé cancelamento de inscrição em cada e-mail que você envia, então vamos zombar um. Este e-mail foi enviado por Nome da empresa, 55 Main Street, Anytown, EUA 12345 Se você gostaria de optar por sair de futuros e-mails,[clique aqui para cancelar a inscrição][5]. + text_body_template: "Este é um email de teste de\n\n[**%{base_url}**][0]\n\nEntregabilidade de email é complicada. Aqui estão algumas poucas coisas importantes que você deve verificar primeiro:\n\n- Esteja *certo* de definir o from: do `email de notificação` corretamente nas configurações do seu site. **O domínio especificado no endereço \"from\" dos emails que você envia é o domínio contra o qual o seu email será validado**.\n\n- Aprenda como ver o código cru de email no seu cliente de email, para que assim você possa examinar os cabeçalhos de email por pistas importantes. No Gmail, isso é a opção \"exibir original\" no menu drop-down no topo à direita de cada email.\n\n- **IMPORTANTE:** O seu provedor de acesso à internet possui um record de DNS reverso configurado para associar os nomes de domínio e endereços IP dos quais você envia email? [Teste seu record de PTR Reverso][2] aqui. Se o seu provedor de acesso à internet não configurar o record de DNS reverso apropriadamente, é muito pouco provável que qualquer do seu email será entregue.\n\n- O [record SPF][8] do seu domínio é correto? [Teste seu record SPF][1] aqui. Note que TXT \n é o tipo oficialmente correto de record para SPF.\n\n- O [record DKIM][3] do seu domínio é correto? Isso melhorará significativamente a entregabilidade de email. [Teste seu record DKIM][7] aqui.\n\n- Se você roda o seu próprio servidor de emails, verifique e certifique-se que os IPs do seu servidor de emails [não estão em quaisquer blacklists de emails][4]. Também verifique que ele esteja decisivamente enviando um fully-qualified hostname que se resolve em DNS em sua mensagem HELO. Caso contrário, isso fará com que seu email seja rejeitado por muitos serviços de email. \n\n(O modo *fácil* é criar uma conta grátis no [Mandrill][md] ou [Mailgun][mg] ou [Mailjet][mj], os quais têm generosos planos grátis de email e serão suficientes para pequenas comunidades. Você ainda precisará configurar os records SPF e DKIM no seu DNS, apesar disso!) \n\nNós esperamos que você tenha recebido este teste de entregabilidade de email OK!\n\nBoa sorte,\n\nSeus amigos do [Discourse](http://www.discourse.org)\n\n\ + [0]: %{base_url}\n[1]: http://www.kitterman.com/spf/validate.html\n[2]: http://mxtoolbox.com/ReverseLookup.aspx\n[3]: http://www.dkim.org/\n[4]: http://whatismyipaddress.com/blacklist-check\n[7]: http://dkimcore.org/tools/dkimrecordcheck.html\n[8]: http://www.openspf.org/SPF_Record_Syntax\n[md]: http://mandrill.com\n[mg]: http://www.mailgun.com/\n[mj]: http://www.mailjet.com/pricing\n" new_version_mailer: subject_template: "[%{site_name}] Nova versão do Discourse, atualização disponível" text_body_template: | @@ -863,132 +1148,119 @@ pt_BR: other: "Essas sinalizações foram submetidos mais de %{count} horas." please_review: "Por favor, revise eles." post_number: "mensagem" + how_to_disable: 'Você pode desabilitar ou modificar a frequência deste lembrete de email através da configuração "notificar sobre sinalizações após".' + subject_template: + one: "1 sinalização aguardando a ser controlada" + other: "%{count} sinalizações aguardando a ser controladas" flag_reasons: - off_topic: "Sua postagem foi sinalizada como ***off-topic**: a comunidade pensa que ela não se encaixa nesse tópico, atualmente definido pelo título e sua primeira postagem." - inappropriate: "A sua mensagem foi marcado como **inapropriado**: a comunidade pensa que é ofensivo, abusivo, ou uma violação das [diretrizes da comunidade](/guidelines)." - spam: "Sua postagem foi sinalizada como **spam*: a comunidade pensa que isso é uma propaganda não necessária ou relevante ao tópico, promocional por natureza." - notify_moderators: "Sua postagem foi sinalizada **para a atenção da moderação**: a comunidade pensa algo sobre a postagem que precisa a intervenção da moderação." + off_topic: "Sua postagem foi sinalizada como **off-topic**: a comunidade sente que não é uma boa adequação ao tópico, como atualmante definido de acordo com o título e o primeiro post." + inappropriate: "Sua postagem foi sinalizada como **não apropriada**: a comunidade sente que ela é ofensiva, abusiva, ou uma violação das [nossas orientações da comunidade](/guidelines)." + spam: "Sua postagem foi sinalizada como **spam**: a comunidade sente que ela é um anúncio, algo que é demasiadamente promocional em natureza ao invés de ser útil ou relevante ao tópico como esperado." + notify_moderators: "Sua postagem foi sinalizada como **para a atenção do moderador**: a comunidade sente que algo a respeito do post exige intervenção manual por um membro da staff." flags_dispositions: agreed: "Obrigado por nos avisar. Concordamos que há um problema e nós estamos olhando para ele." agreed_and_deleted: "Obrigado por nos avisar. Concordamos que há um problema e nós removemos o post." disagreed: "Obrigado por nos avisar. Nós estamos olhando nele." deferred: "Obrigado por nos avisar. Nós estamos olhando nele." deferred_and_deleted: "Obrigado por nos avisar. Nós removemos o post." + temporarily_closed_due_to_flags: "Este tópico foi temporariamente fechado devido à um grande número de sinalizações da comunidade" system_messages: post_hidden: subject_template: "%{site_name} Aviso: Postagem escondida devido a Sinalização pela Comunidade" - usage_tips: text_body_template: | - Esta mensagem privada tem algumas dicas para te ajudar a começar. + Olá, - ## Continue rodando + Esta é uma mensagem automática do % {site_name} para informá-lo que o seu post foi ocultado. - Não existem botões de próxima página ou numerações de página – para ler mais, **simplesmente continue rodando para baixo!** + %{base_url}%{url} - Conforme novas mensagens chegam, elas irão aparecer automaticamente. + %{flag_reason} - ## Onde estou? + Vários membros da comunidade assinalaram este post antes de ser escondido, então por favor, pense em como você pode rever seu post para refletir seu feedback. ** Você pode editar seu post depois de %{edit_delay} minutos, e será automaticamente desocultado. ** Isto irá aumentar o seu nível de confiança. - - Para buscar, sua página de usuário, ou o menu, use os **botões do ícone no canto direito superior**. + No entanto, se o post é escondido pela comunidade uma segunda vez, ele permanecerá escondido até ser manipuladas pela staff - e pode haver outras medidas, incluindo a possibilidade de suspensão da sua conta. - - Qualquer título de tópico irá te levar para a próxima mensagem não lida. Utilize o tempo de última atividade e contagem de mensagens para entrar no começo ou no final. - - - Enquanto lê um tópico, vá para o topo ↑ selecionando o título do tópico. Selecione a barra de progresso verde no canto direito inferior para todos controles de navegação, ou utilize as teclas home e end. - - - - ## Como respondo? - - - Para responder ao tópico geral, utilize o botão de Responder bem no final da página. - - - Para responder uma mensagem em específico, utilize o botão de Responder na respectiva mensagem. - - - Se quiser levar a conversa para outro sentido, mas manter elas relacionadas, utilize Responder como um Tópico linkado à direita da mensagem. - - Para citar alguém na sua resposta, selecione o texto que gostaria de citar, daí aperte qualquer botão de Responder. - - - - Para alertar alguém na sua resposta, mencione o nome deles. Tecle `@` e um auto copmpletador surgirá. - - - - Para [Emojis padrões](http://www.emoji.codes/), simplesmente comece digitando `:` ou os tradicionais smileys `:)` :smile: - - ## O que mais posso fazer? - - Em baixo de cada mensagem existem os botões de ações. - - - - Para deixar que saibam que você gostou a mensagem deles, utilize o botão **curtir**. Se você ver algum problema com uma mensagem, avise a pessoa pessoalmente, ou deixe nossa equipe saber a respeito com o botão **sinalizar**. - - Você também pode **compartilhar** um link para uma mensagem, ou **favoritar** para referência posterior na sua página de usuário. - - ## Quem está falando comigo? - - Quando alguém responde ou cita as suas mensagens, ou menciona o seu `@nomedeusuário`, um número imediatamente aparecerá no topo superior da página. Utilize-o para acessar suas **notificações**. - - - - Não se preocupe se não vir uma resposta – você receberá um email sobre respostas direcionadas (e mensagens privadas) caso você não esteja online quando elas chegarem. - - ## Quando as conversas são consideradas novas? - - Por padrão todas conversas com menos de dois dias são consideradas novas, e qualquer conversa da qual você participou (respondeu, criou ou leu por um período extendido) será automaticamente monitorada. - - Você verá um indicador azul, de "novo" ou números, próximos aos tópicos: - - - - Você pode mudar o estado de notificação individual de um tópico via o controle no final dele (isso também pode ser configurado por categorias). Para mudar como você monitora tópicos, ou a definição de "novo", veja [suas preferências de usuário](/my/preferences). - - ## Por que não posso fazer algumas coisas? - - Usuários novos possuem limitações por segurança. Conforme você participa, ganhará a confiança da comunidade, se tornará um cidadão, e todas essas limitações serão automaticamente removidas. Em [níveis de confiança](https://meta.discourse.org/t/what-do-user-trust-levels-do/4924) altos suficientes, você ganhará até mesmo novas abilidades para nos ajudar a cuidar da nossa comunidade juntos. + Para informações adicionais, por favor consulte a nossas [diretrizes da comunidade] (%{base_url}/guidelines). welcome_user: subject_template: "Bem-vindo ao %{site_name}!" - text_body_template: | - Obrigado por participar de %{site_name}, e bem-vindo! - - %{new_user_tips} - - Acreditamos num [comportamento civilizado na comunidade](%{base_url}/faq) sempre. - - Diverta-se durante sua estadia! welcome_invite: subject_template: "Bem-vindo ao %{site_name}!" + backup_succeeded: + subject_template: "Backup completado com sucesso" + text_body_template: "O backup foi bem sucedido.\nVisite a [administração > seção backup](/admin/backups) para fazer download do seu novo backup." + backup_failed: + subject_template: "O backup falhou" text_body_template: | - Obrigado por aceitar o seu convite para %{site_name}, e bem-vindo! + O backup falhou. - Geramos automaticamente um nome de usuário para você: **%{username}**, mas poderá alterá-lo quantas vezes quiser visitando [o seu perfil de usuário][prefs]. + Aqui está o log: - Para entrar outra vez, escolha uma das opções: + ``` + %{logs} + ``` + restore_succeeded: + subject_template: "Restauração terminada com sucesso" + text_body_template: "A restauração foi bem sucedida." + restore_failed: + subject_template: "A restauração falhou" + text_body_template: | + A restauração falhou. - 1. Utilize uma das credenciais disponíveis -- ela deve ser do **mesmo endereço de email** pelo qual você recebeu o convite original. Caso contrário não poderemos saber se é você! + Aqui está o log: - 2. Crie uma senha única para %{site_name} no [seu perfil de usuário][prefs]. + ``` + %{logs} + ``` + bulk_invite_succeeded: + subject_template: "Seus convites foram enviados com sucesso." + text_body_template: "Seus convites foram enviados com sucesso: %{sent}." + bulk_invite_failed: + subject_template: "Seus convites não forma enviados, contate os moderadores." + text_body_template: | + Seu arquivo de convite de usuários em massa foi processado, %{sent} convites enviados com %{failed} erro(s). - %{new_user_tips} + Aqui está o log: - Acreditamos num [comportamento civilizado na comunidade](%{base_url}/faq) em todos os momentos. + ``` + %{logs} + ``` + csv_export_succeeded: + subject_template: "Exportação de dados completada" + text_body_template: | + Sua exportação de dados foi bem-sucedida! :dvd: - Divirta-se durante sua estadia! + %{file_name} (%{file_size}) - [prefs]: %{user_preferences_url} + O link acima será válido por 48 horas. + csv_export_failed: + subject_template: "A exportação de dados falhou" + text_body_template: "Nos desculpamos, mas sua exportação de dados falhou. Por favor verifique os logs ou entre em contato com um membro da staff." email_reject_trust_level: + subject_template: "Mensagem rejeitada" text_body_template: | Lamentamos, mas o seu e-mail para%{destination} (intutulado %{former_title}) não funcionou. Sua conta não tem o nível de confiança necessário para criar novos tópicos para este endereço de e-mail. Se você acredita que isso é um erro, entre em contato com um membro da equipe. email_reject_no_account: subject_template: "Problema com Email -- Conta Desconhecida" + text_body_template: | + Lamentamos, mas o seu e-mail para%{destination} (intutulado %{former_title}) não funcionou. Não há nenhum relato conhecido com este endereço de e-mail. Tente enviar a partir de um endereço de e-mail diferente, ou entre em contato com um membro da equipe. + email_reject_empty: + subject_template: "Problemas no Email - Mensagem em Branco" + text_body_template: | + Desculpe-nos, mas seu e-mail para %{destination} (entitulado %{former_title}) não funcionou. Nós não conseguimos encontrar nenhum conteúdo no e-mail. Tenha certeza que você escreveu alguma coisa no topo do e-mail - nós não conseguimos analisar respostas em linha. Se você está recebendo isto, mas incluiu conteúdo, tente novamente com conteúdo em HTML no seu e-mail (não somente texto). + email_reject_parsing: + subject_template: "Problemas no Email - Conteúdo desconhecido." + text_body_template: | + Desculpe-nos, mas seu e-mail para %{destination} (entitulado %{former_title}) não funcionou. Não conseguimos encontrar onde sua resposta estava no e-mail. **Tenha certeza de escrever a resposta no topo do e-mail** -- tudo após o início da mensagem respondida é descartado. email_reject_post_error: + subject_template: "Problemas no email - \"Posting Error\" " text_body_template: | Desculpe-nos, mas seu e-mail para %{destination} (entitulado %{former_title}) não funcionou. Algumas possíveis causas são: formatação complexa, mensagem muito grande, mensagem muito pequena. Por favor tente novamente, ou poste através do site se persistir. email_reject_post_error_specified: + subject_template: "Problemas no email - \"Posting Error\" " text_body_template: | Desculpe-nos, mas seu e-mail para %{destination} (entitulado %{former_title}) não funcionou. @@ -997,7 +1269,30 @@ pt_BR: %{post_error} Se você puder corrigir, por favor tente novamente. + email_reject_reply_key: + subject_template: "Problemas no email - Sem endereço para retorno - \"Unknown Reply Key\"" + text_body_template: | + Pedimos desculpas, mas seu email para %{destination} (titled %{former_title}) não foi enviado. + + O endereço de email do destinatário é desconhecido ou inválido, Contate um dos moderadores para relatar esse problema + email_reject_destination: + subject_template: "Probelmas no Email - Email destinatário desconhecido" + text_body_template: | + Desculpe-nos, mas seu e-mail para %{destination} (entitulado %{former_title}) não funcionou. Nenhum dos endereços de destinatário são reconhecidos pelo fórum. Por favor tenha certeza que o fórum está na linha To: (não CC ou BCC), e que você está enviando para o endereço de e-mail fornecido pelos administradores. + email_reject_topic_not_found: + subject_template: "Problema com o email -- Tópico Não Encontrado" + text_body_template: | + Nós nos desculpamos, mas sua mensagem de email para %{destination} (entitulada %{former_title}) não funcionou. + + O tópico não foi encontrado, ele pode ter sido excluído. Se você acredito que isso é um erro, entre em contato com um membro da staff. + email_reject_topic_closed: + subject_template: "Problema com email -- Tópico Fechado" + text_body_template: | + Desculpe-nos, mas a sua mensagem de email para %{destination} (entitulada %{former_title}) não funcionou. + + O tópico está fechado. Se você acredita que isso é um erro, entre em contato com um membro da staff. email_error_notification: + subject_template: "Problema com o email -- Erro de authenticação POP" text_body_template: | Erro de autenticação ao baixar e-mails através do servidor POP. @@ -1115,13 +1410,17 @@ pt_BR: --- %{respond_instructions} digest: + why: "Um breve resumo de %{site_link} desde que viu pela última vez em %{last_seen_at}." subject_template: "[%{site_name}] Atividade do Fórum para %{date}" new_activity: "Nova atividade nos seus tópicos e postagens:" top_topics: "Tópicos populares" other_new_topics: "Tópicos populares" + unsubscribe: "Este resumo é enviado por %{site_link} quando nós não o temos visto em um tempo. Para cancelar a assinatura %{unsubscribe_link}." click_here: "clique aqui" from: "resumo de %{site_name}" read_more: "Leia Mais" + more_topics: "Há %{new_topics_since_seen} outros novos tópicos." + more_topics_category: "Mais tópicos novos:" posts: one: "1 mensagem" other: "%{count} mensagens" @@ -1139,6 +1438,11 @@ pt_BR: text_body_template: "Alguém solicitou para adicionar uma senha para a sua conta em [%{site_name}] (% {base_url}). Alternativamente, você pode fazer login usando qualquer serviço online suportado (Google, Facebook, etc) que está associado a este endereço de e-mail validado.\n \nSe você não fez esse pedido, você pode ignorar este e-mail. \n \nClique no link abaixo para escolher uma senha: \n%{base_url}/users/password-reset/%{email_token}\n" account_created: subject_template: "[%{site_name}] Sua nova Conta" + text_body_template: | + Uma nova conta foi criada para você em %{site_name} + + Clique no link a seguir para escolher uma senha para a sua nova conta: + %{base_url}/users/password-reset/%{email_token} authorize_email: subject_template: "[%{site_name}] Confirma o seu novo endereço de email" text_body_template: | @@ -1147,21 +1451,6 @@ pt_BR: %{base_url}/users/authorize-email/%{email_token} signup_after_approval: subject_template: "Você foi aprovado no %{site_name}!" - text_body_template: | - Bem vindo ao %{site_name}! - - Um membro da equipe aprovou a sua conta em %{site_name}. - - Clique no link abaixo para confirmar e ativar sua nova conta: - %{base_url}/users/activate-account/%{email_token} - - Se o link acima não é clicável, tente copiar e colá-lo na barra de endereços do seu navegador. - - %{new_user_tips} - - Acreditamos no [comportamento da comunidade civilizada](%{base_url}/guidelines) em todos os momentos. - - Aproveite a sua estadia! signup: subject_template: "[%{site_name}] Ativar a sua nova conta" text_body_template: | @@ -1192,9 +1481,9 @@ pt_BR: pasted_image_filename: "Imagem colada" store_failure: "Falha ao arquivar o upload #%{upload_id} do usuário #%{user_id}." attachments: - too_large: "Desculpe, o arquivo que você está tentando enviar é muito grande (tamanho máximo é %{max_size_kb}%kb)." + too_large: "Desculpe, o arquivo que você está tentando enviar é muito grande (o tamanho máximo é %{max_size_kb}KB)." images: - too_large: "Desculpe, a imagem que você está tentando enviar é muito grande (tamanho máximo é %{max_size_kb}%kb), por favor redimensione-a e tente novamente." + too_large: "Desculpe, a imagem que você está tentando enviar é muito grande (o tamanho máximo é %{max_size_kb}KB), por favor a redimensione e tente novamente." fetch_failure: "Desculpe, houve um erro ao transferir a imagem." unknown_image_type: "Desculpe, mas o arquivo que você tentou enviar não parece ser uma imagem." size_not_found: "Desculpe, mas não conseguimos determinar o tamanho da imagem. É possível que seu arquivo de imagem esteja corrompido?" @@ -1220,11 +1509,15 @@ pt_BR: guidelines: "Diretrizes" privacy: "Privacidade" edit_this_page: "Editar esta página" + csv_export: + boolean_yes: "Sim" + boolean_no: "Não" + static_topic_first_reply: "Altere o primeiro post desse tópico para mudar seu conteúdo na página de %{page_name} \n" guidelines_topic: title: "Perguntas frequentes/Diretrizes" - body: "\n\n## [Este é um lugar civilizado para discussão pública](#civilizado)\n\nPor favor, trate este fórum de discussão com o mesmo respeito que seria um parque público. Nós, também, somos um recurso da comunidade compartilhada — um lugar para compartilhar habilidades, conhecimentos e interesses através de conversa.\n\nEstas não são regras rígidas e rápidas, apenas auxilia o julgamento humano de nossa comunidade. Use estas diretrizes para manter este, um lugar bem iluminado limpo para o discurso público civilizado.\n\n\n\n## [Melhorar a Discussão](#melhorar)\n\nAjude-nos a fazer deste um ótimo lugar para discussão trabalhando sempre para melhorar a discussão de alguma forma, ainda que pequena. Se você não tiver certeza de que seu post acrescenta à conversa, pense sobre o que você quer dizer e tente novamente mais tarde. \n\nOs tópicos discutidos aqui são importantes para nós, e nós queremos que você aja como se eles fossem são importantes para você, também. Seja respeitoso com os temas e as pessoas que estão discutindo-los, mesmo se você não concordar com algumas das coisas que está sendo dito. \n\nUma forma de melhorar a discussão é descobrindo as que já estão acontecendo. Por favor, passe algum tempo visitando os tópicos aqui antes de responder ou iniciar o seu próprio, você terá uma melhor chance de encontrar outras pessoas que compartilham os seus interesses.\n\n\n\n## [Seja agradável, mesmo quando você discorda](#agradavel)\n\nVocê pode querer responder a algo por discordar com ele. Isso é bom. Mas, lembre-se de _criticar as idéias, não pessoas_. Por favor, evite: \n\n* Xingamentos. \n* Ataques ad hominem. \n* Respondendo a um tom de mensagem, em vez de seu conteúdo real. \n* Instintiva contradição. \n\nEm vez disso, forneça fundamentados contra-argumentos que melhoram a conversa.\n\n\n\n## [Suas contagens de Participação](#participacao)\n\nAs conversas que temos aqui define o tom para todos. Ajude-nos a influenciar o futuro da nossa comunidade, optando por se envolver em discussões que fazem deste fórum\ - \ um lugar interessante para ser — e evitando aqueles que não o fazem.\n\nDiscourse fornece ferramentas que permitem a comunidade a identificar coletivamente as melhores (e piores) contribuições: favoritos, marcadores, gostos, sinalizações, respostas, edições, e assim por diante. Use essas ferramentas para melhorar a sua própria experiência, e todos os outros também.\n\nVamos tentar deixar o nosso parque melhor do que o encontramos.\n\n\n\n## [Se você vê um problema, sinalize ele](#sinalize-problemas)\n\nModeradores têm autoridade especial; eles são responsáveis por este fórum. Mas assim como você. Com a sua ajuda, os moderadores podem ser facilitadores da comunidade, e não apenas zeladores ou da polícia.\n\nQuando você vê um mau comportamento, não responda. Ele estimula o mau comportamento, reconhecendo-o, consome sua energia, e disperdiça o tempo de todos. _Apenas sinalize ele_. Se acumularem sinalizadores suficiente, serão tomadas medidas, de forma automática ou por intervenção do moderador.\n\nA fim de manter a nossa comunidade, os moderadores se reservam o direito de remover qualquer conteúdo e qualquer conta de usuário por qualquer motivo, a qualquer momento. Moderadores não pré-visualizam novas mensagens de qualquer forma; moderadores e operadores de sites não assumem nenhuma responsabilidade por qualquer conteúdo postado pela comunidade.\n\n\n\n## [Seja sempre Civilizado](#seja-civilizad)\n\nNada sabota uma conversa saudável como grosseria: \n\n* Seja civilizado. Não publique qualquer coisa que uma pessoa razoável consideraria ofensivo, abusivo, ou o discurso de ódio. \n* Mantenha-o limpo. Não publique nada obsceno ou sexualmente explícito. \n* Respeite o outro. Não assedie ou xingue ninguém, personifique pessoas, ou exponha a sua informação privada.\n* Respeite nosso fórum. Não publique spam ou de outro modo vandalizar o fórum. \n\nEstes não são termos concretos com definições precisas — evitar até mesmo a _aparência_ de qualquer uma dessas coisas. Se você não tiver certeza, pergunte-se como você se sentiria se seu post foi destaque\ - \ na primeira página do New York Times. \n\nEste é um fórum público, e motores de busca indexam essas discussões. Mantenha a língua, links, e imagens seguro para a família e amigos.\n\n\n\n## [Mantenha-o arrumado](#mantenha-arrumado)\n\nFaça um esforço para colocar as coisas no lugar certo, para que possamos passar mais tempo discutindo e menos fazendo limpeza. Assim: \n\n* Não comece um tópico na categoria errada. \n* Não poste a mesma coisa em vários tópicos. \n* Não poste respostas que não tenham a ver com o conteúdo. \n* Não desvie um tópico, alterando-o no meio do caminho. \n* Não assine suas mensagens — cada post tem suas informações de perfil ligado a ele. \n\nAo invés de postar \"+1\" ou \"Concordo\", use o botão Like. Ao invés de tomar um tópico existente em uma direção radicalmente diferente, use Resposta como um Novo Tópico.\n\n\n\n## [Poste apenas as tuas coisas](#roubar)\n\nVocê não pode postar qualquer coisa digital que pertence a outra pessoa sem permissão. Você não pode postar descrições, links ou métodos para roubar a propriedade intelectual de alguém (software, vídeo, áudio, imagens), ou para quebrar qualquer outra lei.\n\n\n\n## [Termos de Serviço](#tos)\n\nSim, juridiquês é chato, mas temos de nos proteger – e por extensão, você e seus dados – contra pessoas hostis. Nós temos um [Termos de Serviço] (/tos) descrevendo o seu (e nosso) comportamento e direitos relacionados a conteúdo, privacidade e leis. Para utilizar este serviço, você deve concordar em respeitar os nosso [TOS] (/tos).\n" + body: "\n\n## [Este é um Lugar Civilizado para Discussão Pública](#civilized)\n\nPor gentileza trate este fórum de discussão com o mesmo respeito que você teria em um parque público. Nós, também, somos uma comunidade de recursos compartilhados — um local para compartilhar habilidades, conhecimento e interesses através da discussão em andamento.\n\nEstas não são regras duras e firmes, meramente suportes ao julgamento humano de nossa comunidade. Use estas diretrizes para manter um lugar claro, bem-iluminado de conversa pública civilizada.\n\n\n\n## [Melhore a Discussão](#improve)\n\nAjude-nos a fazer deste um grande lugar de discussão ao trabalhar sempre para melhorar a discussão de alguma forma, mesmo que pequena. Se você estiver incerto de que o seu post acrescenta à conversa, pense melhor no que você quer dizer e tente novamente depois.\n\nOs tópicos discutidos aqui importam para nós, e nós queremos que você aja como se eles importassem para você, também. Seja respeitoso com os tópicos e as pessoas que os discutem, ainda que você discorde com algo do que está sendo dito.\n\nUma forma de melhorar a discussão é por descobrir algumas que já estão acontecendo. Por favor passe algum tempo navegando nos tópicos aque antes de responder ou iniciar o seu próprio, e você terá uma chance de conhecer outros que compartilham dos seus interesses.\n\n\n\n## [Seja Agradável, Mesmo Quando Você Discorda](#agreeable)\n\nVocê pode querer responder à algo discordando. Tudo bem. Mas, lembre-se de _criticar idéias, não pessoas_. Por gentileza evite:\n\n* Xingamentos\n* Ataques ad hominem\n* Responder ao tom de um post ao invés do seu conteúdo efetivo.\n* Contradição impensada.\n\nAo invés disso, ministre contra-argumentos razoáveis que aprimorem a conversa.\n\n\n\n## [Sua Participação Conta](#participate)\n\nAs conversas que nós temos aqui colocam um ambiente para todos. Ajude-nos a influenciar o futuro desta comunidade escolhendo empenhar-se em discussões que façam deste fórum um lugar interessante para se estar — e evitando\ + \ aquelas que não façam.\n\nO Discourse fornece ferramentas que permitem a comunidade de identificar coletivamente as melhores (e piores) contribuições: favoritos, likes, sinalizações, respostas, edits, e assim em diante. Utilize estas ferramentas para aprimorar sua própria experiência, e também a de todos os outros.\n\nVamos tentar deixar nosso parque melhor do que nós o encontramos.\n\n\n\n## [Se Você Ver um Problema, Sinalize-o](#flag-problems)\n\nModeradores têm autoridade especial; eles são responsáveis por este fórum. Mas você também. Com a sua ajuda, moderadores podem ser facilitadores da comunidade, não zeladores ou polícia.\n\nQuando você vê um comportamento ruim, não responda. Isso encoraja o comportamento ruim por reconhecê-lo, consume a sua energia, e desperdiça o tempo de todos. _Apenas sinalize-o_. Se as sinalizações aumentam o suficiente, ação será tomada, seja automaticamente ou através de intervenção da moderação.\n\nDe modo a manter nossa comunidade, moderadores se reservam o direito de remover qualquer conteúdo e qualquer conta de usuário por qualquer motivo e quando quer que seja. Moderadores não prevêem novos posts de qualquer modo; os moderadores e operadores do site não têm qualquer responsabilidade por qualquer conteúdo postado pela comunidade.\n\n\n\n## [Sempre Seja Civilizado](#be-civil)\n\nNada sabota uma conversa saudável como a grosseria:\n\n* Seja civilizado. Não poste qualquer coisa que uma pessoa razoável consideraria ofensivo, abusivo, ou discurso de ódio.\n* Deixe-o limpo. Não poste qualquer coisa obscena ou sexualmente explícita.\n* Respeite aos demais. Não perturbe ou aflija, se passe por outras pessoas, ou exponha suas informações particulares.\n* Respeite nosso fórum. Não poste spam ou vandalize o fórum de algum outro modo.\n\nEstes não são termos concretos com definições precisas — evite mesmo a _aparência_ de qualquer destas coisas. Se você estiver incerto, pergunte-se como você se sentiria se sua postagem fosse veiculada na primeira página do New York Times.\n\nEste é um fórum público, e motores de busca indexam\ + \ estas discussões. Mantenha a linguagem, links, e imagens seguros para família e amigos.\n\n\n\n## [Mantenha-o Arrumado](#keep-tidy)\n\nFaça um esforço para colocar coisas no lugar certo, para que assim nós possamos passar mais tempo discutindo e menos limpando. Assim:\n\n* Não inicie um tópico na categoria incorreta.\n* Não faça postagens cruzadas da mesma coisa em múltiplos tópicos.\n* Não poste respostas sem conteúdo.\n* Não desvie um tópico mudando-no em meio à correnteza.\n* Não assine seus posts — cada postagem tem sua informação de perfil anexada à ela. \n\nAo invés de postar \"+1\" ou \"Concordo\", use o botão Like. Ao invés de levar um tópico existente numa direção radicalmente diferente, utilize Responder como um Tópico Linkado.\n\n\n\n## [Poste Apenas Suas Próprias Coisas](#stealing)\n\nVocê deve não postar o que quer que seja digital que pertence a outro sem permissão. Você deve não postar descrições de, links para, ou métodos para roubar a propriedade intelectual de outro (software, vídeo, áudio, imagens), ou para quebrar qualquer outra lei.\n\n\n\n## [Powered by Você](#power)\n\nEste site é operado pela suar [amigável staff local](/about) e *você*, a comunidade. Se você tiver quaisquer outras questões sobre como as coisas devem funcionar aqui, abra um novo tópico na [categoria meta](/c/meta) e vamos discutir! Se houver um assunto crítico ou urgente que não puder ser resolvido por um tópico meta ou sinalização, entre em contato conosco através da [página da staff](/about).\n\n\n\n## [Terms of Service](#tos)\n\nSim, juridiquês é chata, mas nós devemos nos proteger – e por extensão, você e seus dados – contra pessoas não-amigáveis. Nós temos um [Termos of Serviço](/tos) descrevendo a sua (e a nossa) conduta e direitos relacionados a conteúdo, privacidade e leis. Para usar este serviço, você deve concordar em permanecer por nossos [TOS](/tos).\n" tos_topic: title: "Termos de Serviço" body: "Os seguintes termos e condições regem todo o uso do site %{company_domain} e todo o conteúdo, serviços e produtos disponíveis no ou através do site, incluindo, mas não limitado a, o Software do Fórum do %{company_domain}, o Fóruns de Suporte do %{company_domain} e o serviço de hospedagem (\"hosting\") do %{company_domain}, (em conjunto, o site). O site é propriedade e operada por %{company_full_name} (\"%{company_name}\"). O Site é oferecido sujeito à sua aceitação, sem modificação de todos os termos e condições contidos neste documento e todas as outras regras operacionais, políticas (incluindo, sem limitação, a [Política de Privacidade](/privacy) e [Regras da Comunidade ](/faq)) e procedimentos do %{company_domain} que possam ser publicadas de tempos em tempos neste site por %{company_name} (coletivamente, o \"Contrato\").\n\nPor favor, leia este Contrato cuidadosamente antes de acessar ou utilizar o site. Ao acessar ou utilizar qualquer parte do web site, você concorda em ficar vinculado aos termos e condições deste acordo. Se você não concordar com todos os termos e condições deste contrato, então você não pode acessar o site ou utilizar qualquer serviço. Se estes termos e condições são consideradas uma oferta por %{company_name}, a aceitação é expressamente limitada a estes termos. O site está disponível apenas para indivíduos que são pelo menos 13 anos de idade.\n\n\n\n## [1. Sua conta %{company_domain}](#1)\n\nSe você criar uma conta no site, você é responsável por manter a segurança da sua conta e você é totalmente responsável por todas as atividades que ocorrem sob a conta. Você deve notificar imediatamente %{company_name} de qualquer uso não autorizado de sua conta ou qualquer outra violação de segurança. %{company_name} não será responsável por quaisquer atos ou omissões por você, incluindo quaisquer danos de qualquer tipo incorridos como resultado de tais atos ou omissões.\n\n\n\n## [2. Responsabilidade de Contribuintes](#2)\n\nSe você postar o material no site, postar links no site, ou de outra forma (ou permitir que terceiros o fazem) material disponível por meio do\ @@ -1241,3 +1534,26 @@ pt_BR: body: "\n\n## [Quais as informações que recolhemos?](#coleta)\n\nColetamos informações de você quando você se cadastra em nosso site e coletamos dados quando você participar do fórum, lendo, escrevendo, e avaliar o conteúdo compartilhado aqui. \n\nAo se cadastrar em nosso site, você pode ser solicitado a digitar seu nome e e-mail. Você pode, entretanto, visite nosso site, sem registrar. O seu endereço de e-mail será verificado por um e-mail contendo um link único. Se o link é visitado, sabemos que você controla o endereço de e-mail. \n\nQuando registrado e fazendo postagens, registramos o endereço IP que originou o post. Também podemos manter os logs do servidor, que incluem o endereço IP de todos os pedidos para o nosso servidor.\n\n\n\n## [Em que usamos as suas informações?](#uso)\n\nQualquer uma das informações que coletamos de você pode ser usado em uma das seguintes formas: \n\n* Para personalizar a sua experiência — sua informação nos ajuda a responder melhor às suas necessidades individuais. \n* Para melhorar o nosso site — nós nos esforçamos continuamente para melhorar as nossas ofertas de site com base nas informações e feedback que recebemos de você. \n* Para melhorar o serviço ao cliente — sua informação nos ajuda a responder mais eficazmente às suas solicitações de serviço ao cliente e as necessidades de apoio. \n* Para enviar periodicamente e-mails — O endereço de e-mail que você fornece pode ser usado para enviar-lhe informações, as notificações que você quando há mudanças tópicos que solicitou ou em resposta a seu nome de usuário, responder a perguntas, e/ou outras solicitações ou perguntas.\n\n\n\n## [Como vamos proteger a sua informação?](#protecao)\n\nImplementamos uma série de medidas de segurança para manter a segurança de suas informações pessoais quando você entra, envia ou acessa suas informações pessoais.\n\n\n\n## [Qual é a sua política de retenção de dados?](#retencao-dados)\n\nNós vamos fazer um esforço de boa fé para: \n\n* Manter registros do servidor contendo o endereço\ \ IP de todos os pedidos para este servidor não mais que 90 dias. \n* Manter os endereços de IP associados a usuários registrados e suas mensagens não mais que 5 anos.\n\n\n\n## [Nós usamos cookies?](#cookies)\n\nSim. Cookies são pequenos arquivos que um site ou seu fornecedor de serviços transfere para o disco rígido do seu computador através de seu navegador Web (se você permitir). Estes cookies permitem que o site reconhecer o seu browser e, se você tiver uma conta registrada, associá-lo com a sua conta registrada. \n\nNós usamos cookies para compreender e guardar as suas preferências para futuras visitas e compilar dados agregados sobre o tráfego ea interação no site, para que possamos oferecer um site melhor e ferramentas no futuro. Poderemos contratar prestadores de serviços de terceiros para nos ajudar a compreender melhor os nossos visitantes do site. Esses prestadores de serviços não estão autorizados a utilizar as informações coletadas em nosso nome, exceto para nos ajudar a conduzir e melhorar o nosso negócio.\n\n\n\n## [Podemos divulgar qualquer informação a terceiros?](#divulgar)\n\nNós não vendemos, trocamos, ou de outra forma transferrimos a terceiros as suas informações pessoalmente identificáveis. Isso não inclui terceiros de confiança que nos auxiliam no funcionamento do nosso site, conduzindo nosso negócio, ou serviço que a você, desde que as partes concordam em manter esta informação confidencial. Podemos também divulgar as suas informações quando acreditamos que é apropriado para cumprir a lei, fazer cumprir as nossas políticas do site, ou proteger nosso ou de outros direitos, propriedade ou segurança. No entanto, informações turísticas não pessoalmente identificáveis ​​podem ser fornecidas a terceiros para marketing, publicidade, ou outros usos.\n\n\n\n## [Links de terceiros](#terceiros)\n\nOcasionalmente, a nosso critério, podemos incluir ou oferecer produtos ou serviços de terceiros no nosso site. Estes sites de terceiros têm políticas de privacidade separadas e independentes. Portanto, não temos responsabilidade ou obrigações\ \ pelo conteúdo e atividades desses sites. No entanto, buscamos proteger a integridade do nosso site e agradecemos qualquer feedback sobre esses sites.\n\n\n\n## [Privacidade Online Infantil Protection Act Compliance](#coppa)\n\nNosso site, produtos e serviços são todos dirigidos a pessoas que tem pelo menos 13 anos de idade ou mais. Se o servidor for nos EUA, e você está sob a idade de 13, de acordo com as exigências da COPPA ([Lei de Proteção de Privacidade Online de Crianças] (http://en.wikipedia.org/wiki/Children)), não use este site.\n\n\n\n## [Somente Política de Privacidade Online](#online)\n\nEsta política de privacidade online se aplica somente às informações recolhidas pelo nosso site e não a informações coletadas offline.\n\n\n\n## [Seu Consentimento](#consentimento)\n\nAo utilizar nosso site, você concorda com a nossa política de privacidade web site.\n\n\n\n## [Alterações à nossa Política de Privacidade](#mudancas)\n\nSe nós decidirmos mudar nossa política de privacidade, colocaremos essas alterações nesta página. \n\nEste documento é CC-BY-SA. Ele foi atualizado pela última 31 de maio de 2013.\n" + static: + search_help: | +

Dicas

+

+

    +
  • Resultados de título são priorizados, então quando em dúvida, pesquise pelos títulos
  • +
  • Palavras únicas, incomuns sempre produzirão os melhores resultados
  • +
  • Sempre que possível, pesquise com escopo em uma categoria, usuário ou tópico em particular.
  • +
+

+

Opções

+

+ + + + + + +
order:viewsorder:latest
status:openstatus:closedstatus:archivedstatus:norepliesstatus:singleuser
category:foouser:foo
in:likesin:postedin:watchingin:trackingin:private
in:bookmarks
+

+

+ arco-íris category:parques status:open order:latest irá pesquisar por tópicos contendo a palavra "arco-íris" na categoria "parques" que não estiverem fechados ou arquivados, ordenados pela data do último post. +

diff --git a/config/locales/server.ro.yml b/config/locales/server.ro.yml index 6226fd3aec..c203f3a87d 100644 --- a/config/locales/server.ro.yml +++ b/config/locales/server.ro.yml @@ -725,8 +725,6 @@ ro: post_menu_hidden_items: "Meniul de elemente de ascuns din oficiu în meniul de postări numai dacă elipsa este activată prin click." share_links: "Determină ce elemente apar în dialogul distribuit şi în ce ordine." track_external_right_clicks: "Urmăreşte adresele externe ce sunt activate prin click-dreapta (ex: deschide în tab nou) dezactivate din oficiu fiindcă rescrie URL-uri" - topics_per_page: "Câte discuţii sunt încărcate din oficiu în lista de discuţii, şi când se face scroll pentru a încărca mai multe discuţii" - posts_per_page: "Câte postări sunt încărcate din oficiu într-o discuţie, şi când se face scroll pentru a încărca mai multe postări" site_contact_username: "Toate mesajele priovate automate trimise de forum vor fi de la acest utilizator; dacă este lăsat necompletat va fi folosit contul de sistem oficial." send_welcome_message: "Trimite tuturor utilizatorilor noi un mesaj privat de bun-venit şi un scurt ghid." suppress_reply_directly_below: "Nu arăta numărul expadabil de răspunsuri într-o postare când există doar un sigur răspuns sub acesta." diff --git a/config/locales/server.ru.yml b/config/locales/server.ru.yml index 7db295eee0..eb57bee74e 100644 --- a/config/locales/server.ru.yml +++ b/config/locales/server.ru.yml @@ -6,15 +6,20 @@ # https://www.transifex.com/projects/p/discourse-org/ ru: + stringex: + characters: + number: "-" + i18n: + transliterate: + rule: + ț: "t" + Ț: "t" + ș: "s" + Ș: "s" dates: short_date_no_year: "D MMM" short_date: "D MMM YYYY" long_date: "D MMMM YYYY, HH:mm" - time: - formats: - short: "%d.%m.%Y" - short_no_year: "%d.%m" - date_only: "%d.%m.%Y" title: "Discourse" topics: "Темы" posts: "сообщения" @@ -23,13 +28,13 @@ ru: log_in: "Войти" via: "%{username} на %{site_name}" is_reserved: "зарезервировано" - purge_reason: "Автоматически удалить из-за просроченности и непроверенности" + purge_reason: "Автоматически удален, как неактивная, неактивированная учетная запись" disable_remote_images_download_reason: "Загрузка картинок была отключена из-за недостаточности места на диске." errors: + format: '%{attribute} %{message}' messages: too_long_validation: "не может быть длиннее %{max} символов, а введено %{length}." invalid_boolean: "Неправильное булевое значение." - taken: "уже занято" embed: load_from_remote: "Произошла ошибка при загрузке сообщения." bulk_invite: @@ -100,6 +105,7 @@ ru: latest: "Последние темы" hot: "Популярные темы" too_late_to_edit: "Это сообщение нельзя изменить или удалить, т.к. оно было создано очень давно." + excerpt_image: "изображение" groups: errors: can_not_modify_automatic: "Нельзя изменять автоматическую группу." @@ -190,8 +196,8 @@ ru: base: warning_requires_pm: "Прикреплять предупреждения можно только к приватным сообщениям." too_many_users: "Можно отправлять предупреждения только одному пользователю за раз." - archetype: cant_send_pm: "Извините, вы не можете посылать личные сообщения данному пользователю." + no_user_selected: "Вы должны выбрать корректного пользователя." user: attributes: password: @@ -367,26 +373,34 @@ ru: activation: action: "Активируйте вашу учетную запись" already_done: "Извините, ссылка на активацию учетной записи устарела. Возможно, ваша учетная запись уже активирована?" + please_continue: "Ваша новая учетная запись успешно активирована, вы будете перенаправлены на главную страницу." continue_button: "Перейти на %{site_name}" welcome_to: "Добро пожаловать на сайт %{site_name}!" approval_required: "Ваша учетная запись должна быть вручную подтверждена модератором, чтобы вы смогли зайти на форум. Вы получите электронное письмо, когда ваша учетная запись будет подтверждена!" post_action_types: off_topic: title: 'Не по теме' + description: 'Это сообщение не имеет отношения к данному обсуждению. Возможно, его стоит перенести в соответствующий раздел.' long_form: 'отмечена как не по теме' spam: title: 'СПАМ' description: 'Это реклама! Данное сообщение не представляет особого интереса или не относится к начальной теме. ' long_form: 'отмечено как СПАМ' + email_title: '"%{title}" отмечено как спам' inappropriate: title: 'Неуместно' description: 'Это сообщение может быть оскорбительным или нарушает правила поведения.' long_form: 'отметить как неуместное' notify_user: + title: 'Отправить личное сообщение @{{username}}' description: 'Это сообщение содержит что-то, на что я хочу обратить внимание автора в приватном диалоге. Не отправлять жалобу модераторам.' + long_form: 'оповещаемый пользователь' email_title: 'Ваше сообщение в теме "%{title}"' email_body: "%{link}\n\n%{message}\n" notify_moderators: + title: "Другое" + description: 'Это сообщение требует внимания модератора по другой причине.' + long_form: 'жалоба модератору' email_title: 'Сообщение в теме "%{title}" требует внимания модератора' email_body: "%{link}\n\n%{message}\n" bookmark: @@ -411,7 +425,9 @@ ru: description: 'Эта тема может быть сочтена оскорбительной или нарушает правила поведения на сайте.' long_form: 'отмеченно как неуместное' notify_moderators: + title: "Другое" description: 'Эта тема требует внимания модератора, т.к. нарушает правила поведения или пользовательское соглашение, или по другой причине.' + long_form: 'жалоба модератору' email_title: 'Тема "%{title}" требует внимания модератора' email_body: "%{link}\n\n%{message}" flagging: @@ -512,6 +528,33 @@ ru: title: "Топ ссылающихся тем" xaxis: "Тема" num_clicks: "Переходов" + page_view_anon_reqs: + title: "Анонимы" + xaxis: "Дата" + yaxis: "Просмотров" + page_view_logged_in_reqs: + title: "Пользователи" + xaxis: "Дата" + yaxis: "Просмотров" + page_view_crawler_reqs: + title: "Поисковики" + xaxis: "Дата" + yaxis: "Просмотров" + page_view_total_reqs: + title: "Всего" + xaxis: "Дата" + yaxis: "Просмотров" + http_background_reqs: + title: "Фоновые" + xaxis: "Дата" + yaxis: "Просмотров" + http_2xx_reqs: + title: "Статус 2xx (OK)" + xaxis: "Дата" + yaxis: "Запросов" + http_total_reqs: + title: "Всего" + yaxis: "Всего" dashboard: rails_env_warning: "Ваш сервер работает в режиме %{env}." ruby_version_warning: "Вы используете версию Ruby 2.0.0, у которой имеются известные проблемы. Обновите версию до patch-247 или более новую." @@ -526,15 +569,9 @@ ru: s3_backup_config_warning: 'Сервер сконфигурирован на загрузку резервных копий в хранилище S3, однако не заданы все необходимые настройки : s3_access_key_id, s3_secret_access_key, s3_backup_bucket. Перейдите в настройки сайта и установите необходимые значения. Для получения дополнительной информации перейдите по ссылке How to set up image uploads to S3?.' image_magick_warning: 'Сервер сконфигурирован на создание превью больших изображений, однако на сервере не установлен ImageMagick. Установите ImageMagick используя системный менеджер пакетов, или пройдите по ссылке для загрузки последней версии.' failing_emails_warning: 'Провалено %{num_failed_jobs} почтовых задач. Проверьте файл config/discourse.conf и убедитесь, что почтовые настройки правильные. Просмотр проваленных задач Sidekiq.' - default_logo_warning: "Вы не установили изображение логотипа вашего сайта. Обновите logo_url, logo_small_url и favicon_url в Site Settings." - contact_email_missing: "Вы не установили контактный емаил для вашего сайта. Пожалуйста, обновите contact_email в разделе Настройки сайта." - contact_email_invalid: "Контактный емаил сайта недействителен. Пожалуйста, обновите contact_email в разделе Настройки сайта." - title_nag: "Заголовок Настроек сайта до сих пор установлен на значение по умолчанию. Обновите его в соответствии с названием вашего сайта здесь: Настройки сайта" - site_description_missing: "Опция site_description не установлена. Добавьте короткое описание форума на странице настроек сайта." + contact_email_invalid: "Контактный адрес сайта некорректен. Обновите его в Настройках сайта." + title_nag: "Введите название вашего сайта. Обновите заголовок в Настройках сайта." consumer_email_warning: "Сайт настроен на использование Gmail (или другого почтового сервиса) для отправки сообщений. Gmail имеет ограничения на количество отправляемых сообщений. Вы можете рассмотреть возможность использования альтернативного поставщика услуг (например, mandrill.com) для обеспечения бесперебойности доставки почтовых сообщений." - access_password_removal: "Ваш сайт использовал настройку access_password setting, которая была удалена. Были активированы настройки login_required и must_approve_users settings, которые должны были использоваться. Вы можете изменить их в Site Settings. Не забудьте подтвердить пользователей в листе ожидания. (Это сообщение исчезнет через 2 суток.)" - site_contact_username_warning: "Опция site_contact_username не установлена. Пожалуйста, обновите ее на странице Настройки сайта. Установите ее в значение имени пользователя или администратора от имени которого будут отсылаться системные сообщения пользователям." - notification_email_warning: "Опция notification_email не установлена. Пожалуйста обновите ее на странице Настройки сайта." content_types: education_new_reply: title: "Обучение нового пользователя: Первые ответы" @@ -565,6 +602,7 @@ ru: description: "HTML, который будет добавлен в начале страницы (после заголовка, перед панелью навигации или заголовком темы)." bottom: title: "Внизу страниц" + description: "HTML код, который будет добавлен перед тегом ." site_settings: censored_words: "Слова, которые будут автоматически заменены на ■■■■" delete_old_hidden_posts: "Автоматически удалять сообщения, скрытые дольше чем 30 дней." @@ -582,12 +620,8 @@ ru: allow_duplicate_topic_titles: "Разрешить создание тем с одинаковыми названиями." unique_posts_mins: "Количество минут до того, как пользователь сможет разместить сообщение с тем же содержанием." educate_until_posts: "Количество первых сообщений новых пользователей, для которых необходимо показывать всплывающую подсказку с советами для новичков." - title: "Короткое название этого сайта. Будет использоваться в HTML-тэге title." - site_description: "Короткое описание этого сайта. Будет использоваться в HTML-тэге description." - contact_email: "Главный адрес электронной почты для переписки по вопросам сайта. Команда Discourse.org может посылать важные уведомления на этот адрес." - company_full_name: "DEPRECATED. Not used anymore and will be removed. The full name of the company that runs this site, used in legal documents like /tos and /privacy" - company_short_name: "DEPRECATED. Not used anymore and will be removed. The short name of the company that runs this site, used in legal documents like /tos and /privacy" - company_domain: "DEPRECATED. Not used anymore and will be removed. The domain name owned by the company that runs this site, used in legal documents like /tos and /privacy" + title: "Название этого сайта. Будет использоваться в HTML-тэге title." + site_description: "Опишите сайт одним предложением для использования описания в мета-теге description." queue_jobs: "ТОЛЬКО ДЛЯ РАЗРАБОТЧИКОВ! ВНИМАНИЕ! По умолчанию задачи обрабатываются асинхронно в очереди sidekiq. Если настройка выключена, ваш сайт может не работать." crawl_images: "Скачивать картинки с других сайтов для автоматического определения их размеров." download_remote_images_to_local: "Скачивать картинки, вставленные в сообщения ссылками на другие сайты, и хранить их локально, чтобы предотвратить их изменения или утерю." @@ -607,11 +641,8 @@ ru: post_excerpt_maxlength: "Максимальная длина краткого изложения сообщения." post_onebox_maxlength: "Максимальная длина сообщения с форума Discourse в режиме умной вставки." onebox_domains_whitelist: "Список доменов, разрешенных для умной вставки. Эти домены должны поддерживать OpenGraph или oEmbed. Проверьте их по ссылке http://iframely.com/debug" - logo_url: "Логотип сайта, отображается сверху слева. Напирмер: http://example.com/logo.png" digest_logo_url: "Альтернативный логотип для использования вверху письма-дайджеста. Если оставить пустым, будет использован логотип из настройки `logo_url`. Пример: http://example.com/logo.png" - logo_small_url: "Уменьшенный логотип вашего сайта, используется сверху слева вместо основного логотипа при прокрутке списка тем вниз, например: http://example.com/logo-small.png" favicon_url: "favicon вашего сайта, дополнительная информация: http://en.wikipedia.org/wiki/Favicon" - mobile_logo_url: "Логотип для отображения вверху в мобильной версии сайта. Если оставить пустым, будет использован логотип из настройки `logo_url`. Пример: http://example.com/uploads/default/logo.png" apple_touch_icon_url: "Иконка используемая для тач-устройств Apple. Рекомендуемый размер 144 x 144 px." notification_email: "Отправитель: E-mail использующийся при отправке всех системных писем. Домен, указанный здесь должен иметь правильно сконфигурированные SPF, DKIM и reverse PTR записи, для успешной отправки." email_custom_headers: "Разделенный чертой список дополнительных заголовков в почтовых сообщениях" @@ -639,14 +670,10 @@ ru: ga_universal_domain_name: "Google Universal Analytics (analytics.js) доменное имя, например: mysite.com; больше можно найти на странице http://google.com/analytics" enable_noscript_support: "Включить поддержку тэга noscript для поисковых роботов" allow_moderators_to_create_categories: "Разрешить модераторам создавать новые разделы" - top_menu: "Определите, какие элементы должны располагаться в навигации на главной странице, и в каком порядке. Пример: latest|new|unread|starred|categories|top|read|posted" post_menu: "Определите, какие элементы должны отображаться в меню у сообщения и в какой последовательности. Пример: like|edit|flag|delete|share|bookmark|reply" post_menu_hidden_items: "Пункты меню действий над сообщением, которые должны быть спрятаны и появляться по нажатию на кнопку с троеточием." share_links: "Определите, какие элементы должны отображаться в окне 'Поделиться' и в какой последовательности." track_external_right_clicks: "Отслеживать внешние ссылки, открытые правой кнопкой мыши (например: открыть в новой вкладке), по умолчанию отключено, так как это перезаписывает пути" - topics_per_page: "Сколько тем показывать сразу в списках тем, и подгружать за один раз при прокручивании списка вниз." - posts_chunksize: "Сколько сообщений показывать сразу на странице темы и подгружать за один раз при ее прокручивании вниз." - site_contact_username: "Все личные сообщения, отправляемые форумчанам автоматически, будут отправляться от имени этого пользователя. Если оставить пустым, то будет использован системный пользователь по-умолчанию - system." send_welcome_message: "Отправлять новым зарегистрировавшимся пользователям приватное сообщение с короткой инструкцией по возможностям форума." redirect_users_to_top_page: "Автоматически перенаправлять новых и давно отсутствующих пользователей к началу страницы." show_email_on_profile: "Показать Email пользователя в профиле (видно только себе и персоналу)" @@ -657,11 +684,10 @@ ru: port: "Внимание, только для разработчиков! Использовать порт HTTP отличный от значения по умолчанию. Оставьте это поле пустым для использования стандартного 80-го порта." force_hostname: "Внимание, только для разработчиков! Укажите имя хоста для формирования URL или оставьте пустым для значения по умолчанию." invite_expiry_days: "Срок валидности ключей, высланных приглашенному пользователю, в днях" - min_username_length: "Минимальная длина имени пользователя в символах. ВНИМАНИЕ: ЛЮБЫЕ СУЩЕСТВУЮЩИЕ ПОЛЬЗОВАТЕЛИ С ИМЕНЕМ КОРОЧЕ ЭТОГО ЗНАЧЕНИЯ НЕ СМОГУТ ВОЙТИ НА САЙТ." - max_username_length: "Максимальная длина имени пользователя в символах. ВНИМАНИЕ: ЛЮБЫЕ СУЩЕСТВУЮЩИЕ ПОЛЬЗОВАТЕЛИ С ИМЕНЕМ ДЛИННЕЕ ЭТОГО ЗНАЧЕНИЯ НЕ СМОГУТ ВОЙТИ НА САЙТ." + min_username_length: "Минимально допустимая длина псевдонима. ВНИМАНИЕ: СУЩЕСТВУЮЩИЕ ПОЛЬЗОВАТЕЛИ С ПСЕВДОНИМОМ КОРОЧЕ ЭТОГО ЗНАЧЕНИЯ НЕ СМОГУТ ПОЛЬЗОВАТЬСЯ САЙТОМ." + max_username_length: "Максимально допустимая длина псевдонима. ВНИМАНИЕ: СУЩЕСТВУЮЩИЕ ПОЛЬЗОВАТЕЛИ С ПСЕВДОНИМОМ ДЛИННЕЕ ЭТОГО ЗНАЧЕНИЯ НЕ СМОГУТ ПОЛЬЗОВАТЬСЯ САЙТОМ." min_password_length: "Минимальная длина пароля" block_common_passwords: "Не позволять использовать пароли из списка 10 000 самых частоиспользуемых паролей." - enable_sso: "Включить единый вход через внешний сайт (Примечание: отключает приглашения)" sso_url: "URL точки единого входа" allow_new_registrations: "Разрешить регистрацию новых пользователей. Выключите, чтобы запретить посетителям создавать новые учетные записи." enable_yahoo_logins: "Разрешить идентификацию с Yahoo" @@ -678,6 +704,7 @@ ru: maximum_backups: "Максимальное количество резервных копий к сохранению. Более старые резервные копии будут автоматически удалены." backup_daily: "Автоматически создавать резервную копию сайта раз в день." active_user_rate_limit_secs: "Как часто мы обновляем поле 'last_seen_at', в секундах" + verbose_localization: "Показывать ключи используемых строк в интерфейсе для перевода на другой язык" previous_visit_timeout_hours: "Как долго должно длиться посещение сайта, чтобы мы посчитали его «предыдущим посещением», в часах" rate_limit_create_topic: "Пользователи не могут создавать больше одной новой темы в указанное количество секунд." rate_limit_create_post: "Пользователи не могут писать более одного нового сообщения в указанное количество секунд." @@ -687,7 +714,6 @@ ru: max_flags_per_day: "Максимальное количество поданных жалоб пользователем в день." max_bookmarks_per_day: "Максимальное количество созданных закладок пользователем в день." max_edits_per_day: "Максимальное количество редактирований пользователем в день." - max_stars_per_day: "Максимальное количество тем, которые пользователь может добавить в Избранное за один день." max_topics_per_day: "Максимальное количество тем, которые пользователь может создать за один день." max_private_messages_per_day: "Максимальное количество приватных сообщений, которые пользователь может послать за один день." suggested_topics: "Количество рекомендованных тем, отображаемых внизу текущей темы." @@ -745,7 +771,6 @@ ru: disable_emails: "Запретить форуму отсылать какие-либо письма." strip_images_from_short_emails: "Удалять картинки из писем размером менее 2800 байт" short_email_length: "Какие письма считать короткими, в байтах" - enable_email_names: "Показывать полное имя пользователя в письмах. Выключите, чтобы спрятать полное имя в письмах." pop3_polling_enabled: "Загружать ответы в форум в виде писем с учетной записи POP3." pop3_polling_ssl: "Использовать SSL при подключениик POP3 серверу. (Рекомендовано)" pop3_polling_period_mins: "Периодичность загрузки e-mail учетной записи POP3. ВНИМАНИЕ: требует перезапуска." @@ -758,9 +783,10 @@ ru: relative_date_duration: "Количество дней после отправки сообщения, в течении которых его дата будет отображаться в относительном виде (7д), а позже - в абсолютном (20 Фев)." delete_user_max_post_age: "Запретить удаление пользователей, чье первое сообщение старее (x) дней." delete_all_posts_max: "Максимальное количество сообщений, которое может быть удалено за один раз через кнопку \"Удалить все сообщения\". Если у пользователя сообщений больше этого числа, сообщения удаляются не полностью и пользователь не удаляется." - username_change_period: "Количество дней после регистрации, когда пользователь может сменить свой логин (0 запрещает изменение логина)." + username_change_period: "Количество дней после регистрации, когда пользователь сможет изменить свой псевдоним (0 - запретить изменение псевдонима)." email_editable: "Позволять пользователям изменять свой адрес электронной почты после регистрации." allow_uploaded_avatars: "Разрешить пользователям загружать собственные картинки для аватарок." + allow_animated_thumbnails: "Генерировать анимированные миниатюры gif-картинок." automatically_download_gravatars: "Скачивать аватарку Gravatar пользователя во время создания учетной записи или изменения e-mail." digest_topics: "Максимальное количество тем в письме - сводке новостей." digest_min_excerpt_length: "Минимальная длина (в символах) вытяжки из сообщения в письме - сводке новостей." @@ -773,7 +799,6 @@ ru: enable_mobile_theme: "Мобильные устройства используют адаптированную тему с возможностью переключения в обычный вид. Отключите данную настройку если вы хотите использовать собственный стиль для мобильных устройств." suppress_uncategorized_badge: "Не показывать награду в списках тем для тех тем, которые вне разделов." global_notice: "Показать СРОЧНОЕ ВАЖНОЕ объявление на сайте всем пользователям и посетителям. Удалите содержание, чтобы отменить объявление. Разрешено использование HTML." - enable_names: "Разрешить отображение полных имен пользователей. Выключите, чтобы не показывать полные имена." display_name_on_posts: "Показывать полные имена пользователей в их сообщениях в дополнение к их @нику." short_progress_text_threshold: "После достижения указанного числа сообщений в теме, бар будет отображать только текущий номер сообщения. Если вы измените ширину бара, вы можете изменить это значение." default_code_lang: "Подсветка синтаксиса по умолчанию для блоков кода (lang-auto, ruby, python etc.)" @@ -781,6 +806,8 @@ ru: embed_truncate: "Обрезать встроенные сообщения." embed_post_limit: "Максимальное количество вложенных сообщений." show_create_topics_notice: "Если общее количество тем меньше 5, показывать персоналу соощбение с просьбой создать новые темы." + enable_emoji: "Активируйте Emoji" + emoji_set: "Какой набор использовать? " errors: invalid_email: "Неправильный адрес электронной почты." invalid_username: "Пользователя с таким ником не найдено." @@ -835,7 +862,32 @@ ru: archived_disabled: "Эта тема разархивирована. Она более не заморожена, и может быть изменена." closed_enabled: "Эта тема закрыта. В ней больше нельзя отвечать." closed_disabled: "Эта тема открыта. В ней можно отвечать." + autoclosed_enabled_days: + one: "Эта тема была автоматически закрыта спустя 1 день. В ней больше нельзя отвечать." + few: "Эта тема была автоматически закрыта спустя %{count} дня. В ней больше нельзя отвечать." + other: "Эта тема была автоматически закрыта спустя %{count} дней. В ней больше нельзя отвечать." + autoclosed_enabled_hours: + one: "Эта тема была автоматически закрыта спустя 1 час. В ней больше нельзя отвечать." + few: "Эта тема была автоматически закрыта спустя %{count} часа. В ней больше нельзя отвечать." + other: "Эта тема была автоматически закрыта спустя %{count} часов. В ней больше нельзя отвечать." + autoclosed_enabled_minutes: + one: "Эта тема была автоматически закрыта спустя 1 минуту. В ней больше нельзя отвечать." + few: "Эта тема была автоматически закрыта спустя %{count} минуты. В ней больше нельзя отвечать." + other: "Эта тема была автоматически закрыта спустя %{count} минут. В ней больше нельзя отвечать." + autoclosed_enabled_lastpost_days: + one: "Эта тема была автоматически закрыта через 1 день после последнего ответа. В ней больше нельзя отвечать." + few: "Эта тема была автоматически закрыта через %{count} дня после последнего ответа. В ней больше нельзя отвечать." + other: "Эта тема была автоматически закрыта через %{count} дней после последнего ответа. В ней больше нельзя отвечать." + autoclosed_enabled_lastpost_hours: + one: "Эта тема была автоматически закрыта через 1 час после последнего ответа. В ней больше нельзя отвечать." + few: "Эта тема была автоматически закрыта через %{count} часа после последнего ответа. В ней больше нельзя отвечать." + other: "Эта тема была автоматически закрыта через %{count} часов после последнего ответа. В ней больше нельзя отвечать." + autoclosed_enabled_lastpost_minutes: + one: "Эта тема была автоматически закрыта через 1 минуту после последнего ответа. В ней больше нельзя отвечать." + few: "Эта тема была автоматически закрыта через %{count} минуты после последнего ответа. В ней больше нельзя отвечать." + other: "Эта тема была автоматически закрыта через %{count} минут после последнего ответа. В ней больше нельзя отвечать." autoclosed_disabled: "Эта тема открыта. В ней можно отвечать." + autoclosed_disabled_lastpost: "Эта тема теперь открыта, и в ней можно отвечать." pinned_enabled: "Данная тема прилеплена. Тема будет находиться наверху списка тем раздела, пока кто нибудь из администраторов не открепит ее." pinned_disabled: "Эта тема отлеплена. Она больше не будет отображаться наверху списка тем раздела." pinned_globally_enabled: "Эта тема теперь прилеплена глобально и будет появляться вверху списка тем соответствующего раздела и всех списков тем, пока персонал не отлепит ее глобально или пользователь не отлепит лично для себя." @@ -847,6 +899,7 @@ ru: incorrect_username_email_or_password: "Неверное имя пользователя, адрес электронной почты или пароль" wait_approval: "Спасибо за регистрацию. Мы оповестим вас, когда ваша учетная запись будет одобрена." active: "Ваша учетная запись активирована и готова к использованию." + activate_email: "

Почти готово! Мы выслали письмо на %{email}. Пожалуйста, следуйте инструкциям в этом письме для активации вашей учетной записи.

Если письмо не пришло, пожалуйста, проверьте папку \"спам\", или попробуйте войти еще раз, чтобы выслать активационное письмо повторно.

" not_activated: "Вы пока что не можете войти на сайт. Пожалуйста, следуйте инструкциям по активации учетной записи, которые мы отправили вам по электронной почтой." not_allowed_from_ip_address: "Вам нельзя входить как %{username} с этого IP адреса." suspended: "Вы не можете войти до %{date}." @@ -854,12 +907,13 @@ ru: errors: "%{errors}" not_available: "Недоступно. Попробуйте %{suggestion}?" something_already_taken: "Что-то пошло не так, возможно, имя пользователя или электронный ящик уже используются. Попробуйте восстановить ваш пароль." - omniauth_error: "Извините, невозможно авторизовать вашу учетную запись %{strategy}. Возможно, вы не подтвердили авторизацию?" omniauth_error_unknown: "В процессе входа на сайт произошла ошибка. Пожалуйста, повторите попытку." new_registrations_disabled: "Новые регистрации сейчас ограничены." password_too_long: "Максимальная длина пароля - 200 символов." + missing_user_field: "Вы не заполнили все поля пользователя" close_window: "Аутентификация выполнена. Закройте это окно для продолжения." user: + no_accounts_associated: "Нет ассоциированных учетных записей" username: short: "минимум %{min} знаков" long: "должно быть более %{max} знаков" @@ -906,6 +960,11 @@ ru: Это персональное приглашение от зарегистрированного пользователя, которому мы доверяем, поэтому вам не понадобиться авторизоваться на сайте. invite_password_instructions: subject_template: "Задайте пароль для вашей учетной записи на сайте %{site_name}" + text_body_template: | + Спасибо, что приняли приглашение на сайт %{site_name} -- добро пожаловать! + + Чтобы войти, нажмите следующую следующую ссылку и установите пароль: + %{base_url}/users/password-reset/%{email_token} test_mailer: subject_template: "[%{site_name}] Проверка доставки писем" new_version_mailer: @@ -948,58 +1007,28 @@ ru: few: "Эти жалобы были отправлены более %{count} часов назад." other: "Эти жалобы были отправлены более %{count} часов назад." please_review: "Пожалуйста, рассмотрите их" + post_number: "сообщение" subject_template: one: "1 жалоба ожидает рассмотрения" few: "%{count} жалобы ожидают рассмотрения" other: "%{count} жалоб ожидают рассмотрения" - flag_reasons: - off_topic: "Сообщество считает, что ваше сообщение **не относится к теме**, учитывая ее заголовок и первое сообщение." - inappropriate: "Сообщество считает ваше сообщение **неприемлемым**, т.е. оскорбительным, непристойным или нурашающим [кодекс чести](/guidelines)." - spam: "Сообщество считает, что ваше сообщение **является спамом**, то есть, не имеет отношения к теме и имеет рекламный характер." - notify_moderators: "Сообщество считает, что ваше сообщение **требует внимания модератора**." flags_dispositions: agreed: "Спасибо за информацию. Мы согласны, что это проблема, и уже рассматриваем ее." agreed_and_deleted: "Спасибо за информацию - мы согласны с вами и удалили сообщение." disagreed: "Спасибо за информацию. Уже рассматриваем." deferred: "Спасибо за информацию. Уже рассматриваем." deferred_and_deleted: "Спасибо за информацию. Сообщение удалено." + temporarily_closed_due_to_flags: "Эта тема временно закрыта из-за большого количества жалоб" system_messages: post_hidden: subject_template: "Сообщение скрыто по причине поступления множественных жалоб" welcome_user: subject_template: "Добро пожаловать на %{site_name}!" - text_body_template: | - Спасибо, что зарегистрировались на сайте %{site_name} и добро пожаловать! - - %{new_user_tips} - - Пожалуйста, старайтесь придерживаться [этики и правил вежливости](%{base_url}/guidelines). - - Приятного времяпровождения! welcome_invite: subject_template: "Добро пожаловать на %{site_name}!" - text_body_template: | - Спасибо за то, что приняли приглашение присоединиться к сайту %{site_name}, и добро пожаловать на наш форум! - - Мы автоматически дали вам имя пользователя: **%{username}**, но вы можете изменить его в любое время в [вашем профиле][prefs]. - - Чтобы снова зайти на сайт: - - 1. Используйте учетную запись Facebook, Google, Twitter, или одну из множества других поддерживаемых – но она должна быть зарегистрирована на **тот же адрес электронной почты**, на который вы получили приглашение. Иначе мы не можем быть уверены, что это вы. - - 2. Придумайте уникальный пароль для сайта %{site_name} в [вашем профиле][prefs], затем войдите на сайт, используя его. - - %{site_password} - - %{new_user_tips} - - Мы верим в [культурное сообщество](%{base_url}/faq)! - - Наслаждайтесь общением! - - [prefs]: %{user_preferences_url} backup_succeeded: subject_template: "Резервное копирование успешно завершено" + text_body_template: "Резервная копия создана.\nПерейдите в [Админку > Резервные копии](/admin/backups), чтобы скачать её." backup_failed: subject_template: "Резервное копирование не удалось" text_body_template: | @@ -1036,17 +1065,6 @@ ru: ``` %{logs} ``` - csv_export_succeeded: - subject_template: "Экспорт данных успешно завершен" - text_body_template: | - Экспорт данных прошел успешно. - - Загрузить CSV файл можно по ссылке: %{file_name} - - Ссылка будет действать только 48 часов. - csv_export_failed: - subject_template: "Экспорт не удался" - text_body_template: "Экспорт не удался. Пожалуйста, проверьте логи." email_reject_trust_level: subject_template: "Проблема с письмом - недостаточный уровень доверия" text_body_template: | @@ -1057,6 +1075,12 @@ ru: Если вы считаете, что произошла ошибка, свяжитесь с персоналом (модератором или администратором). email_reject_no_account: subject_template: "Проблема с письмом - не найдена учетная запись" + text_body_template: | + К сожалению, ваше письмо к %{destination} (под названием %{former_title}) не может быть обработано. + + Мы не смогли найти учетную запись с электронным адресом, с которого пришло ваше письмо. + + Попробуйте отправить с другого почтового ящика, или, если вы считаете, что произошла ошибка, свяжитесь с персоналом - модератором или администратором. email_reject_empty: subject_template: "Проблема с письмом - отсутствует текст" email_reject_parsing: @@ -1073,6 +1097,14 @@ ru: Ключ ответа неверный, поэтому мы не можем определить, кому адресован ваш ответ. Пожалуйста, свяжитесь с персоналом (модератором или администратором). email_reject_destination: subject_template: "Проблема с письмом - неизвестный адрес получателя To:" + email_reject_topic_not_found: + subject_template: "Проблема с обработкой письма - тема не найдена" + email_reject_topic_closed: + subject_template: "Проблема с обработкой письма - тема уже закрыта" + text_body_template: | + К сожалению, ваше письмо к %{destination} (под названием %{former_title}) не может быть обработано. + + Тема закрыта. Если вы считаете, что произошла ошибка, свяжитесь с персоналом (модератором или администратором). email_error_notification: subject_template: "Проблема с e-mail -- ошибка POP аутентификации" too_many_spam_flags: @@ -1186,6 +1218,7 @@ ru: --- %{respond_instructions} digest: + why: "Сводка обсуждений на сайте %{site_link} с момента вашего последнего визита %{last_seen_at}" subject_template: "[%{site_name}] Новые сообщения от %{date}" new_activity: "Новая активность в ваших темах и сообщениях:" top_topics: "Популярные темы" @@ -1193,6 +1226,8 @@ ru: click_here: "нажмите здесь" from: "Cводка новостей сайта %{site_name}" read_more: "Читать еще" + more_topics: "Было создано еще %{new_topics_since_seen} новых тем." + more_topics_category: "Еще новые темы:" posts: one: "1 сообщение" few: "1 сообщения" @@ -1259,10 +1294,7 @@ ru: unauthorized: "К сожалению, вы не можете загрузить файл данного типа (список разрешенных типов файлов: %{authorized_extensions})." pasted_image_filename: "Имя файла изображения" store_failure: "Ошибка при загрузке #%{upload_id} пользователя #%{user_id}." - attachments: - too_large: "Файл, который вы пытаетесь загрузить, слишком большой (максимальный разрешенный размер %{max_size_kb}%kb)." images: - too_large: "Изображение, которое вы пытаетесь загрузить, слишком большое (максимальный разрешенный размер %{max_size_kb}%kb), пожалуйста, уменьшите размер изображения и повторите попытку." fetch_failure: "Извините, во время извлечения изображения произошла ошибка." unknown_image_type: "Файл, который вы загружаете, не является изображением." size_not_found: "Извините, мы не можем определить размер изображения. Возможно, изображение повреждено?" @@ -1287,6 +1319,9 @@ ru: guidelines: "Руководство" privacy: "Политика конфиденциальности" edit_this_page: "Отредактировать эту страницу" + csv_export: + boolean_yes: "Да" + boolean_no: "Нет" static_topic_first_reply: | Отредактируйте первое сообщение этой темы чтобы изменить содержание страницы {page_name}. guidelines_topic: diff --git a/config/locales/server.sq.yml b/config/locales/server.sq.yml index 3dc45edfc6..308bf7c724 100644 --- a/config/locales/server.sq.yml +++ b/config/locales/server.sq.yml @@ -20,11 +20,6 @@ sq: short_date_no_year: "D MMM" short_date: "D MMM, YYYY" long_date: "MMMM D, YYYY h:mma" - time: - formats: - short: "%m-%d-%Y" - short_no_year: "%B %-d" - date_only: "%b %-d, %Y" title: "Discourse" topics: "Tema" posts: "postime" @@ -33,13 +28,11 @@ sq: log_in: "Identifikohu" via: "%{username} \tnëpër %{site_name}" is_reserved: "është i rezervuar" - purge_reason: "Automatically deleted due to being old and unverified" disable_remote_images_download_reason: "Remote images download was disabled because there wasn't enough disk space available." errors: messages: too_long_validation: "is limited to %{max} characters; you entered %{length}." invalid_boolean: "Boolean jo i vlefshëm." - taken: "është i zënë" embed: load_from_remote: "Postimi nuk mundi të ngarkohet. Riprovoheni!" bulk_invite: @@ -68,32 +61,32 @@ sq: other: "%{count} përgjigje" too_many_mentions: zero: "Na vjen keq, por s'mund të citosh anëtarë të tjerë." - one: "Sorry, you can only mention one other user in a post." - other: "Sorry, you can only mention %{count} users in a post." + one: "Na vjen keq, you can only mention one other user in a post." + other: "Na vjen keq, you can only mention %{count} users in a post." too_many_mentions_newuser: zero: "Sorry, new users can't mention other users." - one: "Sorry, new users can only mention one other user in a post." - other: "Sorry, new users can only mention %{count} users in a post." + one: "Na vjen keq, new users can only mention one other user in a post." + other: "Na vjen keq, new users can only mention %{count} users in a post." too_many_images: - zero: "Sorry, new users can't put images in posts." - one: "Sorry, new users can only put one image in a post." - other: "Sorry, new users can only put %{count} images in a post." + zero: "Na vjen keq, new users can't put images in posts." + one: "Na vjen keq, new users can only put one image in a post." + other: "Na vjen keq, new users can only put %{count} images in a post." too_many_attachments: - zero: "Sorry, new users can't put attachments in posts." - one: "Sorry, new users can only put one attachment in a post." - other: "Sorry, new users can only put %{count} attachments in a post." + zero: "Na vjen keq, new users can't put attachments in posts." + one: "Na vjen keq, new users can only put one attachment in a post." + other: "Na vjen keq, new users can only put %{count} attachments in a post." too_many_links: - zero: "Sorry, new users can't put links in posts." - one: "Sorry, new users can only put one link in a post." - other: "Sorry, new users can only put %{count} links in a post." - spamming_host: "Sorry you cannot post a link to that host." + zero: "Na vjen keq, new users can't put links in posts." + one: "Na vjen keq, new users can only put one link in a post." + other: "Na vjen keq, new users can only put %{count} links in a post." + spamming_host: "Na vjen keq, you cannot post a link to that host." user_is_suspended: "Suspended users are not allowed to post." just_posted_that: "is too similar to what you recently posted" has_already_been_used: "është përdorur më parë" invalid_characters: "përmban karaktere jo të vlefshëm" is_invalid: "is invalid; try to be a little more descriptive" next_page: "faqja tjetër →" - prev_page: "← previous page" + prev_page: "← faqe mëparshme" page_num: "Faqe %{num}" topics_in_category: "Topics in the '%{category}' category" rss_posts_in_topic: "Burimi RSS i '%{topic}'" @@ -105,7 +98,7 @@ sq: private_message_abbrev: "PM" rss_description: latest: "Temat e fundit" - hot: "Tema të nxehta" + hot: "Temat Kryesore" too_late_to_edit: "That post was created too long ago. It can no longer be edited or deleted." groups: errors: @@ -196,8 +189,6 @@ sq: base: warning_requires_pm: "You can only attach warnings to private messages." too_many_users: "You can only send warnings to one user at a time." - archetype: - cant_send_pm: "Na vjen keq, por nuk mund të dërgoni mesazh privat këtij anëtari." user: attributes: password: @@ -311,11 +302,11 @@ sq: one: "1y" other: "%{count}y" over_x_years: - one: "> 1y" - other: "> %{count}y" + one: "> 1v" + other: "> %{count}v" almost_x_years: - one: "1y" - other: "%{count}y" + one: "1v" + other: "%{count}v" distance_in_words_verbose: half_a_minute: "tani" less_than_x_seconds: @@ -540,15 +531,7 @@ sq: s3_backup_config_warning: 'The server is configured to upload backups to s3, but at least one the following setting is not set: s3_access_key_id, s3_secret_access_key or s3_backup_bucket. Go to the Site Settings and update the settings. See "How to set up image uploads to S3?" to learn more.' image_magick_warning: 'The server is configured to create thumbnails of large images, but ImageMagick is not installed. Install ImageMagick using your favorite package manager or download the latest release.' failing_emails_warning: 'There are %{num_failed_jobs} email jobs that failed. Check your config/discourse.conf file and ensure that the mail server settings are correct. See the failed jobs in Sidekiq.' - default_logo_warning: "You haven't customized the logo images for your site. Update logo_url, logo_small_url, and favicon_url in the Site Settings." - contact_email_missing: "You haven't provided a contact email for your site. Please update contact_email in the Site Settings." - contact_email_invalid: "The site contact email is invalid. Please update contact_email in the Site Settings." - title_nag: "The title Site Setting is still set to the default value. Please update it with your site's title in the Site Settings." - site_description_missing: "The site_description setting is blank. Write a brief description of this forum in the Site Settings." consumer_email_warning: "Your site is configured to use Gmail (or another consumer email service) to send email. Gmail limits how many emails you can send. Consider using an email service provider like mandrill.com to ensure email deliverability." - access_password_removal: "Your site was using the access_password setting, which has been removed. The login_required and must_approve_users settings have been enabled, which should be used instead. You can change them in the Site Settings. Be sure to approve users in the Pending Users list. (This message will go away after 2 days.)" - site_contact_username_warning: "The site_contact_username setting is blank. Please update it in the Site Settings. Set it to the username of an admin user who should be the sender of system messages." - notification_email_warning: "The notification_email setting is blank. Please update it in the Site Settings." content_types: education_new_reply: title: "New User Education: First Replies" @@ -597,12 +580,6 @@ sq: allow_duplicate_topic_titles: "Allow topics with identical, duplicate titles." unique_posts_mins: "How many minutes before a user can make a post with the same content again" educate_until_posts: "When the user starts typing their first (n) new posts, show the pop-up new user education panel in the composer." - title: "Brief title of this site, used in the title tag." - site_description: "Describe this site in one sentence, used in the meta description tag." - contact_email: "Email address of key contact for site. Important notices from discourse.org regarding critical updates may be sent to this address." - company_full_name: "DEPRECATED. Not used anymore and will be removed. The full name of the company that runs this site, used in legal documents like /tos and /privacy" - company_short_name: "DEPRECATED. Not used anymore and will be removed. The short name of the company that runs this site, used in legal documents like /tos and /privacy" - company_domain: "DEPRECATED. Not used anymore and will be removed. The domain name owned by the company that runs this site, used in legal documents like /tos and /privacy" queue_jobs: "DEVELOPER ONLY! WARNING! By default, queue jobs in sidekiq. If disabled, your site will be broken." crawl_images: "Retrieve images from remote URLs to insert the correct width and height dimensions." download_remote_images_to_local: "Convert remote images to local images by downloading them; this prevents broken images." @@ -622,11 +599,8 @@ sq: post_excerpt_maxlength: "Maximum length of a post excerpt / summary." post_onebox_maxlength: "Maximum length of a oneboxed Discourse post in characters." onebox_domains_whitelist: "A list of domains to allow oneboxing for; these domains should support OpenGraph or oEmbed. Test them at http://iframely.com/debug" - logo_url: "The logo image at the top left of your site eg: http://example.com/logo.png" digest_logo_url: "The alternate logo used at the top of your site's email digest. If left blank `logo_url` will be used. eg: http://example.com/logo.png" - logo_small_url: "The small logo image at the top left of your site, seen when scrolling down. eg: http://example.com/logo-small.png" favicon_url: "A favicon for your site, see http://en.wikipedia.org/wiki/Favicon" - mobile_logo_url: "The fixed position logo image used at the top left of your mobile site. If left blank, `logo_url` will be used. eg: http://example.com/uploads/default/logo.png" apple_touch_icon_url: "Icon used for Apple touch devices. Recommended size is 144px by 144px." notification_email: "The from: email address used when sending all essential system emails. The domain specified here must have SPF, DKIM and reverse PTR records set correctly for email to arrive." email_custom_headers: "A pipe-delimited list of custom email headers" @@ -665,14 +639,10 @@ sq: enable_noscript_support: "Enable standard webcrawler search engine support via the noscript tag" allow_moderators_to_create_categories: "Allow moderators to create new categories" cors_origins: "Allowed origins for cross-origin requests (CORS). Each origin must include http:// or https://. The DISCOURSE_ENABLE_CORS env variable must be set to true to enable CORS." - top_menu: "Determine which items appear in the homepage navigation, and in what order. Example latest|new|unread|starred|categories|top|read|posted" post_menu: "Determine which items appear on the post menu, and in what order. Example like|edit|flag|delete|share|bookmark|reply" post_menu_hidden_items: "The menu items to hide by default in the post menu unless an expansion ellipsis is clicked on." share_links: "Determine which items appear on the share dialog, and in what order." track_external_right_clicks: "Track external links that are right clicked (eg: open in new tab) disabled by default because it rewrites URLs" - topics_per_page: "How many topics are loaded by default on the topic list, and when scrolling down to load more topics" - posts_chunksize: "How many posts are loaded by default on a topic, and when scrolling down to load more posts" - site_contact_username: "All automated private messages will be from this user; if left blank the default System account will be used." send_welcome_message: "Send all new users a welcome private message with a quick start guide." suppress_reply_directly_below: "Don't show the expandable reply count on a post when there is only a single reply directly below this post." suppress_reply_directly_above: "Don't show the expandable in-reply-to on a post when there is only a single reply directly above this post." @@ -702,9 +672,7 @@ sq: max_username_length: "Maximum username length in characters. WARNING: ANY EXISTING USERS WITH NAMES LONGER THAN THIS WILL BE UNABLE TO ACCESS THE SITE." min_password_length: "Minimum password length." block_common_passwords: "Don't allow passwords that are in the 10,000 most common passwords." - enable_sso: "Enable single sign on via an external site (Note: disables invites)" sso_url: "URL of single sign on endpoint" - sso_secret: "Secret string used to encrypt/decrypt SSO information, be sure it is 10 chars or longer" sso_overrides_email: "Overrides local email with external site email from SSO payload (WARNING: discrepancies can occur due to normalization of local emails)" sso_overrides_username: "Overrides local username with external site username from SSO payload (WARNING: discrepancies can occur due to differences in username length/requirements)" sso_overrides_name: "Overrides local name with external site name from SSO payload (WARNING: discrepancies can occur due to normalization of local names)" @@ -741,7 +709,6 @@ sq: max_flags_per_day: "Maximum number of flags per user per day." max_bookmarks_per_day: "Maximum number of bookmarks per user per day." max_edits_per_day: "Maximum number of edits per user per day." - max_stars_per_day: "Maximum number of topics that can be starred per user per day." max_topics_per_day: "Maximum number of topics a user can create per day." max_private_messages_per_day: "Maximum number of private messages users can create per day." suggested_topics: "Number of suggested topics shown at the bottom of a topic." @@ -749,7 +716,6 @@ sq: clean_up_uploads: "Remove orphan unreferenced uploads to prevent illegal hosting. WARNING: you may want to back up of your /uploads directory before enabling this setting." clean_orphan_uploads_grace_period_hours: "Grace period (in hours) before an orphan upload is removed." purge_deleted_uploads_grace_period_days: "Grace period (in days) before a deleted upload is erased." - purge_inactive_users_grace_period_days: "Grace period (in days) before a user who has not activated their account is deleted." enable_s3_uploads: "Place uploads on Amazon S3 storage. IMPORTANT: requires valid S3 credentials (both access key id & secret access key)." s3_use_iam_profile: 'Use AWS EC2 IAM role to retrieve keys. NOTE: enabling will override "s3 access key id" and "s3 secret access key" settings.' s3_upload_bucket: "The Amazon S3 bucket name that files will be uploaded into. WARNING: must be lowercase, no periods, no underscores." @@ -828,7 +794,6 @@ sq: disable_emails: "Prevent Discourse from sending any kind of emails" strip_images_from_short_emails: "Strip images from emails having size less than 2800 Bytes" short_email_length: "Short email length in Bytes" - enable_email_names: "Allow showing user full name in emails. Disable to hide full name in emails." pop3_polling_enabled: "Poll via POP3 for email replies." pop3_polling_ssl: "Use SSL while connecting to the POP3 server. (Recommended)" pop3_polling_period_mins: "The period in minutes between checking the POP3 account for email. NOTE: requires restart." @@ -865,7 +830,6 @@ sq: suppress_uncategorized_badge: "Don't show the badge for uncategorized topics in topic lists." global_notice: "Display an URGENT, EMERGENCY global banner notice to all visitors, change to blank to hide it (HTML allowed)." disable_edit_notifications: "Disables edit notifications by the system user when 'download_remote_images_to_local' is active." - enable_names: "Allow showing user full names. Disable to hide full names." display_name_on_posts: "Show a user's full name on their posts in addition to their @username." invites_per_page: "Default invites shown on the user page." short_progress_text_threshold: "After the number of posts in a topic goes above this number, the progress bar will only show the current post number. If you change the progress bar's width, you may need to change this value." @@ -951,9 +915,6 @@ sq: autoclosed_enabled_lastpost_days: one: "This topic was automatically closed 1 day after the last reply. New replies are no longer allowed." other: "This topic was automatically closed %{count} days after the last reply. New replies are no longer allowed." - autoclosed_enabled_lastpost_hours: - one: "This topic was automatically closed 1 hour after the last reply. New replies are no longer allowed." - other: "This topic was automatically closed count} hours after the last reply. New replies are no longer allowed." autoclosed_enabled_lastpost_minutes: one: "This topic was automatically closed 1 minute after the last reply. New replies are no longer allowed." other: "This topic was automatically closed %{count} minutes after the last reply. New replies are no longer allowed." @@ -978,7 +939,6 @@ sq: errors: "%{errors}" not_available: "Not available. Try %{suggestion}?" something_already_taken: "Something went wrong, perhaps the username or email is already registered. Try the forgot password link." - omniauth_error: "Sorry, there was an error authorizing your %{strategy} account. Perhaps you did not approve authorization?" omniauth_error_unknown: "Something went wrong processing your log in, please try again." new_registrations_disabled: "New account registrations are not allowed at this time." password_too_long: "Passwords are limited to 200 characters." @@ -1039,48 +999,6 @@ sq: %{base_url}/users/password-reset/%{email_token} test_mailer: subject_template: "[%{site_name}] Email Deliverability Test" - text_body_template: | - This is a test email from - - [**%{base_url}**][0] - - Email deliverability is complicated. Here are a few important things you should check first: - - - Be *sure* to set the `notification email` from: address correctly in your site settings. **The domain specified in the "from" address of the emails you send is the domain your email will be validated against**. - - - Know how to view the raw source of the email in your mail client, so you can examine email headers for important clues. in Gmail, it is the "show original" option in the drop-down menu at the top right of each mail. - - - **IMPORTANT:** Does your ISP have a reverse DNS record entered to associate the domain names and IP addresses you send mail from? [Test your Reverse PTR record][2] here. If your ISP does not enter the proper reverse DNS pointer record, it's very unlikely any of your email will be delivered. - - - Is your domain's [SPF record][8] correct? [Test your SPF record][1] here. Note that TXT is the correct official record type for SPF. - - - Is your domain's [DKIM record][3] correct? This will significantly improve email deliverability. [Test your DKIM record][7] here. - - - If you run your own mail server, check to make sure the IPs of your mail server are [not on any email blacklists][4]. Also verify that it is definitely sending a fully-qualified hostname that resolves in DNS in its HELO message. If not, this will cause your email to be rejected by many mail services. - - (The *easy* way is to create a free account on [Mandrill][md] or [Mailgun][mg] or [Mailjet][mj], which have free generous free mailing plans and will be fine for small communities. You'll still need to set up the SPF and DKIM records in your DNS, though!) - - We hope you received this email deliverability test OK! - - Good luck, - - Your friends at [Discourse](http://www.discourse.org) - - [0]: %{base_url} - [1]: http://www.kitterman.com/spf/validate.html - [2]: http://mxtoolbox.com/ReverseLookup.aspx - [3]: http://www.dkim.org/ - [4]: http://whatismyipaddress.com/blacklist-check - [5]: %{base_url}/unsubscribe - [7]: http://dkimcore.org/tools/dkimrecordcheck.html - [8]: http://www.openspf.org/SPF_Record_Syntax - [md]: http://mandrill.com - [mg]: http://www.mailgun.com/ - [mj]: http://www.mailjet.com/pricing - - ---- - - There should be an unsubscribe footer on every email you send, so let's mock one up. This email was sent by Name of Company, 55 Main Street, Anytown, USA 12345. If you would like to opt out of future emails, [click here to unsubscribe][5]. new_version_mailer: subject_template: "[%{site_name}] verion i ri i Discourse, përditësoje!" text_body_template: | @@ -1125,11 +1043,6 @@ sq: subject_template: one: "1 flag waiting to be handled" other: "%{count} flags waiting to be handled" - flag_reasons: - off_topic: "Your post was flagged as **off-topic**: the community thinks it does not fit into the topic, as currently defined by the title and the first post." - inappropriate: "Your post was flagged as **inappropriate**: the community thinks it is offensive, abusive, or a violation of [the community guidelines](/guidelines)." - spam: "Your post was flagged as **spam**: the community thinks it is an advertisement, not useful or relevant to the topic, but promotional in nature." - notify_moderators: "Your post was flagged **for moderator attention**: the community thinks something about the post requires moderator intervention." flags_dispositions: agreed: "Thanks for letting us know. We agree there is an issue and we're looking into it." agreed_and_deleted: "Thanks for letting us know. We agree there is an issue and we've removed the post." @@ -1153,105 +1066,10 @@ sq: However, if the post is hidden by the community a second time, it will remain hidden until handled by staff – and there may be further action, including the possible suspension of your account. For additional guidance, please refer to our [community guidelines](%{base_url}/guidelines). - usage_tips: - text_body_template: | - This private message has a few quick tips to get you started. - - ## Keep scrolling - - There are no next page buttons or page numbers – to read more, **just keep scrolling down!** - - As new posts come in, they will appear automatically. - - ## Where am I? - - - For search, your user page, or the menu, use the **icon buttons at the upper right**. - - - Any topic title will take you to the next unread post. Use the last activity time and post count to enter at the top or bottom. - - - While reading a topic, jump to the top ↑ by selecting the topic title. Select the green progress bar at the bottom right for full navigation controls, or use the home and end keys. - - - - ## How do I reply? - - - To reply to the overall topic, use the Reply button at the very bottom of the page. - - - To reply to a specific post, use the Reply button on that post. - - - To take the conversation in a different direction, but keep them linked together, use Reply as linked Topic to the right of the post. - - To quote someone in your reply, select the text you wish to quote, then press any Reply button. - - - - To ping someone in your reply, mention their name. Type `@` and an autocompleter will pop up. - - - - For [standard Emoji](http://www.emoji.codes/), just start typing `:` or the traditional smileys `:)` :smile: - - ## What else can I do? - - There are action buttons at the bottom of each post. - - - - To let someone know that you enjoyed their post, use the **like** button. If you see a problem with a post, privately let them, or our staff, know about it with the **flag** button. - - You can also **share** a link to a post, or **bookmark** it for later reference on your user page. - - ## Who is talking to me? - - When someone replies to your post, quotes your post, or mentions your `@username`, a number will immediately appear at the top right of the page. Use it access your **notifications**. - - - - Don't worry about missing a reply – you'll be emailed direct replies (and private messages) if you aren't online when they arrive. - - ## When are conversations new? - - By default all conversations less than two days old are considered new, and any conversation you've participated in (replied to, created, or read for an extended period) will automatically be tracked. - - You will see the blue new and number indicators next to these topics: - - - - You can change the individual notification state of a topic via the control at the bottom of the topic (this can also be set per category). To change how you track topics, or the definition of new, see [your user preferences](/my/preferences). - - ## Why can't I do certain things? - - New users are somewhat limited for safety reasons. As you participate here, you'll gain the trust of the community, become a full citizen, and those limitations will automatically be removed. At a high enough [trust level](https://meta.discourse.org/t/what-do-user-trust-levels-do/4924), you'll gain even more abilities to help us manage our community together. welcome_user: subject_template: "Mirë se vini tek %{site_name}!" - text_body_template: | - Thanks for joining %{site_name}, and welcome! - - %{new_user_tips} - - We believe in [civilized community behavior](%{base_url}/guidelines) at all times. - - Enjoy your stay! welcome_invite: subject_template: "Mirë se vini tek %{site_name}!" - text_body_template: | - Thanks for accepting your invitation to %{site_name} -- welcome! - - We've automatically generated a username for you: **%{username}**, but you can change it any time by visiting [your user profile][prefs]. - - To log in again, either: - - 1. Log in using any method you like -- but it must resolve to the **same email address** that you received your original invitation email at. Otherwise we won't be able to tell it is you! - - 2. Create a unique password for [your user profile][prefs], and use it to log in. - - %{new_user_tips} - - We believe in [civilized community behavior](%{base_url}/guidelines) at all times. - - Enjoy your stay! - - [prefs]: %{user_preferences_url} backup_succeeded: subject_template: "Backup completed successfully" text_body_template: "The backup was successful.\nVisit the [admin > backup section](/admin/backups) to download your new backup." @@ -1291,17 +1109,6 @@ sq: ``` %{logs} ``` - csv_export_succeeded: - subject_template: "Data Export completed successfully" - text_body_template: | - The data export was successful. - - Download CSV file: %{file_name} - - CSV file download link will expire after 48 hours. - csv_export_failed: - subject_template: "Export failed" - text_body_template: "The export has failed. Please check the logs." email_reject_trust_level: subject_template: "Email issue -- Insufficient Trust Level" text_body_template: | @@ -1540,21 +1347,6 @@ sq: %{base_url}/users/authorize-email/%{email_token} signup_after_approval: subject_template: "You've been approved on %{site_name}!" - text_body_template: | - Welcome to %{site_name}! - - A staff member approved your account on %{site_name}. - - Click the following link to confirm and activate your new account: - %{base_url}/users/activate-account/%{email_token} - - If the above link is not clickable, try copying and pasting it into the address bar of your web browser. - - %{new_user_tips} - - We believe in [civilized community behavior](%{base_url}/guidelines) at all times. - - Enjoy your stay! signup: subject_template: "[%{site_name}] Activate your new account" text_body_template: | @@ -1584,10 +1376,7 @@ sq: unauthorized: "Sorry, the file you are trying to upload is not authorized (authorized extensions: %{authorized_extensions})." pasted_image_filename: "Pasted image" store_failure: "Failed to store upload #%{upload_id} for user #%{user_id}." - attachments: - too_large: "Sorry, the file you are trying to upload is too big (maximum size is %{max_size_kb}%kb)." images: - too_large: "Sorry, the image you are trying to upload is too big (maximum size is %{max_size_kb}%kb), please resize it and try again." fetch_failure: "Sorry, there has been an error while fetching the image." unknown_image_type: "Sorry, but the file you tried to upload doesn't appear to be an image." size_not_found: "Sorry, but we couldn't determine the size of the image. Maybe your image is corrupted?" @@ -1617,98 +1406,6 @@ sq: Edit the first post in this topic to change the contents of the %{page_name} page. guidelines_topic: title: "FAQ/Guidelines" - body: | - - - ## [This is a Civilized Place for Public Discussion](#civilized) - - Please treat this discussion forum with the same respect you would a public park. We, too, are a shared community resource — a place to share skills, knowledge and interests through ongoing conversation. - - These are not hard and fast rules, merely aids to the human judgment of our community. Use these guidelines to keep this a clean, well-lighted place for civilized public discourse. - - - - ## [Improve the Discussion](#improve) - - Help us make this a great place for discussion by always working to improve the discussion in some way, however small. If you are not sure your post adds to the conversation, think over what you want to say and try again later. - - The topics discussed here matter to us, and we want you to act as if they matter to you, too. Be respectful of the topics and the people discussing them, even if you disagree with some of what is being said. - - One way to improve the discussion is by discovering ones that are already happening. Please spend some time browsing the topics here before replying or starting your own, and you’ll have a better chance of meeting others who share your interests. - - - - ## [Be Agreeable, Even When You Disagree](#agreeable) - - You may wish to respond to something by disagreeing with it. That’s fine. But, remember to _criticize ideas, not people_. Please avoid: - - * Name-calling. - * Ad hominem attacks. - * Responding to a post’s tone instead of its actual content. - * Knee-jerk contradiction. - - Instead, provide reasoned counter-arguments that improve the conversation. - - - - ## [Your Participation Counts](#participate) - - The conversations we have here set the tone for everyone. Help us influence the future of this community by choosing to engage in discussions that make this forum an interesting place to be — and avoiding those that do not. - - Discourse provides tools that enable the community to collectively identify the best (and worst) contributions: favorites, bookmarks, likes, flags, replies, edits, and so forth. Use these tools to improve your own experience, and everyone else’s, too. - - Let’s try to leave our park better than we found it. - - - - ## [If You See a Problem, Flag It](#flag-problems) - - Moderators have special authority; they are responsible for this forum. But so are you. With your help, moderators can be community facilitators, not just janitors or police. - - When you see bad behavior, don’t reply. It encourages the bad behavior by acknowledging it, consumes your energy, and wastes everyone’s time. _Just flag it_. If enough flags accrue, action will be taken, either automatically or by moderator intervention. - - In order to maintain our community, moderators reserve the right to remove any content and any user account for any reason at any time. Moderators do not preview new posts in any way; the moderators and site operators take no responsibility for any content posted by the community. - - - - ## [Always Be Civil](#be-civil) - - Nothing sabotages a healthy conversation like rudeness: - - * Be civil. Don’t post anything that a reasonable person would consider offensive, abusive, or hate speech. - * Keep it clean. Don’t post anything obscene or sexually explicit. - * Respect each other. Don’t harass or grief anyone, impersonate people, or expose their private information. - * Respect our forum. Don’t post spam or otherwise vandalize the forum. - - These are not concrete terms with precise definitions — avoid even the _appearance_ of any of these things. If you’re unsure, ask yourself how you would feel if your post was featured on the front page of the New York Times. - - This is a public forum, and search engines index these discussions. Keep the language, links, and images safe for family and friends. - - - - ## [Keep It Tidy](#keep-tidy) - - Make the effort to put things in the right place, so that we can spend more time discussing and less cleaning up. So: - - * Don’t start a topic in the wrong category. - * Don’t cross-post the same thing in multiple topics. - * Don’t post no-content replies. - * Don’t divert a topic by changing it midstream. - * Don’t sign your posts — every post has your profile information attached to it. - - Rather than posting “+1” or “Agreed”, use the Like button. Rather than taking an existing topic in a radically different direction, use Reply as a New Topic. - - - - ## [Post Only Your Own Stuff](#stealing) - - You may not post anything digital that belongs to someone else without permission. You may not post descriptions of, links to, or methods for stealing someone’s intellectual property (software, video, audio, images), or for breaking any other law. - - - - ## [Terms of Service](#tos) - - Yes, legalese is boring, but we must protect ourselves – and by extension, you and your data – against unfriendly folks. We have a [Terms of Service](/tos) describing your (and our) behavior and rights related to content, privacy, and laws. To use this service, you must agree to abide by our [TOS](/tos). tos_topic: title: "Terms of Service" body: | @@ -1930,25 +1627,3 @@ sq: If we decide to change our privacy policy, we will post those changes on this page. This document is CC-BY-SA. It was last updated May 31, 2013. - static: - search_help: | -

Tips

-

-

    -
  • Title matches are prioritized, so when in doubt, search for titles
  • -
  • Unique, uncommon words will always produce the best results
  • -
  • Whenever possible, scope your search to a particular category, user, or topic
  • -
-

-

Options

-

- - - - - -
order:viewsorder:latest
status:openstatus:closedstatus:archivedstatus:norepliesstatus:singleuser
category:foouser:foo
in:likesin:postedin:watchingin:tracking
-

-

- rainbows category:parks status:open order:latest will search for topics containing the word "rainbows" in the category "parks" that are not closed or archived, ordered by date of last post. -

diff --git a/config/locales/server.sv.yml b/config/locales/server.sv.yml index d8dcfbe6f3..fa90ac6bd0 100644 --- a/config/locales/server.sv.yml +++ b/config/locales/server.sv.yml @@ -10,11 +10,6 @@ sv: short_date_no_year: "D MMM" short_date: "D MMM, YYYY" long_date: "MMMM D, YYYY h:mma" - time: - formats: - short: "%m-%d-%Y" - short_no_year: "%B %-d" - date_only: "%b %-d, %Y" title: "Discourse" topics: "Trådar" posts: "inlägg" @@ -23,18 +18,37 @@ sv: log_in: "Logga in" via: "%{username} via %{site_name}" is_reserved: "är reserverat" - purge_reason: "Automatiskt borttagen på grund av att vara gammal och ej verifierad" disable_remote_images_download_reason: "Fjärrbilds nedladdning är inaktiverad eftersom det inte fanns tillräckligt mycket lagringsutrymme tillgängligt." errors: messages: too_long_validation: "är begränsad till %{max} karaktärer; du skrev in %{length}. " invalid_boolean: "Ogiltigt boolean." taken: "används redan" + accepted: måste accepteras + blank: kan inte vara blankt + present: måste vara tomt + confirmation: "matchar inte %{attribute}" + empty: kan inte vara tomt + equal_to: måste vara lika med %{count} + even: måste vara jämnt + exclusion: är reserverad + greater_than: måste vara större än %{count} + greater_than_or_equal_to: måste vara större eller lika med %{count} + inclusion: är inte inkluderad i listan + invalid: är ogiltig + less_than: måste vara mindre än %{count} + less_than_or_equal_to: måste vara mindre eller lika med %{count} + not_a_number: är inte ett nummer + not_an_integer: måste vara ett tal + odd: måste vara udda + template: + body: 'Det var ett problem med dom följande fälten:' embed: load_from_remote: "Det uppstod ett fel vid laddning av artikeln." bulk_invite: file_should_be_csv: "Den uppladdade filen ska vara i csv eller txt format." backup: + operation_already_running: "En operation är redan igång. Kan inte starta ett nytt job just nu." backup_file_should_be_tar_gz: "Backup filen ska vara ett .tar.gz arkiv." not_enough_space_on_disk: "Det finns inte tillräckligt mycket utrymme på disken för att ladda upp denna backup." not_logged_in: "Du måste vara inloggad för att göra detta." @@ -96,6 +110,7 @@ sv: latest: "Senaste trådar" hot: "Heta trådar" too_late_to_edit: "Inlägget skapades för långt tillbaka i tiden. Det kan inte lägre redigeras eller tas bort." + excerpt_image: "bild" groups: errors: can_not_modify_automatic: "Du kan inte modifiera en automatisk grupp" @@ -138,26 +153,45 @@ sv: models: topic: attributes: - archetype: - cant_send_pm: "Tyvärr, du kan inte skicka ett privat meddelande till den användaren." + base: + cant_send_pm: "Tyvärr, du kan inte skicka ett privat medelande till den användaren. " + no_user_selected: "Du måste välja en giltig användare." user: attributes: password: common: "är ett av de 10000 vanligaste lösenorden. Vänligen använd ett säkrare lösenord." ip_address: signup_not_allowed: "Registration är inte tillåtet från detta konto." + color_scheme_color: + attributes: + hex: + invalid: "är inte en giltig färg" user_profile: no_info_me: "
Din profils Om Mig-fält är för närvarande tomt, skulle du vilja fylla i det?
" no_info_other: "
%{name} har inte skrivit någonting i dess profils Om Mig-fält ännu
" vip_category_name: "Lounge" + staff_category_name: "Personal" + staff_category_description: "Privat kategori för personal diskussioner. Ämnen här är bara synliga till administratörer och moderatorer." category: post_template: "%{replace_paragraph}\n\nUse the following paragraphs for a longer description, as well as to establish any category guidelines or rules.\n\nSome things to consider in any discussion replies below:\n\n- What is this category for? Why should people select this category for their topic?\n\n- How is this different than the other categories we already have?\n\n- Do we need this category?\n\n- Should we merge this with another category, or split it into more categories?\n" + errors: + uncategorized_parent: "Kategorin \"Okategoriserat\" kan inte ha en föräldrar kategori." + cannot_delete: + uncategorized: "Du kan inte ta bort kategorin \"Okategoriserat\"" + has_subcategories: "Du kan inte ta bort den här kategorin för att den har under-kategorier." trust_levels: newuser: title: "besökare" basic: title: "vanlig användare" + regular: + title: "medlem" + leader: + title: "vanlig" + elder: + title: "ledare" rate_limiter: + slow_down: "Du har utfört den här handlingen för många gånger, var vänligen och försök igen senare." too_many_requests: "Du gör det där för ofta. Var god vänta %{time_left} innan du försöker igen." hours: one: "1 timme" @@ -336,6 +370,10 @@ sv: title: "Bokmärken" xaxis: "Dag" yaxis: "Antal nya bokmärken" + starred: + title: "Favoriserad" + xaxis: "Dag" + yaxis: "Antal favoriserade trådar" users_by_trust_level: title: "Users per Trust Level" xaxis: "Trust Level" @@ -379,6 +417,34 @@ sv: title: "Mest refererade trådar" xaxis: "Tråd" num_clicks: "Klick" + page_view_anon_reqs: + title: "Anonym" + xaxis: "Dag" + yaxis: "Anonyma sidvisningar" + page_view_logged_in_reqs: + title: "Inloggad" + xaxis: "Dag" + yaxis: "Inloggade sidvisningar" + page_view_crawler_reqs: + xaxis: "Dag" + page_view_total_reqs: + title: "Total" + xaxis: "Dag" + yaxis: "Totala Sidvisningar" + http_background_reqs: + title: "Bakgrund" + xaxis: "Dag" + http_2xx_reqs: + xaxis: "Dag" + http_3xx_reqs: + xaxis: "Dag" + http_4xx_reqs: + xaxis: "Dag" + http_5xx_reqs: + xaxis: "Dag" + http_total_reqs: + title: "Totala" + xaxis: "Dag" dashboard: rails_env_warning: "Your server is running in %{env} mode." ruby_version_warning: "Du använder en version av Ruby 2.0.0 som är känd för att ha problem. Uppgradera till patch-nivå 247 eller senare." @@ -388,10 +454,6 @@ sv: queue_size_warning: 'Antalet köade jobb är %{queue_size}, vilket är högt. Det skulle kunna indikera ett problem med Sidekiq-processen/-erna, eller så kanske du behöver lägga till fler Sidekiq workers.' memory_warning: 'Your server is running with less than 1 GB of total memory. At least 1 GB of memory is recommended.' s3_config_warning: 'Servern är konfigurerad till att ladda upp filer till s3, men minst en av följande inställningar är inte satt: s3_access_key_id, s3_secret_access_key eller s3_upload_bucket. Gå till Inställningar och uppdatera dem. Se "How to set up image uploads to S3?" för att lära dig mer.' - default_logo_warning: "Du har inte valt egna logotyper för din sida. Uppdatera logo_url, logo_small_url, och favicon_url under Inställningar." - contact_email_missing: "Du har inte lagt till en kontaktadress för din sida. Vänligen uppdatera contact_email under Inställningar." - contact_email_invalid: "Sidans kontaktadress är ogiltig. Vänligen uppdatera contact_email under Inställningar." - title_nag: "Inställningen för titel är fortfarande satt till standardvärdet. Vänligen uppdatera den med din sidas titel under Inställningar." consumer_email_warning: "Din sida är konfigurerad till att använda Gmail (eller någon annan konsumenttjänst) för att skicka email. Gmail limits how many emails you can send. Consider using an email service provider like mandrill.com to ensure email deliverability." content_types: education_new_reply: @@ -451,6 +513,13 @@ sv: faq_url: "Om du har en FAQ någon annanstans som du vill använda, skriv den fullständiga URL:en här." privacy_policy_url: "Om du har ett dokument med en integritetspolicy någon annanstans som du vill använda, skriv den fullständiga URL:en här." default_digest_email_frequency: "Hur ofta användare får emailutskick som standard. De kan ändra detta val under sina inställningar." + errors: + invalid_email: "Felaktig e-postadress." + invalid_username: "Det finns ingen användare med detta användarnamn." + invalid_integer_min_max: "Värdet måste vara mellan %{min} och %{max}." + invalid_integer_min: "Värdet måste vara %{max} eller större." + invalid_integer_max: "Värdet får inte vara större än %{max}." + invalid_integer: "Värdet måste vara ett heltal." notification_types: mentioned: "%{display_username} mentioned you in %{link}" liked: "%{display_username} liked your post in %{link}" @@ -492,7 +561,6 @@ sv: errors: "%{errors}" not_available: "Not available. Try %{suggestion}?" something_already_taken: "Something went wrong, perhaps the username or email is already registered. Try the forgot password link." - omniauth_error: "Sorry, there was an error authorizing your %{strategy} account. Perhaps you did not approve authorization?" omniauth_error_unknown: "Something went wrong processing your log in, please try again." user: username: @@ -513,6 +581,10 @@ sv: subject_template: "Välkommen till %{site_name}!" welcome_invite: subject_template: "Välkommen till %{site_name}!" + restore_succeeded: + text_body_template: "Återställningen lyckades." + restore_failed: + subject_template: "Återställningen misslyckades" too_many_spam_flags: subject_template: "Nytt konto blockerat" blocked_by_staff: @@ -601,6 +673,8 @@ sv: signup: subject_template: "[%{site_name}] Activate your new account" text_body_template: "Välkommen till %{site_name}!\n\nKlicka på följande länk för att bekräfta och aktivera ditt nya konto:\n%{base_url}/users/activate-account/%{email_token}\n\nOm länken ovan inte går att klicka på kan du kopiera och klistra in länken i adressfältet i din webbläsare. \n" + page_not_found: + see_more: "Mer" deleted: 'raderad' upload: pasted_image_filename: "Inklistrad bild" diff --git a/config/locales/server.te.yml b/config/locales/server.te.yml new file mode 100644 index 0000000000..71f81f8bd4 --- /dev/null +++ b/config/locales/server.te.yml @@ -0,0 +1,753 @@ +# encoding: utf-8 +# +# Never edit this file. It will be overwritten when translations are pulled from Transifex. +# +# To work with us on translations, join this project: +# https://www.transifex.com/projects/p/discourse-org/ + +te: + stringex: + characters: + number: "-" + i18n: + transliterate: + rule: + ț: "t" + Ț: "t" + ș: "s" + Ș: "s" + dates: + short_date_no_year: "D MMM" + short_date: "D MMM, YYYY" + long_date: "MMMM D, YYYY h:mma" + title: "డిస్కోర్స్" + topics: "విషయాలు" + posts: "టపాలు" + loading: "లోడవుతోంది" + powered_by_html: ' డిస్కౌర్స్ చేత శక్తివంతం చేయబడింది, జావాస్క్రిప్ట్ చేతనం చేస్తే బాగా కనిపిస్తుంది. ' + log_in: "లాగిన్" + via: "%{site_name} నుండి %{username}" + is_reserved: "రక్షితము" + purge_reason: "వదిలేసినదిగా స్వీయంగా కనుగొను, చేతనం చేయని ఖాతా" + disable_remote_images_download_reason: "సుదూర బొమ్మల దిగుమతి అచేతనమైంది ఎందుకంటే డిస్క్ జాగా తక్కువగా ఉంది." + errors: + format: '%{attribute} %{message}' + messages: + too_long_validation: "%{max} అక్షరాలకే పరిమితము. మీరు %{length} అక్షరాలు రాసారు." + invalid_boolean: "చెల్లని బూలియన్" + taken: "ఇప్పటికే తీసుకొన్నారు" + accepted: తప్పనిసరి ఒప్పుకోవాలి + blank: ఖాళీగా ఉండకూడదు + present: తప్పనిసరిగా ఖాళీగా ఉండాలి + confirmation: "%{attribute} కు జత అవ్వలేదు " + empty: ఖాళీ ఉండకూడదు + equal_to: తప్పనిసరిగా %{count} కు సమానంగా ఉండాలి + even: జతగా ఉండాలి + exclusion: రక్షితము + greater_than: ' %{count} కన్నా ఎక్కువ ఉండాలి' + greater_than_or_equal_to: '%{count} కన్నా సమానం లేదా ఎక్కువ ఉండాలి' + inclusion: జాబితాలో జతపర్చబడలేదు + invalid: చెల్లనిది + less_than: '%{count} కన్నా తక్కువ ఉండాలి' + less_than_or_equal_to: '%{count} కన్నా సమానం లేదా తక్కువగా ఉండాలి' + not_a_number: సంఖ్య కాదు + not_an_integer: తప్పనిసరిగా పూర్ణ సంఖ్య అయి ఉండాలి + odd: తప్పనిసరి బేసి సంఖ్య కావాలి + record_invalid: 'సరిజూత విఫలమైంది: %{errors}' + restrict_dependent_destroy: + one: "రికార్డు తొలగించలేము. ఎందుకంటే ఆధారిత %{record} ఉంది" + many: "రికార్డు తొలగించలేము. ఎందుకంటే ఆధారిత %{record} ఉంది" + too_long: + one: ఇది చాలా పొడుగు (గరిష్ట పరిమితి ఒక అక్షరం) + other: ఇది చాలా పొడుగు. (గరిష్ట పరిమితి %{count} అక్షరాలు) + too_short: + one: ఇది చాలా పొట్టి (కనీస పరిమితి ఒక అక్షరం) + other: ఇది చాపా పొట్టి (కనీస పరిమితి %{count} అక్షరాలు) + wrong_length: + one: ఇది సరైన పొడుగు కాదు (ఒక అక్షరం మాత్రమే ఉండాలి) + other: సరైన పొడుగు కాదు. (తప్పనిసరి %{count} అక్షరాలు ఉండాలి) + other_than: "%{count} కాకుండా వేరే అవ్వాలి" + template: + body: 'ఈ దిగువ క్షేత్రాలతో సమస్య ఉంది' + header: + one: ఒక దోషం వల్ల ఈ %[model] భద్రపరుచుట వీలవలేదు + other: '%{count} దోషాల వల్ల %{model} భద్రపరుచుట వీలవలేదు' + embed: + load_from_remote: "ఈ టపా లోడింగులో దోషం" + bulk_invite: + file_should_be_csv: "ఎగుమతించే దస్త్రం కేవలం csv లేదా txt రూపంలో ఉండాలి" + backup: + operation_already_running: "ఒక పరిక్రియ ప్రస్తుతం జరుగుతోంది. కొత్త పని ఇప్పుడు మొదలుపెట్టవీలవదు." + backup_file_should_be_tar_gz: "బ్యాకప్ దస్త్రం తప్పనిసరి .tar.gz కట్ట అయి ఉండాలి." + not_enough_space_on_disk: "ఈ బ్యాకప్ ఎగుమతించడానికి సరిపోవు జాగా డిస్కుపై లేదు." + not_logged_in: "మీరు లాగిన్ అయి ఉండాలి. " + read_only_mode_enabled: "సైటు కేవలం చదివే రీతిలో ఉంది. చర్చలు ఇప్పుడు అచేతనం." + too_many_replies: + one: "క్షమించాలి. కొత్త సభ్యులు తాత్కాలికంగా ఒక విషయానికి ఒక జవాబుకు మాత్రమే పరిమితం చేయబడ్డారు" + other: "క్షమించాలి. కొత్త సభ్యులు, తాత్కాలికంగా ఒక విషయానికి %{count} జవాబులకు మాత్రమే పరిమితం చెయ్యబడ్డారు." + embed: + start_discussion: "చర్చ మొదలుపెట్టు" + continue: "చర్చ కొనసాగించు" + more_replies: + one: "మరో జవాబు ఉంది" + other: "ఇంకా %{count} జవాబులు ఉన్నాయి" + loading: "చర్చ లోడవుతోంది" + permalink: "శాస్వత లంకె" + imported_from: "%{link} వద్ద మూల పద్దుకు జోడు చర్చా విషయం ఉంది." + in_reply_to: "▶ %{username}" + replies: + one: "ఒక జవాబు" + other: "%{count} జవాబులు" + too_many_mentions: + zero: "క్షమించాలి. మీరు ఇతర సభ్యులను ప్రస్తావించలేరు" + one: "క్షమించాలి. మీరు ఒక టపాలో ఒక సభ్యుడిని మాత్రమే ప్రస్తావించవచ్చు" + other: "క్షమించాలి. మీరు ఒక టపాలో కేవలం %[count] సభ్యులను మాత్రమే ప్రస్తావించవచ్చు" + too_many_mentions_newuser: + zero: "క్షమించాలి. కొత్త సభ్యులు ఇతర సభ్యులను ప్రస్తావించలేరు." + one: "క్షమించాలి. కొత్త సభ్యులు ఒక టపాలో కేవలం ఒక సభ్యుడిని మాత్రమే ప్రస్తావించవచ్చు." + other: "క్షమించాలి. కొత్త సభ్యులు ఒక టపాలో కేవలం %{count} సభ్యులను మాత్రమే ప్రస్తావించవచ్చు." + too_many_images: + zero: "క్షమించాలి. కొత్త సభ్యులు టపాలో బొమ్మలు ఉంచలేరు." + one: "క్షమించాలి. కొత్త సభ్యులు టపాలో ఒక బొమ్మ మాత్రమే ఉంచవచ్చు." + other: "క్షమించాలి. కొత్త సభ్యులు ఒక టపాలో కేవలం %{count} బొమ్మలను మాత్రమే ఉంచవచ్చు." + too_many_attachments: + zero: "క్షమించాలి. కొత్త సభ్యులు టపాలో జోడింపులు ఉంచవీలవదు." + one: "క్షమించాలి. కొత్త సభ్యులు ఒక టపాలో కేవలం ఒక జోడింపు మాత్రమే ఉంచ వీలవుతుంది." + other: "క్షమించాలి. కొత్త సభ్యులు ఒక టపాలో కేవలం %{count} జోడింపులు మాత్రమే ఉంచ వీలవుతుంది." + too_many_links: + zero: "క్షమించాలి, కొత్త సభ్యులు టపాలో లంకెలు ఉంచవీలవదు." + one: "క్షమించాలి. కొత్త సభ్యులు ఒక టపాలో కేవలం ఒక లంకె మాత్రమే ఉంచ వీలవుతుంది." + other: "క్షమించాలి. కొత్త సభ్యులు ఒక టపాలో కేవలం %[count] లంకెలు మాత్రమే ఉంచ వీలవుతుంది." + spamming_host: "క్షమించాలి. ఆ అతిధికి మీరు లంకె టపా చెయ్యలేరు." + user_is_suspended: "సస్పెండైన సభ్యులు టపా రాయుట వీలవదు" + just_posted_that: "ఇది ఇప్పుడే రాసిన విషయంలా ఉంది. డూప్లికేటు టపా?" + has_already_been_used: "ఇప్పటికే వాడుకలో ఉంది." + invalid_characters: "చెల్లని అక్షరాలు ఉన్నాయి." + is_invalid: "చెల్లనిది. మరింత వివరణాత్మకంగా ఉండేట్టు చూడండి." + next_page: "తరువాతి పుట →" + prev_page: "← గత పుట" + page_num: "పుట %[num]" + topics_in_category: "'%{category}' వర్గంలోని విషయాలు" + rss_posts_in_topic: "'%{topic}' యొక్క ఆర్ యస్ యస్ వడ్డన" + author_wrote: "%{author} రాసారు:" + num_posts: "టపాలు:" + num_participants: "భాగస్వాములు:" + read_full_topic: "పూర్తి విషయం చదువు" + private_message_abbrev: "PM" + rss_description: + latest: "తాజా విషయాలు" + hot: "వేడివేడి విషయాలు" + too_late_to_edit: "ఈ టపా పురాతన కాలంలో సృష్టించబడింది. ఇప్పుడు ఇహ సవరించలేము లేదా తొలగించలేము." + excerpt_image: "బొమ్మ" + groups: + errors: + can_not_modify_automatic: "మీరు ఒక ఆటోమేటిక్ గుంపును మార్చలేరు" + default_names: + everyone: "ప్రతీఒక్కరూ" + admins: "అధికారులు" + moderators: "నిర్వాహకులు" + staff: "సిబ్బంది" + trust_level_0: "నమ్మకం_స్థాయి_0" + trust_level_1: "నమ్మకం_స్థాయి_1" + trust_level_2: "నమ్మకం_స్థాయి_2" + trust_level_3: "నమ్మకం_స్థాయి_3" + trust_level_4: "నమ్మకం_స్థాయి_4" + education: + until_posts: + one: "ఒక టపా" + other: "%{count} టపాలు" + new-topic: "స్వాగతం %{site_name} — **కొత్త సంభాషణ మొదలు పెట్టినందుకు ధన్యవాదాలు!**\n\n\n- శీర్షిక ఖచ్చితంగా మీ విషయాన్ని వివరిస్తుందా? ఆసక్తికరంగా ఉందా? \n\n\n\n- ఏ విషయం గురించి ? అది ఆసక్తిగా ఉంటుందా ? ఈ విషయం ఎందుకు ? ఈ సముదాయం నుండి ఏ విధమైన స్పందనలు ఆశిస్తున్నారు ?\n\n\n\n- మీ అంశంలో ఇతరులు శోధించడానికి వీలుగా మంచిశోధన పదాలు కలపండి.మీఅంశాన్ని సంబంధిత\ + \ అంశాలకు జతపరచండి, వర్గాన్ని ఎంచుకోండి.\n\n\n\nమరింత సమాచారం కోసం ,[వర్గ మార్గదర్శకసూత్రాలను చూడండి](/guidelines).ఈ పట్టిక మొదట మీకే కనిపిస్తుంది \n%{education_posts_text}.\n\n\n" + new-reply: | + స్వాగతం %{site_name} — **పాల్గొన్నందుకు ధన్యవాదాలు!** + + - మీ సమాధానం సంభాషణను కొంత మెరుగు పరిచిందా ? + + - తోటి కమ్యూనిటి సభ్యులతో మర్యాదగా ఉండండి . + + - నిర్మాణాత్మక విమర్శలకు స్వాగతం, కానీ *ఆలోచన*లను విమర్శించండి,వ్యక్తులని కాదు. + + + మరింత సమాచారం కోసం ,[వర్గ మార్గదర్శక సూత్రాలను చూడండి](/guidelines).ఈ పట్టిక మొదట మీకే కనిపిస్తుంది%{education_posts_text}. + avatar: | + ###మీ ఖాతాకి ఒక కొత్త చిత్రం గురించి? + + మీరు కొన్ని విషయాలు మరియు ప్రత్యుత్తరాలను పోస్ట్ చేసారు, కానీ మీ అవతార్ మీలాగా ఏకైకం కాదు - ఇది అందరూ కొత్త వినియోగదారులు అదే అప్రమేయ అవతార్ అవుతుంది. + + మీరు ఆలోచించారా **[మీ వినియోగదారు ప్రొఫైల్ సందర్శించడం](%{profile_path})** మరియు మీరు ఎగుమతి చేసిన చిత్రం మిమ్మల్ని సూచిస్తుందా ? + + ప్రతి ఒక్కరూ ఒక ఏకైక అవతార్ కలిగిఉన్నప్పుడు సంఘ చర్చలు జరపడానికి మరియు సభ్యుల ఆసక్తికరమైన సంభాషణలు కనుగొనేందుకు ఇది చాలా సులభం ! + activerecord: + attributes: + category: + name: "వర్గం పేరు" + post: + raw: "శరీరం" + user_profile: + bio_raw: "నా గురించి" + errors: + messages: + is_invalid: "చెల్లనిది. మరింత వివరణాత్మకంగా ఉండటానికి ప్రయత్నించండి" + has_already_been_used: "ఇప్పటికే వాడుకలో ఉంది" + models: + topic: + attributes: + base: + warning_requires_pm: "ప్రైవేటు సందేశాలకు మీరు కేవలం హెచ్చరికలు మాత్రమే జోడించగలరు" + too_many_users: "మీరు హెచ్చరికలు ఒక పర్యాయం కేవలం ఒక సభ్యునికి మాత్రమే పంపగలరు." + cant_send_pm: "క్షమించాలి. ఆ సభ్యునికి మీరు ప్రైవేటు సందేశం పంపలేరు." + no_user_selected: "మీరు ఒక సభ్యుడిని ఎంచుకోవాలి." + user: + attributes: + password: + common: "ఇది పదివేలు తరచూ వాడే సంకేతపదాలలో ఒకటి. దయచేసి మరింత భద్రమైన సంకేతపదం వాడండి." + ip_address: + signup_not_allowed: "ఈ ఖాతాకు సైన్ అప్ అనుమతించబడదు" + color_scheme_color: + attributes: + hex: + invalid: "ఇది చెల్లని రంగు" + user_profile: + no_info_other: "
%{పేరు} ఇంకా వారి ప్రొఫైల్ లో ఎబౌట్ మీ స్థానంలో ఏమీ చేర్చలేదు
" + vip_category_name: "అడ్డా" + vip_category_description: "నమ్మకం స్థాయి మూడు లేదా ఆపై ఉన్న సభ్యులకు మాత్రమే పరిమితమైన వర్గం" + meta_category_name: "మెటా" + meta_category_description: "ఈ సైటు గురించి చర్చ. ఎలా మొదలైంది. ఎలా పని చేస్తుంది. ఇంకా ఎలా మెరుగు పరచవచ్చు." + staff_category_name: "సిబ్బంది" + staff_category_description: "సిబ్బంది చర్చలకు ప్రైవేటు వర్గం. విషయాలు కేవలం అధికారులకు మరియు నిర్వాహకులకు మాత్రమే కనిపిస్తాయి." + lounge_welcome: + title: "అడ్డాకు స్వాగతం." + category: + topic_prefix: "%{category} వర్గం గురించి" + errors: + uncategorized_parent: "అవర్గీకృతానికి తండ్రి వర్గం ఉండకూడదు" + self_parent: "ఉప వర్గపు తండ్రి అదే వర్గం అవ్వకూడదు" + depth: "మీరు ఒక ఉపవర్గాన్ని మరోదాని కింద ఉంచకూడదు" + cannot_delete: + uncategorized: "అవర్గీకృతాన్ని తొలగించలేరు" + has_subcategories: "ఈ వర్గాన్ని తొలగించలేరు. ఎందుకంటే దీనికి ఉప వర్గాలు ఉన్నాయి." + topic_exists: + one: "ఈ వర్గాన్ని తొలగించలేరు. ఎందుకంటే దీనిలో ఒక విషయం ఉంది. పురాతన విషయం %{topic_link}." + other: "ఈ వర్గాన్ని తొలగించలేరు. ఎందుకంటే దీనిలో %[count] విషయాలు ఉన్నాయి. పురాతన విషయం %{topic_link}." + topic_exists_no_oldest: "ఈ వర్గాన్ని తొలగించలేరు. ఎందుకంటే దీనిలో %{count} విషయాలు ఉన్నాయి." + trust_levels: + newuser: + title: "కొత్త సభ్యుడు" + basic: + title: "ప్రాథమిక సభ్యుడు" + regular: + title: "మెంబరు" + leader: + title: "రెగ్యులర్" + elder: + title: "లీడర్" + rate_limiter: + slow_down: "ఈ చర్చ మీరు చాలాసార్లు జరిపారు. కొంతసేపటితర్వాత ప్రయత్నించండి" + too_many_requests: "ఈ చర్యకు రోజువారీపరిమితి ఉంది. దయచేసి %{time_left} సమయం తర్వాత ప్రయత్నించండి" + hours: + one: "ఒక గంట" + other: "%{count} గంటలు" + minutes: + one: "ఒక నిమిషం" + other: "%{count} నిమిషాలు" + seconds: + one: "ఒక సెకను" + other: "%{count} సెకన్లు" + datetime: + distance_in_words: + half_a_minute: "< 1ని" + less_than_x_seconds: + one: "< 1సె" + other: "<%{count}సె" + x_seconds: + one: "1సె" + other: "%{count}సె" + less_than_x_minutes: + one: "< 1ని" + other: "< %{count}ని" + x_minutes: + one: "1ని" + other: "%{count}ని" + about_x_hours: + one: "1గం" + other: "%{count}గం" + x_days: + one: "1రో" + other: "%{count}రో" + about_x_months: + one: "1నెల" + other: "%{count}నెల" + x_months: + one: "1నెల" + other: "%{count}నెల" + about_x_years: + one: "1సం" + other: "%{count}సం" + over_x_years: + one: "> 1సం" + other: "> %{count}సం" + almost_x_years: + one: "1సం" + other: "%{count}సం" + distance_in_words_verbose: + half_a_minute: "ఇప్పుడే" + less_than_x_seconds: + one: "ఇప్పుడే" + other: "ఇప్పుడే" + x_seconds: + one: "1 సెకన్ వెనుక" + other: "%{count} సెకన్ల వెనుక" + less_than_x_minutes: + one: "ఒక్క నిమిషం ముందు" + other: "%{count} నిమిషాల ముందు" + x_minutes: + one: "ఒక్క నిమిషం ముందు" + other: "%{count} నిమిషాలు ముందు" + about_x_hours: + one: "1 గంట ముందు" + other: "%{count} గంటల ముందు" + x_days: + one: "1 రోజు ముందు" + other: "%{count} రోజుల ముందు" + about_x_months: + one: "రమారమీ ఒక మాసం ముందు" + other: "రమారమీ %{count} నెలల ముందు" + x_months: + one: "ఒక మాసం ముందు" + other: "%{count} నెలల వెనుక" + about_x_years: + one: "రమారమీ ఒక సంవత్సరం ముందు" + other: "రమారమీ %{count} సంవత్సరాల క్రితం" + over_x_years: + one: "ఒక సంవత్సరం పైబడి" + other: "%{count} సవంత్సరాలకు పైబడి" + almost_x_years: + one: "అటో ఇటో ఒక సంవత్సరం వెనుక" + other: "అటోఇటో %{count} సంవత్సరాల ముందు" + password_reset: + no_token: "క్షమించాలి, మీ టోకెన్ గడువుతీరింది. దయచేసి సంకేతపదం మరలా రీసెట్ చేయప్రయత్నించండి." + choose_new: "దయచేసి కొత్త సంకేతపదం ఎన్నుకోండి" + choose: "దయచేసి ఒక సంకేతపదం ఎన్నుకోండి" + update: 'సంకేతపదం ఉన్నతీకరించండి' + save: 'సంకేతపదం అమర్చండి' + title: 'సంకేతపదం రీసెట్ చెయ్యండి' + success: "మీరు విజయవంతంగా మీ సంకేతపదం మార్చారు ఇంకా ఇప్పుడు లాగిన్ అయ్యారు కూడా." + success_unapproved: "మీరు విజయవంతంగా సంకేతపదం మార్చారు." + continue: "%{site_name} కు కొనసాగండి" + change_email: + confirmed: "మీ ఈమెయిల్ చిరునామా మారింది." + please_continue: "%{site_name} కు కొనసాగండి" + error: "మీ ఈమెయిల్ చిరునామా మార్చడంలో దోషం. బహుశా ఆ చిరునామా ఈసరికే వాడుకలో ఉందా?" + activation: + action: "మీ ఖాతాను చేతనం చేయండి" + already_done: "క్షమించాలి. ఖాతా ధృవపరుచు లంకె కాలాతీతమైంది. బహుశా మీ ఖాతా ఇప్పటికే చేతనమై ఉందేమో?" + please_continue: "మీ ఖాతా ధృవపర్చబడింది. మీరిప్పుడు తొలిపుటకు మళ్లించబడతారు." + continue_button: "%{site_name} కు కొనసాగండి." + welcome_to: "%{site_name} కు సుస్వాగతం!" + approval_required: "ఈ ఫోరమ్ మీరు వాడేముందు ఒక నిర్వాహకుడు మీ ఖాతాను అంగీకరించాలి. మీ ఖాతా అంగీకరించగానే మీకు ఒక ఈమెయిల్ వస్తుంది." + post_action_types: + off_topic: + title: 'విషయాంతరం' + description: ' శీర్షిక మరియు తొలి టపా ప్రకారం ఈ టపా ప్రస్తుత చర్చకు సంబంధంలేనిది. బహుశా వేరేచోటుకు తరలించాలేమో.' + long_form: 'దీన్ని విషయాంతరంగా కేతనించాము' + spam: + title: 'స్పాము' + description: 'ఈ టపా వాణిజ్య ప్రకటన. ప్రస్తుత విషయానికి ఉపయోగకరమైనది కాదు, సంబందించినదీ కాదు. కానీ మార్కెటింగు స్వభావితమైనది.' + long_form: 'దీన్ని స్పాముగా కేతనించాము' + email_title: '"%{title}" ప్పాముగా కేతనించాము' + email_body: "%{link}\n\n%{message}" + inappropriate: + title: 'అసమంజసమైనది' + description: 'ఈ టపాలో విషయం కొంతమందికి అభ్యంతరకరమైనది, అగౌరవపరిచేది లేదా మా కమ్యునిటీ మార్గదర్శకాలకు లోబడినది కాదు.' + long_form: 'దీన్ని అసమంజసమైనదిగా కేతనించాము' + notify_user: + title: 'ప్రైవేటు సందేశము @{{username}}' + description: 'ఈ టపా నేను సభ్యునితో వ్యక్తిగతంగా మాట్లాడాలనుకున్న విషయం కలిగి ఉంది. కేతనం అవ్వసరంలేదు.' + long_form: 'సభ్యుడికి ప్రైవేటు సందేశం పంపాము' + email_title: '"%{title}" లో మీ టపా' + email_body: "%{link}\n\n%{message}" + notify_moderators: + title: "వేరే ఏదో" + description: 'ఇక్కడ ప్రదర్శిచని వేరే కారణంగా ఈ టపా నిర్వాహకుని దృష్టికి తీసుకెళ్లాలి' + long_form: 'దీన్ని నిర్వాహకుని దృష్టికి కేతనించాము' + email_title: '"%{title}" లోని ఒక టపా నిర్వాహకుని చర్య కోసం వేచిఉంది. ' + email_body: "%{link}\n\n%{message}" + bookmark: + title: 'పేజీక' + description: 'ఈ టపాకు పేజీక ఉంచు' + long_form: 'ఈ టపాకు పేజీక ఉంచారు' + like: + title: 'ఇష్టం' + description: 'ఈ టపాను ఇష్టపడు' + long_form: 'ఈ టపాను ఇష్టపడ్డారు' + vote: + title: 'వోటు' + description: 'ఈ టపాకు ఓటు వేయి' + long_form: 'ఈ టపాకు వోటు వేసారు' + topic_flag_types: + spam: + title: 'స్పాము' + description: 'ఈ విషయం వాణిజ్య ప్రకటన. ఇది ఎట్టి ఉపయోగం లేనిదీ, ఈ సైటుకు సంబంధం లేనిది మార్కెటింగు గిమ్మిక్కు.' + long_form: 'దీన్ని స్పాముగా కేతనించారు' + inappropriate: + title: 'అసమంజసం' + description: 'ఈ అంశంలో ఉన్న విషయం ద్వారా ఒక సహేతుకమైన వ్యక్తిని ప్రమాదకరమైన,అసంబధ్ధమైనవానిగా పరిగణిస్తారు,లేదా మన వర్గ మార్గదర్శకాల ఉల్లంఘన జరుగుతుంది.' + long_form: 'దీన్ని అసమంజసమైనదిగా కేతనించారు' + notify_moderators: + title: "వేరే ఏదో" + description: 'మార్గదర్శకాల ఆధారంగా ఈ అంశానికి సాధారణ పరిశీలకుల శ్రధ్ధ అవసరం, TOS, లేదా పైన జాబితాలో పేర్కొనబడని మరొక కారణం కావచ్చు.' + long_form: 'దీన్ని నిర్వాహకుల దృష్టికి తెచ్చారు' + email_title: '"%{title}" విషయం నిర్వాహకుల దృష్టిలో ఉంది.' + email_body: "%{link}\n\n%{message}" + flagging: + you_must_edit: '

మీ టపా సమూహం ద్వారా కేతనం చెందింది. దయచేసి మీ ఆంతరంగిక సందేశాలు చూడండి.

' + user_must_edit: '

ఈ టపా కమ్యునిటీ కేతనించింది. తాత్కాలికంగా దాచబడింది

' + archetypes: + regular: + title: "రెగ్యులర్ విషయం" + banner: + message: + make: "ఈ విషయం ఇప్పుడు బ్యానరు. సభ్యుడు తుడిచే వరకూ ఈ విషయం ప్రతి పుటకు అగ్రభాగాన కనిపిస్తుంది." + remove: "ఈ విషయం ఇప్పుడు బ్యానరు కాదు. ఇహ అది ప్రతి పుట అగ్రభాగానా కనిపించదు" + unsubscribed: + title: 'చందాముగిసింది' + description: "మీ చందా ముగిసింది. మిమ్ము మేము ఇహ సంప్రదించము!" + oops: "మీరు కోరుకున్నది ఇది కాకుంటే దిగువ నొక్కండి." + error: "చందా తొలగించడంలో దోషం" + preferences_link: "అమరికలు పుట నుండి కూడా మీరు చందా తొలగించవచ్చు" + different_user_description: "మీరు ప్రస్తుతం వేరే పేరుతో లాగిన్ అయి ఉన్నారు. దయచేసి లాగవుట్ అయి లాగిన్ కండి. " + not_found_description: "క్షమించాలి. మీ చందా మేము రద్దు చేయలేకపోయాము. బహుశా మీ ఈ మెయిల్లోని లంకె కాలాతీతమై ఉండవచ్చు." + resubscribe: + action: "మరలా చందాకట్టు" + title: "మరలా చందాకట్టారు" + description: "మీ చందా మరలా చేతనమైంది" + reports: + visits: + title: "సభ్యుని గణాంకాలు" + xaxis: "రోజు" + yaxis: "దర్శనాల సంఖ్య" + signups: + title: "సభ్యులు" + xaxis: "రోజు" + yaxis: "కొత్త సభ్యుల సంఖ్య" + topics: + title: "విషయాలు" + xaxis: "రోజు" + yaxis: "కొత్త విషయాల సంఖ్య" + posts: + title: "టపాలు" + xaxis: "రోజు" + yaxis: "కొత్త టపాల సంఖ్య" + likes: + title: "ఇష్టాలు" + xaxis: "రోజు" + yaxis: "కొత్త ఇష్టాల సంఖ్య" + flags: + title: "కేతనాలు" + xaxis: "రోజు" + yaxis: "కేతనాల సంఖ్య" + bookmarks: + title: "పేజీకలు" + xaxis: "రోజు" + yaxis: "కొత్త పేజీకల సంఖ్య" + starred: + title: "చుక్కేసినవి" + xaxis: "రోజు" + yaxis: "కొత్తగా చుక్కపెట్టిన విషయాలు" + users_by_trust_level: + title: "నమ్మకపు స్థాయి వారీగా సభ్యులు" + xaxis: "నమ్మకం స్థాయి" + yaxis: "సభ్యుల సంఖ్య" + emails: + title: "పంపిన ఈమెయిల్ల సంఖ్య" + xaxis: "రోజు" + yaxis: "ఈమెయిల్ల సంఖ్య" + user_to_user_private_messages: + title: "సభ్యుని నుండి సభ్యునికి" + xaxis: "రోజు" + yaxis: "ప్రైవేటు సందేశాల సంఖ్య" + system_private_messages: + title: "వ్వవస్థ" + xaxis: "రోజు" + yaxis: "ప్రైవేటు సందేశాల సంఖ్య" + moderator_warning_private_messages: + title: "నిర్వాహకుని హెచ్చరిక" + xaxis: "రోజు" + yaxis: "ప్రైవేటు సందేశాల సంఖ్య" + notify_moderators_private_messages: + title: "నిర్వాహకుల దృష్టి" + xaxis: "రోజు" + yaxis: "ప్రైవేటు సందేశాల సంఖ్య" + notify_user_private_messages: + title: "సభ్యుల దృష్టి" + xaxis: "రోజు" + yaxis: "ప్రైవేటు సందేశాల సంఖ్య" + top_referrers: + title: "అగ్ర రిఫరరు" + xaxis: "సభ్యుడు" + num_clicks: "నొక్కులు" + num_topics: "విషయాలు" + top_traffic_sources: + title: "అగ్ర ట్రాఫిక్ మూలాలు" + xaxis: "డొమైను" + num_clicks: "నొక్కులు" + num_topics: "విషయాలు" + num_users: "సభ్యులు" + top_referred_topics: + title: "అగ్ర రిఫరరు విషయాలు" + xaxis: "విషయం" + num_clicks: "నొక్కులు" + page_view_anon_reqs: + title: "అనామక" + xaxis: "రోజు" + yaxis: "అనామక పుట సందర్శనాలు" + page_view_logged_in_reqs: + title: "లాగిన్ అయిన" + xaxis: "రోజు" + yaxis: "లాగిన్ పుట సందర్శనాలు" + page_view_crawler_reqs: + title: "జాల క్రాలర్లు" + xaxis: "రోజు" + yaxis: "జాల క్రాలరు పుట సందర్శనాలు" + page_view_total_reqs: + title: "మొత్తం" + xaxis: "రోజు" + yaxis: "మొత్తం పుట సందర్శనాలు" + http_background_reqs: + title: "వెనుతలం" + xaxis: "రోజు" + yaxis: "లైవ్ ఉన్నతీకరణ మరియు గమనికల కోసం వాడిన అభ్యర్థనలు" + http_2xx_reqs: + title: "స్థితి 2xx(ఓకే)" + xaxis: "రోజు" + yaxis: "విజయవంత అభ్యర్థనలు(స్థితి 2xx)" + http_3xx_reqs: + title: "HTTP 3xx (మళ్లింపు)" + xaxis: "రోజు" + yaxis: "మళ్లించిన అభ్యర్థనలు (స్థితి 3xx) " + http_4xx_reqs: + title: "HTTP 4xx (క్లైంటు దోషం)" + xaxis: "రోజు" + yaxis: "క్లైంటు దోషాలు (స్థితి 4xx)" + http_5xx_reqs: + title: "HTTP 5xx (సర్వరు దోషం)" + xaxis: "రోజు" + yaxis: "సర్వరు దోషాలు (స్థితి 5xx)" + http_total_reqs: + title: "మొత్తం" + xaxis: "రోజు" + yaxis: "మొత్తం అభ్యర్థనలు" + dashboard: + rails_env_warning: "మీ సర్వరు %(env) రీతిలో నడుస్తోంది" + ruby_version_warning: "మీరు రూబీ 2.0.0 వాడుతున్నారు. ఇది సమస్యలతో కూడినది. దయచేసి పేచీ స్థాయి 247 లేదా ఆపైకి ఉన్నతీకరించగలరు" + host_names_warning: "మీ config/database.yml దస్త్రం అప్రమేయ లోకల్ హోస్ట్ వాడుతున్నది. దాన్ని మీ సైటు పేరుకు మార్చగలరు. " + queue_size_warning: "వరుసలో ఉన్న ఉద్యోగాల సంఖ్య %{queue_size}, ఏది ఆధిక్యత.ఈ సైడ్‌కిక్ ప్రక్రియ(లు) ఒక సమస్య సూచిస్తుంది , లేదా \nమీకు మరింతమంది సైడ్‌కిక్ కార్మికులు అవసరం కావచ్చు." + memory_warning: 'మీ సర్వరు ఒక జీబీ కన్నా తక్కువ మెమరీతో నడుస్తున్నది. కనీసం ఒక జీబీ అయినా ఉండుట మంచిది. ' + contact_email_missing: "మీరు మీ సైట్ యొక్క సంప్రదింపు ఈ-మెయిల్ నమోదు చేయండి దాని ద్వారా సైట్‌కు సంబంధించిన అత్యవసర విషయాలు అందుకొంటారు. సైట్ సెట్టింగ్స్‌ లో అప్‌డేట్ చేయండి." + contact_email_invalid: "సైట్ సంప్రదింపు ఈ-మెయిల్ చెల్లదు.సైట్ సెట్టింగ్స్‌ లో అప్‌డేట్ చేయండి." + title_nag: "సైట్ పేరు నమోదు చేయండి.శీర్షికను సైట్ సెట్టింగ్స్‌ లో అప్‌డేట్ చేయండి" + site_description_missing: "శోధన ఫలితాల్లో కనిపించడానికి మీ సైట్ యొక్క ఏకవాక్య వివరణ నమోదు చేయండి.సైట్ వివరణను సైట్ సెట్టింగ్స్‌ లో అప్‌డేట్ చేయండి." + site_contact_username_warning: "ముఖ్యమైన స్వయంసిధ్ధ సందేశాలు పంపడానికి ఒక స్నేహపూర్వక సిబ్బంది వాడుకదారు పేరును Update site_contact_username లో ప్రవేశపెట్టండి సైట్ సెట్టింగ్స్." + notification_email_warning: "ఈ-మెయిల్ ప్రకటనలు మీ డొమైన్ లో ఒక చెల్లుబాటు అయ్యే ఈ-మెయిల్ చిరునామా నుండి పంపలేదు :ఈ-మెయిల్ బట్వాడా అనిశ్చిత మరియు నమ్మలేనిదిగా ఉంటుంది.దయచేసి ప్రకటనను ఉంచండి_ఒక చెల్లుబాటు అయ్యే స్థానిక ఈమెయిల్ చిరునామాకు ఈ-మెయిల్ చేయండిసైట్ సెట్టింగ్స్ లో." + content_types: + education_new_reply: + title: "కొత్త వాడుకరి విద్య: మొదటి ప్రత్యుత్తరాలు" + description: "వినియోగదారులు వారి మొదటి రెండు కొత్త ప్రత్యుత్తరాలు టైప్ చేయడం ప్రారంభించిన సమయంలో పాప్‌అప్ మార్గదర్శకత్వం స్వయంచాలకంగా కంపోజర్ పైన కనిపిస్తుంది. " + education_new_topic: + title: "కొత్త వాడుకరి విద్య: మొదటి విషయాలు" + description: "వినియోగదారులు వారి మొదటి రెండు కొత్త విషయాలు టైప్ చేయడం ప్రారంభించిన సమయంలో పాప్‌అప్ మార్గదర్శకత్వం స్వయంచాలకంగా కంపోజర్ పైన కనిపిస్తుంది. " + usage_tips: + title: "కొత్త వాడుకరి మార్గదర్శకత్వం" + description: "కొత్త వాడుకరుల కోసం మార్గదర్శకత్వం మరియు అవసరమైన సమాచారం" + welcome_user: + title: "సుస్వాగతం: కొత్త సభ్యుడు" + description: "కొత్త వినియోగదారులు సైన్ అప్ చేసినప్పుడు , ఒక ఆంతరంగిక సందేశం స్వయంచాలకంగా పంపుతుంది." + welcome_invite: + title: "స్వాగతం : ఆహ్వానించబడిన వాడుకరి" + description: "చర్చలో పాల్గొనేందుకు ఒక వినియోగదారుని నుండి ఆహ్వానం అంగీకరించినప్పుడు ఒక ప్రైవేట్ సందేశం స్వయంచాలకంగా కొత్తగా ఆహ్వానించిన వినియోగదారులందరికీ అందుతుంది." + login_required_welcome_message: + title: "లాగిన్ అవసరం: స్వాగత సందేశం" + description: "'లాగిన్ అవసరం' సెట్టింగ్ ప్రారంభించబడి ఉన్నప్పుడు లాగ్ అవుట్ వినియోగదారులకు స్వాగతసందేశం ప్రదర్శించబడుతుంది." + login_required: + title: "లాగిన్ అవసరం: హోమ్‌పేజ్" + head: + description: "ట్యాగ్‌ల లోపల HTML ప్రవేశపెట్టబడింది." + top: + title: "పుటల యొక్క పై భాగంలో" + bottom: + title: "పుటల అడుగు భాగం" + description: "ట్యాగ్ చెయ్యక ముందే HTML ను కలుపుతారు." + site_settings: + min_post_length: "టపా అనుమతించే అక్షరాల కనిష్ఠ పొడవు" + min_private_message_post_length: "ప్రైవేట్ సందేశాలకు కనిష్ఠంగా ఎన్ని అక్షరాలకు అనుమతి" + max_post_length: "టపా అనుమతించే అక్షరాల గరిష్ఠ పొడవు" + min_topic_title_length: "శీర్షిక కు అనుమతించే అక్షరాల కనిష్ఠ పొడవు" + max_topic_title_length: "శీర్షిక కు అనుమతించే అక్షరాల గరిష్ఠ పొడవు" + min_private_message_title_length: "ఒక శీర్షికకు కనిష్ఠంగా ఎన్ని అక్షరాలకు అనుమతి" + min_search_term_length: "శోధనకు అవసరమయ్యే అక్షరాల కనీస పొడవు" + allow_uncategorized_topics: "అనుమతించే విషయాలు వర్గం లేకుండా సృష్టించబడుతున్నాయి." + title: "ఈ సైట్ యొక్క పేరు ,టైటిల్ ట్యాగ్ లో ఉపయోగించారు." + max_topics_in_first_day: "ఈ సైట్ లో వినియోగదారు మొదటి రోజున గరిష్ఠంగా ఎన్ని విషయాలకి అనుమతి ఉంది " + max_replies_in_first_day: "ఈ సైట్ లో వినియోగదారు మొదటి రోజున గరిష్ఠంగా ఎన్ని జవాబులకు అనుమతి ఉంది " + invite_expiry_days: "వాడుకరుల ఆహ్వాన కీ ఎన్ని రోజులు చెల్లుతుంది" + min_password_length: "సంకేతపదపు కనీస పొడవు." + max_likes_per_day: "వినియోగదారులు గరిష్ఠంగా రోజుకు చేయగలిగే లైక్‌ల సంఖ్య" + max_flags_per_day: "వినియోగదారులు గరిష్ఠంగా రోజుకు చేయగలిగే కేతనాల సంఖ్య" + max_bookmarks_per_day: "వినియోగదారులు గరిష్ఠంగా రోజుకు చేయగలిగే బుక్‌మార్క్‌ల సంఖ్య" + max_edits_per_day: "వినియోగదారులు గరిష్ఠంగా రోజుకు చేయగలిగే సవరణల సంఖ్య" + max_topics_per_day: "వినియోగదారులు గరిష్ఠంగా రోజుకు సృష్టించగలిగే విషయాలు" + max_private_messages_per_day: "వినియోగదారులు గరిష్ఠంగా రోజుకు సృష్టించగలిగే ప్రైవేటు సందేశాలు" + max_invites_per_day: "వినియోగదారు గరిష్ఠంగా రోజుకు ఎన్ని ఆహ్వానాలు పంపవచ్చు." + suggested_topics: "విషయం దిగువన చూపే సూచనల సంఖ్య" + newuser_max_links: "కొత్త వాడుకరి టపా కి ఎన్ని లంకెలు జోడించవచ్చు." + newuser_max_images: "కొత్త వాడుకరి టపా కి ఎన్ని చిత్రాలు జోడించవచ్చు." + newuser_max_attachments: "కొత్త వాడుకరి టపా కి ఎన్నిఅనుబంధాలు జోడించవచ్చు" + errors: + invalid_email: "చెల్లని ఈమెయిలు చిరునామా." + invalid_username: "ఆ పేరుతో ఏ వాడురకీ లేరు." + invalid_integer: "విలువ తప్పనిసరిగా సంఖ్య మాత్రమే." + invalid_string: "చెల్లుబాటు కాని విలువ" + invalid_string_min: "కనీసం %{min} అక్షరాలు ఉండాలి." + invalid_string_max: "%{max} కంటే ఎక్కువ అక్షరాలు ఉండకూడదు." + search: + types: + category: 'వర్గాలు' + topic: 'ఫలితాలు' + user: 'సభ్యులు' + original_poster: "మూల టపా రచయిత" + most_posts: "అధిక టపాలు" + most_recent_poster: "ఇటీవలి పోస్టర్" + frequent_poster: "తరచూ వచ్చే పోస్టర్" + topic_statuses: + autoclosed_disabled_lastpost: "ఈ విషయం ఇప్పుడు తెరవబడింది.కొత్త జవాబులకు అనుమతి." + login: + incorrect_username_email_or_password: "తప్పు సభ్యుని పేరు లేదా ఈమెయిల్ లేదా సంకేతపదం" + active: "మీ ఖాతా చేతనమైంది మరియు వాడటానికి సిద్దంగా ఉంది." + not_allowed_from_ip_address: "మీరు ఆ ఐపీ నుండి %{username} లా లాగిన్ అవ్వలేరు" + suspended: "మీరు %{date}. తారీకు వరకు లాగిన్ అవ్వలేరు" + suspended_with_reason: "మీరు %{date} వరకు లాగిన్ అవ్వలేరు. మీరు సస్పెండైన కారణం: %{reason}" + errors: "%{errors}" + not_available: "అందుబాటులో లేదు. %{suggestion} ప్రయత్నించండి?" + new_registrations_disabled: "కొత్త ఖాతా నమోదులను ప్రస్తుతం అనుమతించడం లేదు." + password_too_long: "సంకేతపదాలు 200 అక్షరాలకు పరిమితం" + missing_user_field: "మీరు అన్ని సభ్య క్షేత్రాలూ నింపలేదు" + close_window: "ద్రువీకరణ ముగిసింది. ఈ విండోను మూసి కొనసాగండి" + user: + no_accounts_associated: "ఎటువంటి ఖాతాలు భాగమవలేదు" + username: + short: "కనీసం %[min] అక్షరాలు ఉండాలి" + long: "గరిష్టం %{max} అక్షరాలు ఉండాలి" + characters: "కేవలం సంఖ్యలు, అక్షరాలు మరియు అండర్ స్కోరు మాత్రమే ఉండాలి. " + unique: "ఏకైకంగా ఉండాలి" + blank: "తప్పనిసరిగా ఉండాలి" + must_begin_with_alphanumeric: "తప్పనిసరిగా సంఖ్యతోగాని, అక్షరంతోగాని మొదలవ్వాలి" + email: + not_allowed: "ఆ ఈమెయిల్ ప్రొవైడరును అనుమంతిచుటలేదు. దయచేసి మరో ఈమెయిల్ చిరునామా రాయండి" + blocked: "అనుమతించుటలేదు" + ip_address: + blocked: "నిలపబడింది" + flags_reminder: + please_review: "దయచేసి వాటిని సమీక్షించండి." + post_number: "టపా" + system_messages: + welcome_user: + subject_template: "%{site_name} కు సుస్వాగతం!" + welcome_invite: + subject_template: "%{site_name} కు సుస్వాగతం!" + email_reject_no_account: + subject_template: "ఈ-మెయిల్ విషయం -- తెలియని ఖాతా" + email_reject_topic_not_found: + subject_template: "ఈ-మెయిల్ విషయం -- అంశం కనుగొనలేదు" + too_many_spam_flags: + subject_template: "కొత్త ఖాతా బ్లాక్ చేశారు" + user_notifications: + previous_discussion: "గత జవాబులు" + user_replied: + subject_template: "[%{site_name}] %{topic_title}" + digest: + new_activity: "మీ విషయాలు మరియు టపాల మీద కొత్త కార్యక్రమం మొదలైంది:" + top_topics: "ప్రముఖ టపాలు" + other_new_topics: "ప్రముఖ విషయాలు" + click_here: "ఇక్కడ క్లిక్ చేయండి" + read_more: "ఇంకా చదువు" + more_topics_category: "మరిన్ని కొత్త విషయాలు:" + posts: + one: "1 టపా" + other: "%{count} టపాలు" + account_created: + subject_template: "[%{site_name}] మీ కొత్త ఖాతా" + page_not_found: + popular_topics: "ప్రముఖ" + recent_topics: "తాజా" + see_more: "ఇంకా" + search_title: "ఈ సైట్ వెదుకు" + search_google: "గూగుల్" + terms_of_service: + title: "సేవా నియమాలు" + signup_form_message: 'నేను సేవా నియమాలను చదివాను, వాటికి అంగీకరిస్తున్నాను.' + deleted: 'తొలగించారు' + upload: + images: + fetch_failure: "క్షమించండి, చిత్రం తెచ్చుటలో తప్పిదము ఉంది." + unknown_image_type: "క్షమించండి, మీరు ఎగుమతి చేయడానికి ప్రయత్నించిన ఫైల్ చిత్రం వలె కనిపించడం లేదు." + size_not_found: "క్షమించండి, కానీ మేము చిత్రం యొక్క పరిమాణం నిర్ణయించలేదు. బహుశా మీ చిత్రం పాడై ఉండవచ్చు?" + email_log: + seen_recently: "వినియోగదారు కొత్తగా చూశారు" + notification_already_read: "ఈ ఈమెయిల్ గురించి ప్రకటన ఇప్పటికే చదివబడింది" + post_deleted: "టపా రచయితచే తొలగింపబడింది" + user_suspended: "వినియోగదారు నిలిపివేయబడ్డారు" + already_read: "వినియోగదారు ఇప్పటికే ఈ టపా చదివారు" + body_blank: "ముఖ్యభాగం ఖాళీగా ఉంది" + color_schemes: + base_theme_name: "ఆధారం" + guidelines: "మార్గదర్శకాలు" + edit_this_page: "ఈ పేజిని సవరించు" + csv_export: + boolean_yes: "అవును" + boolean_no: "లేదు" + guidelines_topic: + title: "తఅప్ర/మార్గదర్శకాలు" + body: "\n\n## [బహిరంగ చర్చకు ఇది ఒక నాగరిక స్థానం](#నాగరికత )\n\nదయచేసి ఈ చర్చావేదికను ఒక పబ్లిక్ పార్క్ తో సమానంగా పరిగణించండి.మనకి చాలా ఉపయుక్తమైన భాగస్వామ్య సమాజ పంపిణీ వనరు — కొనసాగుతున్న సంభాషణ ద్వారా నైపుణ్యాలు, జ్ఞానం మరియు ఆసక్తులు పంచుకోవడానికి ఒక చోటు.\n\nఇవి కఠినమైన మరియు ధృడమైన నియమాలు కావు, కేవలం మన సంఘంలో మానవతీర్పుకు\ + \ ఉపకరణాలు.\nనాగరిక ప్రజా సంభాషణ కోసం స్వచ్ఛమైన,మరింత ఆహ్లాదకరమైన స్థానంగా ఉంచడానికి ఈ మార్గదర్శకాలను ఉపయోగించండి.\n\n\n\n## [చర్చను మెరుగుపరచండి](#మెరుగుపరచు)\n\nదీనిని గొప్ప చర్చావేదిక గా తయారుచేయడానికి మీరు ఎల్లప్పుడూ చర్చను మెరుగుపరచడానికి ఏదో విధంగా, చిన్న సహాయమైనా చేయండి. మీరు మీ టపాకి సంభాషణని జత చేసింది లేనిది ఖచ్చితంగా తెలియకపోతే\ + \ , మీరు ఏమి చెప్పదలుచుకున్నారో ఆలోచించండి మరియు మరలా ప్రయత్నించండి.\n\nఇక్కడ చర్చించబడిన విషయాలు మనకు సంబంధించినవి, మరియు మీ విషయంగా భావించండి.విషయం గురించి చర్చించే వ్యక్తుల పట్ల , ఎవరైనా చెప్పినదాని పట్ల విభేధించినా మర్యాదతో వ్యవహరించండి.\n\nచర్చ మెరుగుదలకు ఇప్పటికే ఒక మార్గం కనుగొనడం జరిగింది.మీరు ఏ విషయానికైనా బదులివ్వడానికి లేదా కొత్తగా మొదలు పెట్టడానికి\ + \ ముందు బ్రౌజింగ్ కోసం కొంత సమయం వెచ్చించండి,మరియు మీ అభిరుచులను పంచుకోవడానికి ఒక మంచి సమావేశానికి అవకాశం కల్పిస్తాము.\n\n\n\n## [మీరు విభేధించినప్పటికీ ,సమ్మతంగా వ్యవహరించండి](#సమ్మతి)\n\nమీరు ఏ విషయంతోనైనా విభేధించాలి అనుకొంటే , అది బాగుంది కానీ , గుర్తుంచుకోండి_ఆలోచనలను విమర్శించండి, వ్యక్తులను కాదు_దయచేసి నివారించండి.\n\n*పేరు\ + \ పిలవండి.\n*మానవ దాడులు.\n*అసలు విషయం బదులుగా టపా యొక్క ధ్వనితో సమాధానం ఇస్తుంది.\n*స్వల్ప వైరుధ్యాలు.\n\nబదులుగా, సంభాషణను మెరుగుపర్చడానికి హేతుబధ్దమైన ప్రతివాదనకు అవకాశం కల్పించండి.\n\n\n\n## [మీరు పాల్గొన్న సంఖ్య](#పాల్గొను)\n\nఇక్కడ ఉన్న సంభాషణలన్నింటికీ మేము ఒక ధ్వని ఏర్పాటు చేశాము.ఈ చర్చాస్థలాన్ని మరింత ఆసక్తికరంగా తయారుచేయుటకు\ + \ ఈ వర్గం యొక్క భవిష్యత్‌ను ప్రభావితం చేసే చర్చల్లో పాల్గొని మాకు సహాయం చేయండి — మరియు చేయలేనివి వదిలేయండి.\n\nడిస్కోర్స్ మీకు సమిష్టిగా ఉత్తమ (మరియు చెత్త) రచనలు గుర్తించడానికి సాధ్యమయ్యే వర్గ ఉపకరణాలు అందిస్తుంది.ఇష్టాలు, బుక్‌మార్క్‌లు, జెండాలు, ప్రత్యుత్తరాలు, సవరణలు, మరియు మొదలగునవి.చాలా, మీరు మరియు ప్రతి ఒక్కరూ సొంత అనుభవాన్ని చాలా మెరుగుపరచడానికి\ + \ ఈ సాధనాలను ఉపయోగించండి.\n\nదీని కంటే ఒక మెరుగైన వేదిక దొరికితే , దీనిని వదలడానికి ప్రయత్నించండి.\n\n\n\n## [మీరు ఒక సమస్యని గుర్తించినట్లయితే, కేతనం చెయ్యండి](#సమస్య-కేతనం)\n\nపరిశీలకులు ప్రత్యేక అధికారం కలిగియున్నారు;ఈ చర్చాస్థలంనకు వారే భాధ్యులు.కానీ మీరు కూడా, మీ సహాయంతో పరిశీలకులు వర్గ దోహదకారులుగా ఉంటారు,కాపలాదారులు లేదా\ + \ రక్షకభటులుగా కాదు.\n\nమీరు చెడు ప్రవర్తన చూసినప్పుడు, సమాధానం ఇవ్వకండి. ఇది గుర్తించడం ద్వారా చెడు ప్రవర్తనను ప్రోత్సహిస్తున్నట్లే , మీ శక్తి ఖర్చవుతుంది, మరియు ప్రతి ఒక్కరి సమయం వృధా అవుతుంది. _కేవలం కేతనించండి_.తగినన్ని కేతనాలు వచ్చి ఉంటే ,స్వయంచాలకంగా గాని లేదా పరిశీలకుని జోక్యంతో గాని చర్య తీసుకోబడుతుంది. \n\nమన వర్గం నిర్వహించే క్రమంలో , పరిశీలకులు\ + \ ఏ సమయంలోనైనా మరియు ఏ కారణం చేతనైనా, ఏ విషయాన్నైనా, ఏ వినియోగదారుని ఖాతానైనా తొలగించే హక్కు కలిగియుంటారు.పరిశీలకులు కొత్త టపాలను ఏ విధంగానూ ముందుగా చూడరు; పరిశీలకులు మరియు సైట్ నిర్వాహకులు ఈ వర్గంలోని టపాల లోని విషయం పట్ల ఏ భాధ్యత తీసుకోరు.\n\n\n\n## [ఎల్లప్పుడూ మర్యాదగా వ్యవహరించు](#మర్యాద)\n\nఆరోగ్యకరమైన సంభాషణ చెడిపోయేలా,అమర్యాదగా\ + \ వ్యవహరించకండి.\n\n*మర్యాదతో మెలగండి.ఒక సహేతుకమైన వ్యక్తిని గురించి హానికర, అసంబద్ధ టపాలు లేదా ద్వేషపూరిత ప్రసంగాన్ని చేయవద్దు.\n*స్వఛ్చంగా ఉంచండి. ఎటువంటి అశ్లీలమైన లేదా ప్రత్యక్ష లైంగిక పరమైన టపాలు చేయవద్దు.\n*పరస్పరం గౌరవించుకోండి. వేధించడం లేదా భాధ కలిగించడం,వ్యక్తులను అనుకరించడం,వారి ఆంతరంగిక విషయాలు బహిర్గతపరచడం చేయకండి.\n*మన చర్చావేదికను గౌరవించండి.ఎటువంటి\ + \ స్పామ్ సందేశాలు లేదా హాని కలిగించే టపాలు చేయకండి.\n\nఇవి మిశ్రమ నిబంధనలు,ఖచ్చితమైన నిర్వచనాలు లేవు — ఈ పనుల యొక్క రూపాన్ని కూడా తొలగించండి.మీకు తెలియకుంటే, మీ టపా న్యూ యార్క్ టైమ్స్ ముందు పేజీలో ప్రత్యేక వ్యాసంగా వేయబడితే అని మీరు ఎలాంటి భావన పొందుతారో మిమ్మల్ని మీరే ప్రశ్నించుకోండి.\n\nఇది బహిరంగ చర్చావేదిక, మరియు సెర్చ్ ఇంజన్స్ ఈ చర్చలను సూచిస్తాయి.భాష,\ + \ లంకెలు మరియు చిత్రాలను కుటుంబం మరియు స్నేహితులు కొరకు సురక్షితంగా ఉంచండి.\n\n\n\n## [చక్కగా ఉంచండి](#చక్కగా ఉంచండి)\n\nసంగతులు సరైన స్థానంలో పెట్టడానికి కృషిచేయండి.ఎందుకంటే మేము ఎక్కువ సమయం చర్చలమీద మరియు తక్కువ శుభ్రత మీద పెట్టదలచుకొన్నాం.\n\n*ఒక తప్పు వర్గంలో విషయాన్ని మొదలుపెట్టకండి.\n*ఒకే విషయాన్ని బహుళవిషయాలకు వైరుధ్యమైన\ + \ టపాలు చేయవద్దు.\n *ప్రత్యుత్తరంగా విషయం లేని టపాలు చేయవద్దు.\n*విషయాన్ని మధ్యలో దారి మళ్ళించకండి.\n*మీ టపాలకు సంతకం చేయకండి — ప్రతి టపా మీ వ్యక్తిగత వివరాల సమాచారాన్ని కలిగిఉంటుంది.\n\nమీ టపాలు \"+1\" లేదా \"అంగీకారం\" కంటే ఎక్కువ అయితే , లైక్ బటన్ ని ఉపయోగించండి. అయితే మౌలికంగా విభిన్న దిశలో ఇప్పటికే తీసుకొని ఉన్నదాని కంటే ఎక్కువగా ఉంటే, అనుబంధ విషయాన్ని\ + \ సమాధానంగా ఉపయోగించండి.\n\n\n\n## [మీ సొంత విషయాన్ని మాత్రమే టపా చేయండి](#దొంగతనము)\n\nమీరు ఎవరికో చెందిన విషయాన్ని వారి అనుమతి లేకుండా టపా చేయకూడదు.మీరు, ఏ ఇతర చట్టాన్ని అతిక్రమించి వర్ణనలు, లింకులు, లేదా పద్ధతులు ఒకరి మేధో సంపత్తి నుండి దొంగిలించిన \n(సాఫ్ట్వేర్, వీడియో, ఆడియో, చిత్రాలు) టపా చెయ్యకూడదు.\n\n\n\ + \n## [మీరే ఆధారం](#శక్తి)\n\nఈ సైట్ మీ [స్నేహపూర్వక స్థానిక సిబ్బంది] ద్వారా నిర్వహించబడుతుంది. మీకు ఇంకా ఇక్కడ విషయాలు ఎలా పని చేస్తాయి అని ఏవైనా ప్రశ్నలు ఉంటే ,ఒక కొత్త విషయాన్ని తెరచి [మెటా వర్గం](/c/మెటా) చర్చించండి! ఒక మెటా అంశం లేదా కేతనం ఒక క్లిష్టమైన లేదా తక్షణ సమస్యగా ఉండి ఉంటే , మమ్మల్ని [సిబ్బంది పేజ్](/గురించి) ద్వారా సంప్రదించండి.\n\n\n\n## [సేవా నిబంధనలు](#నిబంధనలు)\n\nఅవును, చట్టం విసుగు ప్రక్రియ,– మరియు పొడగింపు ద్వారా, మీరు మరియు మీ సమాచారం – ప్రతికూలమైన చర్యలకు వ్యతిరేకంగా కానీ మనల్ని మనంరక్షించుకోవాలి.మనం [సేవా నిబంధనలు]ను విషయానికి సంబంధించిన మీ (మరియు మా) ప్రవర్తన మరియు హక్కులను వివరిస్తూ గోప్యతా మరియు చట్టాలు ఉన్నాయి.ఈ సేవలను ఉపయోగించుకోవడానికి, మనం కట్టుబడి\ + \ ఉండాలి.[నిబంధనలు](/నిబంధనలు).\n" + tos_topic: + title: "సేవా నిబంధనలు" + privacy_topic: + title: "ప్రైవసీ పోలసీ" + static: + search_help: | +

సూచనలు

+

+

    +
  • టైటిల్ వెతుకులాటలో, ఏదైనా సందేహం ఉన్నప్పుడు, ప్రాధాన్యతాక్రమంను బట్టి సరిపోలుతుంది
  • +
  • ప్రత్యేక, అసాధారణ పదాలు ఎల్లప్పుడూ ఉత్తమ ఫలితాలను ఇస్తాయి
  • +
  • వీలైనప్పుడల్లా, మీరు ఒక నిర్దిష్ట వర్గం, వినియోగదారు, లేదా అంశం శోధించడానికి అవకాశం ఉంటుంది
  • +
+

+

ఆప్షన్‌లు

+

+ <పట్టిక> + క్రమము:చూపులుక్రమము:తాజా + స్థాయి:తెరచివుంచుస్థాయి:మూసిఉంచుస్థాయి:భద్రపరచుస్థాయి:సమాధానంలేనిస్థాయి:ఒకేవినియోగదారు + వర్గం:ఫోవినియోగదారు:ఫో + ఇష్టాలలోటపాలలోవీక్షణాలలోట్రాకింగ్‌లోవ్యక్తిగతాలలో + బుక్‌మార్క్‌లలో + +

+

+ ఇంధ్రధనస్సు వర్గం:నిలుపు స్థాయి:తెరుచు స్థాయి:తాజా ఏదేని విషయంలో "రెయిన్‌బో" అన్న పదo ఉంటే "నిలుపు" వర్గం లోని మూసివేసిన లేదా భద్రపరచిన దానిలో,ఆఖరి టపా తేదిని బట్టి వెదకండి. +

diff --git a/config/locales/server.tr_TR.yml b/config/locales/server.tr_TR.yml new file mode 100644 index 0000000000..e0212d6c33 --- /dev/null +++ b/config/locales/server.tr_TR.yml @@ -0,0 +1,1814 @@ +# encoding: utf-8 +# +# Never edit this file. It will be overwritten when translations are pulled from Transifex. +# +# To work with us on translations, join this project: +# https://www.transifex.com/projects/p/discourse-org/ + +tr_TR: + stringex: + characters: + number: "-" + i18n: + transliterate: + rule: + ț: "t" + Ț: "t" + ș: "s" + Ș: "s" + dates: + short_date_no_year: "D MMM" + short_date: "D MMM, YYYY" + long_date: "MMMM D, YYYY h:mma" + title: "Discourse" + topics: "Konular" + posts: "gönderiler" + loading: "Yükleniyor" + powered_by_html: 'Gücünü Discourse ''tan alır, en iyi görünüm için JavaScript etkinleştirmeniz gerekir' + log_in: "Giriş Yap" + via: "%{site_name} sitesinden %{username}" + is_reserved: "rezerve edilmiş" + purge_reason: "Bırakılmış, etkinleştirilmemiş hesap olarak otomatik silindi" + disable_remote_images_download_reason: "Yeterli disk alanı kalmaması sebebiyle uzaktan görüntü indirme devre dışı bırakıldı." + errors: + format: '%{attribute} %{message}' + messages: + too_long_validation: "%{max} karakter ile limitli; siz %{length} karakter girdiniz." + invalid_boolean: "Geçersiz boolean değeri." + accepted: onaylanmalı + blank: boş bırakılamaz + present: boş bırakılmalı + confirmation: "%{attribute} ile eşleşmiyor" + empty: boş olamaz + equal_to: '%{count} rakamına eşit olmalı' + even: çift sayı olmalı + exclusion: rezerve edildi + greater_than: '%{count} rakamından fazla olmalı' + greater_than_or_equal_to: '%{count} rakamına eşit veya daha fazla olmalı' + inclusion: listeye dahil edilmemiş + invalid: geçersiz + less_than: '%{count} rakamından az olmalı' + less_than_or_equal_to: '%{count} rakamına eşit veya daha az olmalı' + not_a_number: sayı değil + not_an_integer: tam sayı değil + odd: tek sayı olmalı + record_invalid: 'Doğrulama başarısız: %{errors}' + restrict_dependent_destroy: + one: "Kayıt silinemiyor çünkü bir bağımlı %{record} var" + many: "Kayıt silinemiyor çünkü bir bağımlı %{record} var" + too_long: + other: çok uzun (maksimum %{count} karakter olabilir) + too_short: + other: çok kısa (minimum %{count} karakter olabilir) + wrong_length: + other: yanlış uzunlukta (%{count} karakter olmalı) + other_than: "%{count} dışında bir rakam olmalı" + template: + body: 'Şu alanlarla ilgili problem yaşandı:' + header: + other: '%{count} hata bu %{model} kaydının alınmasını engelledi' + embed: + load_from_remote: "Gönderi yüklenirken bir hata oluştu." + bulk_invite: + file_should_be_csv: "Yüklenen dosya csv veya txt formatında olmalı. " + backup: + operation_already_running: "Devam eden bir işlem var. Yeni bir işlem başlatılamaz." + backup_file_should_be_tar_gz: "Yedekleme dosyası .tar.gz uzantılı olmalı." + not_enough_space_on_disk: "Yedeklemenin yapılması için diskte yeterli alan bulunmuyor." + not_logged_in: "Bunun için giriş yapmanız gerekir." + read_only_mode_enabled: "Bu site sadece okuma modunda. Etkileşimler etkisizleştirildi." + too_many_replies: + other: "Üzgünüz, yeni kullanıcılar geçici olarak aynı konu içinde sadece %{count} cevap ile sınırlılar. " + embed: + start_discussion: "Tartışma Başlat" + continue: "Tartışmaya Devam Et" + more_replies: + other: "%{count} cevap daha" + loading: "Tartışma Yükleniyor..." + permalink: "Kalıcı Bağlantı" + imported_from: "Bu ilave bir tartışmadır, asıl konu adresi %{link}" + in_reply_to: "▶ %{username}" + replies: + other: "%{count} cevap" + too_many_mentions: + zero: "Üzgünüz, diğer kullanıcılardan bahsedemezsiniz." + one: "Üzgünüz, bir gönderide sadece bir kullanıcıdan bahsedebilirsiniz." + other: "Üzgünüz, bir gönderide sadece %{count} kullanıcıdan bahsedebilirsiniz." + too_many_mentions_newuser: + zero: "Üzgünüz, yeni kullanıcılar diğer kullanıcılardan bahsedemezler." + one: "Üzgünüz, yeni kullanıcılar bir gönderide sadece bir kullanıcıdan bahsedebilirler." + other: "Üzgünüz, yeni kullanıcılar bir gönderide sadece %{count} kullanıcıdan bahsedebilirler." + too_many_images: + zero: "Üzgünüz, yeni kullanıcılar gönderilere resi ekleyemezler." + one: "Üzgünüz, yeni kullanıcılar bir gönderiye sadece bir resim ekleyebilirler." + other: "Üzgünüz, yeni kullanıcılar bir gönderiye sadece %{count} resim koyabilirler. " + too_many_attachments: + zero: "Üzgünüz, yeni kullanıcılar gönderilere bir şey ekleyemezler." + one: "Üzgünüz, yeni kullanıcılar bir gönderiye sadece bir eklenti koyabilirler. " + other: "Üzgünüz, yeni kullanıcılar bir gönderiye sadece %{count} eklenti koyabilirler." + too_many_links: + zero: "Üzgünüz, yeni kullanıcılar gönderilere bağlantı ekleyemezler." + one: "Üzgünüz, yeni kullanıcılar bir gönderiye sadece bir bağlantı ekleyebilirler. " + other: "Üzgünüz, yeni kullanıcılar bir gönderiye sadece %{count} bağlantı ekleyebilirler." + spamming_host: "Üzgünüz bu sunucuya bağlantı veremezsiniz." + user_is_suspended: "Uzaklaştırılmış kullanıcılar gönderi yapamazlar." + just_posted_that: "yakın zamanda yaptığınız bir gönderiye çok benziyor" + has_already_been_used: "önceden kullanıldı" + invalid_characters: "geçersiz karakterler barındırıyor" + is_invalid: "geçersiz; biraz daha açıklayıcı olmaya çalışın" + next_page: "sonraki sayfa →" + prev_page: "← önceki sayfa" + page_num: "Sayfa %{num}" + topics_in_category: "'%{category}' kategorisindeki konular" + rss_posts_in_topic: " '%{topic}' konusuna ait RSS akışı" + rss_topics_in_category: "'%{category}' kategorisindeki konulara ait RSS akışı" + author_wrote: "%{author} yazdı:" + num_posts: "Gönderiler:" + num_participants: "Katılımcılar:" + read_full_topic: "Konunun tamamını okuyun" + private_message_abbrev: "Özel Mesaj" + rss_description: + latest: "En son konular" + hot: "Sıcak konular" + too_late_to_edit: "Gönderi çok uzun zaman önce oluşturulmuş. Artık düzenlenemez ya da silinemez. " + excerpt_image: "resim" + groups: + errors: + can_not_modify_automatic: "Otomatik oluşturulan grubu düzenleyemezsiniz" + default_names: + everyone: "herkes" + admins: "adminler" + moderators: "moderatörler" + staff: "görevli" + trust_level_0: "güven_seviyesi_0" + trust_level_1: "güven_seviyesi_1" + trust_level_2: "güven_seviyesi_2" + trust_level_3: "güven_seviyesi_3" + trust_level_4: "güven_seviyesi_4" + education: + until_posts: + other: "%{count} gönderi" + new-topic: |+ + %{site_name} &mdash sitesine hoşgeldiniz; **yeni bir sohbet başlattığınız için teşekkür ederiz! ** + + - Başlık konunuzu en iyi şekilde açıklıyor mu? İlgi çekiyor mu? + + - Bu sohbet neyle ilgili? Kimin ilgisini çeker? Neden önemli? Topluluktan ne gibi cevaplar almayı bekliyorsunuz? + + - Diğerlerinin konunuzu *bulabilmesi * için doğru arama kelimeleri girin. Konunuzun benzer konularla gruplanması için bir kategori seçin. + + Daha fazlası için, [topluluk yönergelerimize bakın](/guidelines). Bu yazı sadece ilk gönderinizde %{education_posts_text} gözükür. + + new-reply: | + %{site_name} &mdash sitesine hoşgeldiniz; katılımınız için teşekkür ederiz! + + - Cevabınız sohbetin gelişmesine yardımcı oluyor mu? + + - Topluluğun diğer üyelerine karşı nazik olun. + + - Yapıcı eleştirici her zaman kabul edilir, ama fikirleri eleştirin, insanları değil. + + Daha fazlası için, [topluluk yönergelerine bakın](/guidelines). Bu yazı sadece ilk gönderinizde %{education_posts_text} gözükür. + avatar: | + ### Hesabın için bir fotoğraf yüklemeye ne dersin? + + Birkaç konu açtın ve cevap bıraktın, fakat avatarın hala kendine has değil - tüm yeni kullanıcıların sahip olduğu varsayılan avatar. + + **[Profil sayfana](%{profile_path})** gitmeyi ve seni temsil eden özel bir resim yüklemeyi düşündün mü? + + Herkesin kendine has avatarı olduğunda, topluluktaki tartışmaları takip etmek ve sohbet esnasında ilginç insanlarla tanışmak daha kolay! + sequential_replies: "### Birden fazla gönderiyi aynı anda cevaplayabilirsin\n\nAynı konuya ardı ardına cevaplar yazmak yerine, lütfen önceki gönderilerden alıntı veya @isim referansları içeren tek bir cevap yaz. \n\nHerhangi bir yazıyı seçtikten sonra çıkan alıntılayarak cevapla butonuna tıklayarak alıntı yapmak için bir önceki cevabınızı düzenleyebilirsiniz.\n\nDaha az kısa, tekil cevaplardan ve iç içe yanıtlamalardan oluşan konuların okunması herkes için daha kolay oluyor.\n" + dominating_topic: "###Sohbete başkalarının katılmasına izin verin\n\nBu konunun sizin için önemli olduğunu görüyoruz – buradaki cevapların %{percent}% oranından daha fazlasını siz göndermişsiniz. \n\nDiğerlerinin de kendi fikirlerini paylaşmaları için onlara yeteri kadar zaman tanıdığınıza emin misiniz?\n" + too_many_replies: "### Bu konu için cevap limitinizi doldurdunuz\n\nÜzgünüz, ancak geçici olarak, yeni kullanıcılar aynı konu içinde en fazla %{newuser_max_replies_per_topic} cevap yazabiliyorlar. \n\nYeni bir cevap yazmak yerine, önceki cevaplarınızı düzenlemeyi, ya da başka konulara göz atmayı düşünün.\n" + reviving_old_topic: | + ### Bu konuyu tekrar canlandırmak mı istediniz? + + Bu başlığa en son %{days} gün önce cevap yazılmış. Cevabınız, bu konuyu listesinin en üstüne taşıyacak ve geçmişte sohbete katılmış olan tüm yazarlara bildirim gönderecek. + + Bu eski sohbeti devam ettirmek istediğinize emin misiniz? + activerecord: + attributes: + category: + name: "Kategori Adı" + post: + raw: "İçerik" + user_profile: + bio_raw: "Hakkımda" + errors: + messages: + is_invalid: "geçersiz; biraz daha açıklayıcı olmaya çalışın" + has_already_been_used: "önceden kullanıldı" + models: + topic: + attributes: + base: + warning_requires_pm: "Özel mesajlara sadece uyarı ekleyebilirsiniz. " + too_many_users: "Uyarıları aynı anda sadece bir kişiye gönderebilirsiniz." + cant_send_pm: "Üzgünüz, seçtiğiniz kullanıcıya özel mesaj gönderemezsiniz." + no_user_selected: "Geçerli bir kullanıcı seçmelisiniz." + user: + attributes: + password: + common: "En çok kullanılan 10000 paroladan biri. Lütfen daha güvenli bir parola seçin." + ip_address: + signup_not_allowed: "Bu hesaptan yeni üyelik oluşturulmasına izin verilmiyor." + color_scheme_color: + attributes: + hex: + invalid: "geçerli bir renk değil" + user_profile: + no_info_me: "
Profilinizdeki Hakkımda bölümü boş, doldurmak ister misiniz?
" + no_info_other: "
%{name} profilinde Hakkımda kısmına henüz bir şey girmedi
" + vip_category_name: "Lobi" + vip_category_description: "Güven seviyesi 3 ve üzerinde olan üyelere özel kategori." + meta_category_name: "Meta" + meta_category_description: "Bu site, sitenin organizasyonu, nasıl çalıştığı ve nasıl geliştirilebileceği ile ilgili tartışma alanı." + staff_category_name: "Görevliler" + staff_category_description: "Görevliler arası konuşmalar için özel kategori. Konular sadece admin ve moderatörlere gözükür." + assets_topic_body: "Bu kalıcı bir konudur, sadece görevlilere gözükür, site tasarımında kullanılan resim ve dosyaların depolanması için kullanılır. Sakın silmeyin!\n\n\nİşleyiş aşağıdaki gibidir:\n\n\n1. Bu konuyu cevaplayın.\n2. Logolar, favicon'lar vb. için kullanmak istediğiniz resimleri buraya yükleyin. (Gönderi düzenleyicisindeki yükleme düğmesini kullanabilir, veya resimleri sürükleyip bırakabilir veya kopyalayıp yapıştırabilirsiniz.)\n3. Cevabı gönderin.\n4. Yeni gönderinizdeki resimlere sağ tıklayarak ya da düzenleme ikonuna tıklayıp gönderinizi düzenleyerek resimlerin bağlantılarına ulaşabilirsiniz. Bağlantıları kopyalayın.\n5. Bağlantıları [basit ayarlar](/admin/site_settings/category/required) ilgili bölümlere yapıştırın.\n\n\nEğer farklı dosya tipleri yükleyebilmek istiyorsanız, [dosya ayarları](/admin/site_settings/category/files) sayfasındaki `authorized_extensions` ayarını düzenleyin." + lounge_welcome: + title: "Lobiye hoşgeldiniz" + body: |2 + + Tebrikler! :confetti_ball: + + Bu konuyu görebiliyorsanız, yakın süre önce **standart** (güven seviyesi 3) seviyesine terfi etmişsiniz demektir. + + Artık … + + * Herhangi bir konunun başlık bilgisini düzenleyebilirsiniz + * Herhangi bir konunun kategorisini değiştirebilirsiniz + * Eklediğiniz linkler takip edilebilir durumunda olacaktır ([otomatik nofollow] (http://en.wikipedia.org/wiki/Nofollow) kaldırıldı) + * Sadece 3. güven seviyesi ve üzerindekilere gözüken özel Lobi kategorisine erişebilirsiniz + * Tek bayraklamayla spam içerikli mesajları gizleyebilirsiniz + + [Şu anki standart seviyedeki arkadaşların listesi(/badges/3/regular). Merhaba demeyi unutmayın. + + Bu topluluğun önemli bir parçası olduğunuz için teşekkür ederiz! + + (Güven seviyeleriyle ilgili daha fazla bilgi için, [bu konuya bakın][trust]. Lütfen, sadece sürekli olarak kriterlere uyan üyelerin standart seviyede kalacağını unutmayın.) + + [trust]: https://meta.discourse.org/t/what-do-user-trust-levels-do/4924 + category: + topic_prefix: "%{category} kategorisi hakkında " + replace_paragraph: "[Bu paragrafın yerine kategorinizin kısa bir açıklamasını girin. Buraya yazılanlar kategori seçim alanında görüneceği için, açıklamanızı 200 karakterin altında tutmaya çalışın. Siz bu metni düzenleyene veya herhangi bir konu yaratana kadar bu kategori, kategoriler sayfasında gözükmeyecek.]" + post_template: "%{replace_paragraph}\n\nDaha uzun bir açıklama, kategori yönergeleri veya kuralları oluşturmak için aşağıdaki paragrafları kullanın.\n\nHerhangi bir tartışma için düşünülebilecek bazı maddeler:\n\n- Bu kategorinin amacı ne? İnsanlar konu oluştururken neden bu kategoriyi seçmeliler?\n\n- Bu kategorinin sahip olduğumuz diğer mevcut kategorilerden farkı ne?\n\n- Bu kategoriye ihtiyacımız var mı?\n\n- Bunu başka bir kategoriyle birleştirmeli miyiz, yoksa daha fazla kategoriye mi bölmeli miyiz?\n" + errors: + uncategorized_parent: "Kategorisizin üst kategorisi olamaz" + self_parent: "Alt kategorinin üst kategorisi kendisi olamaz" + depth: "Bir alt kategori başka bir alt kategorinin altında yer alamaz" + cannot_delete: + uncategorized: "Kategorisiz silinemez" + has_subcategories: "Alt kategorileri bulunduğu için bu kategori silinemez." + topic_exists: + other: "Bu kategoriyi silemezsiniz çünkü %{count} konusu bulunuyor. En eski konu; %{topic_link}." + topic_exists_no_oldest: "Bu kategoriyi silemezsiniz çünkü %{count} konusu bulunuyor." + trust_levels: + newuser: + title: "yeni kullanıcı" + basic: + title: "acemi kullanıcı" + regular: + title: "üye" + leader: + title: "standart" + elder: + title: "lider" + change_failed_explanation: " %{user_name} adlı kullanıcıyı '%{new_trust_level}' seviyesine düşürmeye çalıştınız. Ancak, halihazırda kullanıcının güven seviyesi zaten '%{current_trust_level}'. %{user_name} '%{current_trust_level}' seviyesinde kalacak - eğer seviyesini düşürmek istiyorsanız öncelikle güven seviyesini kilitlemelisiniz" + rate_limiter: + slow_down: "Bu işlemi çok fazla kere tekrar ettiniz, lütfen daha sonra tekrar deneyin" + too_many_requests: "Bu işlem için günlük limitinizi aştınız. Lütfen tekrar denemek için %{time_left} bekleyin. " + hours: + other: "%{count} saat" + minutes: + other: "%{count} dakika" + seconds: + other: "%{count} saniye" + datetime: + distance_in_words: + half_a_minute: "< 1dk" + less_than_x_seconds: + other: "< %{count}sn" + x_seconds: + other: "%{count}sn" + less_than_x_minutes: + other: "< %{count}dk" + x_minutes: + other: "%{count}dk" + about_x_hours: + other: "%{count}sa" + x_days: + other: "%{count}gün" + about_x_months: + other: "%{count}ay" + x_months: + other: "%{count}ay" + about_x_years: + other: "%{count}yıl" + over_x_years: + other: "> %{count}yıl" + almost_x_years: + other: "%{count}yıl" + distance_in_words_verbose: + half_a_minute: "şu anda" + less_than_x_seconds: + other: "şu anda" + x_seconds: + other: "%{count} saniye önce" + less_than_x_minutes: + other: "son %{count} dakika içerisinde" + x_minutes: + other: "%{count} dakika önce" + about_x_hours: + other: "%{count} saat önce" + x_days: + other: "%{count} gün önce" + about_x_months: + other: "yaklaşık %{count} ay önce" + x_months: + other: "%{count} ay önce" + about_x_years: + other: "yaklaşık %{count} yıl önce" + over_x_years: + other: "%{count} yıldan daha fazla önce" + almost_x_years: + other: "yaklaşık %{count} yıl önce" + password_reset: + no_token: "Üzgünüz, erişim jetonunuzun süresi doldu. Lütfen parolanızı sıfırlamayı tekrar deneyin." + choose_new: "Lütfen yeni bir parola seçin" + choose: "Lütfen parola seçin" + update: 'Parolayı Güncelle' + save: 'Parola Belirle' + title: 'Parolayı Sıfırla' + success: "Parolanızı başarıyla değiştirdiniz ve giriş yaptınız." + success_unapproved: "Parolanızı başarıyla değiştirdiniz." + continue: "%{site_name} adresine devam edin" + change_email: + confirmed: "E-posta adresiniz güncellendi." + please_continue: "%{site_name} adresine devam edin" + error: "E-posta adresiniz değiştirilirken bir hata oluştu. Bu adres zaten kullanımda olabilir." + activation: + action: "Hesabınızı etkinleştirin" + already_done: "Üzgünüz, hesap doğrulama linki artık geçerli değil. Hesabınız zaten etkin olabilir mi?" + please_continue: "Hesabınız doğrulandı; şimdi ana sayfaya yönlendirileceksiniz." + continue_button: "%{site_name} adresine devam edin" + welcome_to: "%{site_name} sitesine hoşgeldiniz!" + approval_required: "Bu foruma erişebilmeniz için yeni hesabınızın bir moderatör tarafından manuel olarak onaylanması gerekir. Hesabınız onaylandığında e-posta ile bilgilendirileceksiniz!" + post_action_types: + off_topic: + title: 'Konu dışı' + description: 'Bu gönderinin, başlık ve ilk gönderide belirtilen mevcut tartışmayla alakası yok, ve büyük ihtimal başka bir yere taşınmalı.' + long_form: 'konu dışı olarak bayraklandı' + spam: + title: 'Spam' + description: 'Bu gönderi reklam içeriklidir. Mevcut konuya alakası ya da faydası yoktur.' + long_form: 'spam olarak bayraklanmış' + email_title: '"%{title}" spam olarak bayraklandı' + email_body: "%{link}\n\n%{message}" + inappropriate: + title: 'Uygunsuz' + description: 'Bu gönderi saldırgan, kötüleyici ya da topluluk yönergelerini ihlal eden içerik barındırmaktadır. ' + long_form: 'uygunsuz olarak bayraklanmış' + notify_user: + title: 'Özel Mesaj @{{username}}' + description: 'Bu gönderi, bu kişiyle doğrudan veya özel olarak konuşmamı gerektiren içeriğe sahip. Bayraklanacak bir durum yok.' + long_form: 'kullanıcıya özel mesaj yollandı' + email_title: '"%{title}" başlıklı gönderiniz' + email_body: "%{link}\n\n%{message}" + notify_moderators: + title: "Başka Bir Şey" + description: 'Bu gönderinin, yukarıda belirtilmeyen bir nedenden ötürü moderatör tarafından kontrol edilmesi gerekiyor.' + long_form: 'moderatör tarafından kontrol edilmesi için bayraklandı' + email_title: '"%{title}" başlığındaki bir gönderinin moderatör tarafından kontrol edilmesi gerekiyor.' + email_body: "%{link}\n\n%{message}" + bookmark: + title: 'İşaretle' + description: 'Bu gönderiyi işaretle' + long_form: 'bu gönderi işaretlendi' + like: + title: 'Beğen' + description: 'Bu gönderiyi beğen' + long_form: 'bu beğenildi' + vote: + title: 'Oyla' + description: 'Bu gönderiyi oyla' + long_form: 'bu gönderi oylandı' + topic_flag_types: + spam: + title: 'Spam' + description: 'Bu konu reklam içeriklidir. Bu siteye alakası ya da faydası yoktur.' + long_form: 'spam olarak bayraklandı' + inappropriate: + title: 'Uygunsuz' + description: 'Bu konu saldırgan, kötüleyici ya da topluluk yönergelerini ihlal eden içerik barındırmaktadır. ' + long_form: 'uygunsuz olarak bayraklandı' + notify_moderators: + title: "Başka Bir Şey" + description: 'Bu gönderi yönergeler, kullanıcı sözleşmesi veya yukarıda belirtilmeyen bir nedenden dolayı genel bir moderatör kontrolü gerektiriyor.' + long_form: 'moderatörün ilgisi için bayraklandı' + email_title: '"%{title}" konu başlığının moderatör tarafından kontrol edilmesi gerekiyor' + email_body: "%{link}\n\n%{message}" + flagging: + you_must_edit: '

Gönderiniz topluluk tarafından bayraklandı. Lütfen özel mesajlarınıza göz atın.

' + user_must_edit: '

Bu gönderi topluluk tarafından bayraklandı ve geçici olarak gizlendi.

' + archetypes: + regular: + title: "Standart Konu" + banner: + message: + make: "Bu konu artık bir manşettir. Kullanıcı tarafından yoksayılana kadar her sayfanın en üstünde belirecektir." + remove: "Bu konu artık manşet değildir. Artık her sayfanın en üstünde yer belirmeyecektir." + unsubscribed: + title: 'Aboneliğiniz İptal Edildi' + description: "Aboneliğiniz iptal edildi. Sizinle bir daha iletişime geçmeyeceğiz!" + oops: "Bu işlemi yanlışlıkla yaptıysanız, aşağıya tıklayın." + error: "Abonelik İptalinde Hata Oluştu" + preferences_link: "Özet e-postaları aboneliğinizi ayarlar sayfasınızdan da iptal edebilirsiniz" + different_user_description: "Özet e-postalarının yollandığı kullanıcıdan farklı bir kullanıcı hesabı ile giriş yapmış bulunmaktasınız. Lütfen çıkış yapıp tekrar deneyin." + not_found_description: "Üzgünüz, aboneliğinizi iptal edemedik. E-postanızdaki bağlantı artık geçerli olmayabilir." + resubscribe: + action: "Tekrar Abone Ol" + title: "Tekrar Abone Olundu!" + description: "Tekrar abone oldunuz." + reports: + visits: + title: "Kullanıcı Ziyaretleri" + xaxis: "Gün" + yaxis: "Ziyaret sayısı" + signups: + title: "Kullanıcılar" + xaxis: "Gün" + yaxis: "Yeni kullanıcı sayısı" + topics: + title: "Konular" + xaxis: "Gün" + yaxis: "Yeni konu sayısı" + posts: + title: "Gönderiler" + xaxis: "Gün" + yaxis: "Yeni gönderi sayısı" + likes: + title: "Beğeniler" + xaxis: "Gün" + yaxis: "Yeni beğenilerin sayısı" + flags: + title: "Bayraklar" + xaxis: "Gün" + yaxis: "Bayrakların sayısı" + bookmarks: + title: "İşaretlenenler" + xaxis: "Gün" + yaxis: "Yeni işaretlenenlerin sayısı" + starred: + title: "Yıldızlı" + xaxis: "Gün" + yaxis: "Yeni yıldızlı konuların sayısı" + users_by_trust_level: + title: "Güven Seviyesine ait Kullanıcı Sayısı" + xaxis: "Güven Seviyesi" + yaxis: "Kullanıcı Sayısı" + emails: + title: "Gönderilen E-postalar" + xaxis: "Gün" + yaxis: "E-posta Sayısı" + user_to_user_private_messages: + title: "Kullanıcıdan Kullanıcıya" + xaxis: "Gün" + yaxis: "Özel mesajların sayısı" + system_private_messages: + title: "Sistem" + xaxis: "Gün" + yaxis: "Özel mesajların sayısı" + moderator_warning_private_messages: + title: "Moderatör Uyarısı" + xaxis: "Gün" + yaxis: "Özel mesajların sayısı" + notify_moderators_private_messages: + title: "Moderatörleri Bilgilendir" + xaxis: "Gün" + yaxis: "Özel mesajların sayısı" + notify_user_private_messages: + title: "Kullanıcıyı Bilgilendir" + xaxis: "Gün" + yaxis: "Özel mesajların sayısı" + top_referrers: + title: "En Çok Atıfta Bulunanlar" + xaxis: "Kullanıcı" + num_clicks: "Tıklamalar" + num_topics: "Konular" + top_traffic_sources: + title: "En İyi Trafik Kaynakları" + xaxis: "Alan Adı" + num_clicks: "Tıklamalar" + num_topics: "Konular" + num_users: "Kullanıcılar" + top_referred_topics: + title: "En Çok Atıfta Bulunulan Konular" + xaxis: "Konu" + num_clicks: "Tıklamalar" + page_view_anon_reqs: + title: "Anonim" + xaxis: "Gün" + yaxis: "Anonim Sayfa Gösterimi" + page_view_logged_in_reqs: + title: "Giriş Yapılmış" + xaxis: "Gün" + yaxis: "Giriş Yapılmış Sayfa Gösterimi" + page_view_crawler_reqs: + title: "Ağ Gezgini" + xaxis: "Gün" + yaxis: "Ağ Gezgini Sayfa Gösterimi" + page_view_total_reqs: + title: "Toplam" + xaxis: "Gün" + yaxis: "Toplam Sayfa Gösterimi" + http_background_reqs: + title: "Arkaplan" + xaxis: "Gün" + yaxis: "Canlı güncelleme ve takip için kullanılan istekler" + http_2xx_reqs: + title: "Statü 2xx (OK)" + xaxis: "Gün" + yaxis: "Başarılı istekler (Statü 2xx)" + http_3xx_reqs: + title: "HTTP 3xx (Yönlendirme)" + xaxis: "Gün" + yaxis: "Yönlendirme istekleri (Statü 3xx)" + http_4xx_reqs: + title: "HTTP 4xx (Kullanıcı Hatası)" + xaxis: "Gün" + yaxis: "Kullanıcı Hataları (Statü 4xx)" + http_5xx_reqs: + title: "HTTP 5xx (Sunucu Hatası)" + xaxis: "Gün" + yaxis: "Sunucu Hataları (Statü 5xx)" + http_total_reqs: + title: "Toplam" + xaxis: "Gün" + yaxis: "Toplam istekler" + dashboard: + rails_env_warning: "Sunucunuz %{env} modunda çalışıyor." + ruby_version_warning: "Problemli olduğu bilinegelen Ruby 2.0.0 sürümünü kullanmaktasınız. Yama seviyesi 247 veya daha yenisine güncelleme yapınız." + host_names_warning: "config/database.yml dosyasınızda, bilgisayar adı olarak varsayılan değer olan \"localhost\" ayarlı. Değeri, sitenizin bilgisayar adını kullanacak biçimde güncelleyiniz." + gc_warning: 'Sunucunuz, Ruby''nin varsayılan çöp toplama ayarlarını kullanıyor ki bu size en iyi performansı vermeyecektir. Performansı ayarı için şu konuyu okuyun: Discourse için Ruby on Rails Ayarları.' + sidekiq_warning: 'Sidekiq çalışmıyor. E-posta yollamak gibi gibi birçok asenkron görev sidekiq''in işidir. En az bir tane sidekiq süreci çalıştırdığınızdan emin olun. Sidekiq ile ilgili bilgi burada.' + queue_size_warning: 'Kuyrukta çok fazla, %{queue_size} tane iş var. Bu Sidekiq süreçleriyle ilgili bir sorunu gösteriyor olabilir, daha fazla Sidekiq işçisi eklemeniz gerekebilir.' + memory_warning: 'Sunucunuz toplam 1GB''tan az bellek ile çalışıyor. En az 1GB bellek tavsiye edilmektedir.' + enable_google_logins_warning: "Google OpenID kimlik doğrulama sisteminin ıskartaya çıkartılmış bir sürümünü kullanıyorsunuz. Google OpenID'den desteğini 20 Nisan 2015'te çekecek. Bir an önce Google OAuth2 kullanmaya başlayın. Daha fazlasını öğrenmek için bu rehberi okuyun" + both_googles_warning: "Site ayarlarında enable_google_logins ve enable_google_oauth2_logins ayarlarından her ikisi de etkin. enable_google_logins ayarını devre dışı bırakın." + google_oauth2_config_warning: 'Sunucu Google OAuth2 (enable_google_oauth2_logins) ile üyelik oluşturulması ve giriş yapılmasına elveriyor, fakat the kullanıcı IDsi and gizli kullanıcı değerleri henüz ayarlanmamış. Site Ayarları sayfasına gidin ve ayarları güncelleyin. Daha fazla bilgi için bu yönetmeliğe bakın.' + facebook_config_warning: 'Sunucu Facebook (enable_facebook_logins) ile üyelik oluşturulması ve giriş yapılmasına izin veriyor, fakat app ID ve gizli app değerleri henüz ayarlanmamış. Site Ayarları sayfasına gidin ve ayarları güncelleyin. Daha fazla bilgi için bu yönetmeliğe bakın.' + twitter_config_warning: 'Sunucu Twitter (enable_twitter_logins) ile üyelik oluşturulması ve giriş yapılmasına izin veriyor, fakat anahtar ve gizli değerler henüz ayarlanmamış. Site Ayarları sayfasına gidin ve ayarları güncelleyin. Daha fazla bilgi için bu yönetmeliğe bakın.' + github_config_warning: 'Sunucu GitHub (enable_github_logins) ile üyelik oluşturulması ve giriş yapılmasına izin veriyor, fakat kullanıcı IDsi ve gizli değerler henüz ayarlanmamış. Site Ayarları sayfasına gidin ayarları güncelleyin. Daha fazla bilgi için bu yönetmeliğe bakın.' + s3_config_warning: 'Sunucu s3''e dosya yüklenebilmesi için yapılandırılmış, fakat şunlardan en az biri henüz ayarlanmamış: s3_access_key_id, s3_secret_access_key or s3_upload_bucket. Site Ayarları sayfasına gidin ve ayarları güncelleyin. Daha fazla bilgi için "How to set up image uploads to S3?" konulu gönderiye bakın.' + s3_backup_config_warning: 'Sunucu s3''e yedekleme yüklenebilmesi için yapılandırılmış, fakat şunlardan en az biri henüz ayarlanmamış: s3_access_key_id, s3_secret_access_key or s3_backup_bucket. Site Ayarları sayfasına gidin ve ayarları güncelleyin. Daha fazla bilgi için "How to set up image uploads to S3?" konulu gönderiye bakın.' + image_magick_warning: 'Sunucu büyük resimlerin küçük boylarının oluşturulması için yapılandırılmış, fakat ImageMagick henüz kurulmamış. Favori paket yöneticinizi kullanarak ImageMagick kurun veya son sürümünü indirin.' + failing_emails_warning: 'Başarısız sonuçlanmış %{num_failed_jobs} e-posta işlemi bulunuyor. config/discourse.conf dosyanızı kontrol edin ve e-posta sunucu ayarlarınızın doğru olduğundan emin olun. Sidekiq''deki başarısız işlemlere gözatın.' + default_logo_warning: "Siteniz için logo grafikleri yükleyin. Site Ayarları'nda logo_url, logo_small_url, ve favicon_url güncellemelerini yapın." + contact_email_missing: "Sitenizle alakalı acil durumlar için size ulaşabileceğimiz bir iletişim emaili girin. Lütfen Site Ayarları'nda iletişim emailinizi güncelleyin." + contact_email_invalid: "Site iletişim emaili geçersiz. İletişim emailini Site Ayarları sayfasından güncelleyin." + title_nag: "Sitenizin ismini girin. Site ismini Site Ayarları sayfasından güncelleyin." + site_description_missing: "Arama sonuçlarında gözükecek, sitenizi açıklayan tek bir cümle girin. Site Ayarları sayfasından site açıklamasını güncelleyin." + consumer_email_warning: "Sitenizde e-posta gönderimleri için Gmail (ya da başka bir e-posta hizmeti) kurulumu yapılmış. Gmail ile gönderebileceğiniz e-posta sayısı limitlidir. Onun yerine, emaillarınızın ulaştırılabildiğinden emin olmak için mandrill.com benzeri bir hizmet sağlayıcısını kullanın." + site_contact_username_warning: "Otomatik yollanan önemli özel mesajları gönderen kişi olarak gözükecek yardımsever adminin kullanıcı adını girin. Site Ayarları</a> sayfasından güncelleyin. " + notification_email_warning: "Bildiri emaillari alan adınıza bağlı geçerli bir email adresinden yollanmıyor; emaillar düzenli ve güvenilir bir şekilde ulaşmayabilir. Lütfen Site Ayarları sayfasından notification_email değeri için geçerli bir yerel email adresi girin." + content_types: + education_new_reply: + title: "Yeni Kullanıcı Eğitimi: İlk Cevaplar" + description: "Yeni kullanıcılar, ilk iki cevaplarını yazmaya başladıklarında, pop-up son-dakika yardım kılavuzu metin düzenleyecisinin üstünde otomatik olarak çıksın." + education_new_topic: + title: "Yeni Kullanıcı Eğitimi: İlk Konular" + description: "Yeni kullanıcılar, ilk iki cevaplarını yazmaya başladıklarında, pop-up son-dakika yardım kılavuzu metin düzenleyecisinin üstünde otomatik olarak çıksın." + usage_tips: + title: "Yeni Kullanıcı Kılavuzu" + description: "Yeni kullanıcılar için kılavuz ve gerekli bilgiler." + welcome_user: + title: "Hoşgeldiniz: Yeni Kullanıcı" + description: "Tüm yeni kullanıcılara, ilk üye olduklarında otomatik olarak gönderilen özel mesaj. " + welcome_invite: + title: "Hoşgeldiniz: Davetli Kullanıcı" + description: "Bir başka kullanıcı tarafından davet edilen yeni kullanıcılara, daveti kabul ettiklerinde gönderilen otomatik özel mesaj." + login_required_welcome_message: + title: "Giriş Yapmak Zorunlu: Hoşgeldiniz Mesajı" + description: "'Giriş yapmak zorunlu' ayarı etkinleştirildiğinde, sisteme giriş yapmamış olan ziyaretçilere gözüken hoşgeldiniz mesajı. " + login_required: + title: "Giriş Yapmak Zorunlu: Anasayfa" + description: "Siteye giriş yapmanın zorunlu olduğu durumlarda izin almamış kullanıcılara çıkan mesaj." + head: + title: "HTML başlık" + description: " etiketleri arasına eklenecek HTML." + top: + title: "Sayfaların en üstü" + description: "Her sayfanın en üstüne eklenecek HTML (sayfa başlığından sonra, navigasyondan ya da konu başlığından önce)." + bottom: + title: "Sayfaların en altı" + description: " etiketinden önce eklenecek HTML" + site_settings: + censored_words: " ■■■■ yerine otomatik olarak kullanılacak kelimeler" + delete_old_hidden_posts: "30 günden fazla süreyle gizli kalan gizlenmiş gönderileri otomatik olarak sil." + default_locale: "Bu Discourse yüklemesinin (ISO 639-1 Code) varsayılan dili" + allow_user_locale: "Kullanıcıların arayüz için kendi istedikleri dili seçmesine izin ver" + min_post_length: "Gönderide olması gereken en az karakter sayısı" + min_private_message_post_length: "Özel mesaj gönderilerinde olması gereken en az karakter sayısı" + max_post_length: "Gönderide izin verilen en fazla karakter sayısı" + min_topic_title_length: "Konuda olması gereken en az karakter sayısı" + max_topic_title_length: "Konu başlığında izin verilen en fazla karakter sayısı" + min_private_message_title_length: "Özel mesajın başlığında olabilecek en az karakter sayısı" + min_search_term_length: "Arama için girilecek kelimede olması gereken en az karakter sayısı" + allow_uncategorized_topics: "Konuların kategori seçmeden oluşturulmasına izin ver." + uncategorized_description: "Kategorisiz kategorisinin tanıtımı. Boş bırakabilirsiniz." + allow_duplicate_topic_titles: "Aynı başlık ile birden çok konu açılmasına izin ver." + unique_posts_mins: "Kullanıcının aynı içerikle yeni bir gönderi oluşturmadan önce geçmesi gereken dakika" + educate_until_posts: "Kullanıcılar, ilk (n) gönderilerini yazmaya başladıklarında, pop-up yeni kullanıcı eğitim paneli metin düzenleyecisinin üstünde çıksın." + title: "Sayfa başlığı etiketinde kullanılacak, bu sitenin ismi." + site_description: "Meta açıklama etiketinde kullanılacak, bu sitenin bir cümlelik açıklaması." + contact_email: "Bu site için sorumlu kişinin email adresi. Sadece acil durumlar için /about iletişim formu içerisinde ve beklenmeyen bayraklamalar gibi kritik bildiriler için kullanılacak." + queue_jobs: "SADECE YAZILIMCILAR İÇİN! UYARI! Aksi gerekmediği takdirde sidekiq işlerini hep otomatik sıraya koydurtun. Bu özellik devre dışı bırakılırsa, siteniz düzgün çalışmayacaktır." + crawl_images: "Doğru genişlik ve yükseklik boyutlarını girmek için uzak URL'lerdeki resimlerin birer kopyasını alınsın." + download_remote_images_to_local: "Uzaktaki resimler yerel resimlere çevirmek için indirilsin; bu ayar resim bağlantılarının kırılmasını önleyecektir" + download_remote_images_threshold: "Uzaktaki resimlerin yerele indirilmesi için gereken en az disk alanı (yüzdesel)" + disabled_image_download_domains: "Bu alan adlarından hiç bir zaman uzaktan resim indirme. Sınırlandırılmış liste." + ninja_edit_window: "Gönderi oluşturulduktan (n) saniye içerisinde bir düzenleme yapıldığında, gönderi tarihinde yeni bir versiyon yaratma." + post_edit_time_limit: "Yazar gönderiyi yayınladıktan sonra (n) dakika içerisinde gönderiyi düzenleyebilir ya da silebilir. Sonsuz için 0 girin." + edit_history_visible_to_public: "Düzenlenmiş gönderinin eski versiyonlarını herkesin görmesine izin ver. Bu özellik kapatıldığında, eski versiyonları sadece görevliler görebilir." + delete_removed_posts_after: "Yazar tarafından kaldırılan gönderiler (n) saat sonra otomatik olarak silinecektir. 0 olarak ayarlanırsa, gönderiler beklemeden hemen silinir." + max_image_width: "Bir gönderideki küçük resimlerin en fazla genişliği" + max_image_height: "Bir gönderideki küçük resimlerin en fazla yüksekliği" + category_featured_topics: "/categories sayfasında yer alan kategori başına düşen konu sayısı. Bu değeri değiştirirseniz, değişikliğin kategoriler sayfasına yansıması 15 dakika kadar sürebilir." + show_subcategory_list: "Bir kategoriye girildiğinde konu listesi yerine alt kategori listesini göster." + fixed_category_positions: "Seçerseniz, kategoriler için sabit bir sıralama belirleyebileceksiniz. Seçmezseniz, kategoriler aktivite sırasına göre listelenir. " + add_rel_nofollow_to_user_content: "İç bağlantılar (ana alan adları dahil) hariç, gönderilen tüm kullanıcı içeriklerine rel nofollow ekle. Bu ayarı değiştirirseniz, tüm gönderileri \"rake post:rebake\" ile rebake etmeniz gerekir." + exclude_rel_nofollow_domains: "Nofollow eklenmeyecek sınırlandırılmış alan adları listesi. (tld.com'a izin verirseniz otomatik olarak sub.tld.com'a da izin verilir.)" + post_excerpt_maxlength: "Gönderi alıntısının / özetinin en fazla uzunluğu." + post_onebox_maxlength: "Kutulanmış bir Discourse gönderisinin en fazla karakter uzunluğu" + onebox_domains_whitelist: "Kutulamaya izin verilen alan adları listesi; bu alan adları OpenGraph ya da oEmbed desteklemeliler. http://iframely.com/debug adresinden test edebilirsiniz." + logo_url: "Sitenizin sol üstünde yer alacak logo resmi; boş bırakılırsa, sitenin ismi gözükecek." + digest_logo_url: "Sitenizin özet e-postalarının üst kısmında kullanılacak logo. Boş bırakılırsa `logo_url` kullanılacak. örn: http://example.com/logo.png" + logo_small_url: "Sitenizin sol üstünde yer alacak, sayfayı aşağı kaydırdığınızda gözükecek küçük logo. Boş bırakılırsa, ana sayfa glifi gözükecek." + favicon_url: "Site favikonunuz. Bakın; http://en.wikipedia.org/wiki/Favicon" + mobile_logo_url: "Mobil sitenizin sol üstünde yer alacak sabit pozisyonlu logo resmi. Boş bırakılırsa, sitenin ismi kullanılacak." + apple_touch_icon_url: "Apple dokunmatik cihazları için kullanılan ikon. Önerilen boyut; 144 x 144 pixel." + notification_email: "Tüm önemli sistem e-postaları için kullanılacak olan gönderen e-posta adresi. E-postaların başarıyla ulaşması için buraya girilen alan adının SPF, DKIM ve reverse PTR kayıtlarının doğru yapılması lazım." + email_custom_headers: "Sınırlandırılmış özel e-posta başlıkları listesi" + email_subject: "Standart e-postaları için özelleştirilebilir konu formatı. Bakınız https://meta.discourse.org/t/customize-subject-format-for-standard-emails/20801" + use_https: "Sitenizin tam url'i (Discourse.base_url) http mi yoksa https uzantılı mı olmalı? HTTPS AYARLARINIZ TAMAMLANMADIYSA VE HENÜZ ÇALIŞMIYORSA BUNU ETKİNLEŞTİRMEYİN! " + summary_score_threshold: "Bir gönderinin 'Bu Konuyu Özetle' içinde yer alması için gereken en az skor." + summary_posts_required: "'Bu Konuyu Özetle'nin etkinleştirilmesi için konuda olması gereken en az gönderi sayısı" + summary_likes_required: "'Bu Konuyu Özetle'nin etkinleştirilmesi için konuda olması gereken en az beğeni sayısı" + summary_percent_filter: "Kullanıcı 'Bu Konuyu Özetle'ye tıkladığında, gönderinin ilk % kısmını göster" + summary_max_results: "'Bu Konuyu Özetle'den dönen en fazla gönderi sayısı" + enable_private_messages: "Güven seviyesi 1 olan kullanıcıların özel mesaj oluşturmasına ve özel mesajlara cevap vermesine izin ver" + enable_long_polling: "Bildiri için kullanılan message bus uzun sorgular yapabilir" + long_polling_base_url: "Uzun sorgular için kullanılan baz URL (CDN dinamik içerik sunuyorsa, bunu origin olarak ayarladığına emin ol) ör: http://origin.site.com" + long_polling_interval: "Gönderilecek bilgi olmadığı zaman sunucunun kullanıcılara geri dönmeden önce beklemesi gereken zaman (sadece giriş yapmış kullanıcın için)" + polling_interval: "Uzun sorgular yapılmadığı zaman, kaç mili saniyede bir giriş yapmış kullanıcılar poll yapmalı" + anon_polling_interval: "Kaç mili saniyede bir anonim kullanıcılar sorgu yapmalı" + background_polling_interval: "(Pencere arkaplanda olduğu zaman) kaç mili saniyede bir kullanıcılan sorgu yapmalı" + auto_track_topics_after: "Bir konu otomatik olarak takip edilmeye başlanılmadan önce varsayılan global mili saniye süre, kullanıcılar bu ayarı geçersiz kılabilir (her zaman için 0, hiç bir zaman için -1 girin)" + new_topic_duration_minutes: "Bir konunun yeni sayılması için varsayılan global dakika süre, kullanıcılar bu ayarı geçersiz kılabilir (her zaman için -1, son ziyaret için -2 girin)" + flags_required_to_hide_post: "Bir postun otomatik olarak gizlenmesi ve yazarına Özel Mesaj gönderilmesi için gereken bayrak sayısı (Hiç bir zaman için 0 girin)" + cooldown_minutes_after_hiding_posts: "Topluluk tarafından bayraklanarak gizlenen gönderiyi düzenleyebilmek için, kullanıcının beklemesi gereken dakika süresi " + max_topics_in_first_day: "Kullanıcının sitedeki ilk gününde, oluşturabileceği en fazla konu sayısı" + max_replies_in_first_day: "Kullanıcının sitedeki ilk gününde, oluşturabileceği en fazla cevap sayısı" + num_flags_to_block_new_user: "Eğer, yeni bir kullanıcının gönderileri num_flags_to_block_new_user farklı kullanıcıdan buradaki sayı kadar bayrak alırsa , kullanıcının bütün gönderilerini gizle ve yeni gönderi oluşturabilmesine izin verme. Devre dışı bırakmak için 0 girin." + num_users_to_block_new_user: "Eğer, yeni bir kullanıcının gönderileri buradaki sayı kadar farklı kullanıcı tarafından num_flags_to_block_new_user bayrak alırsa, kullanıcının bütün gönderilerini gizle ve yeni gönderi oluşturabilmesine izin verme. Devre dışı bırakmak için 0 girin." + notify_mods_when_user_blocked: "Eğer bir kullanıcı otomatik olarak engellendiyse, tüm moderatörlere mesaj yolla." + flag_sockpuppets: "Eğer, yeni kullanıcı konuya, konuyu başlatan yeni kullanıcı ile aynı IP adresinden cevap yazarsa, her iki gönderiyi de potansiyel spam olarak işaretle. " + traditional_markdown_linebreaks: "Markdown'da, satır sonundan önce yazının sağında iki tane boşluk gerektiren, geleneksel satır sonu metodunu kullan kullan." + post_undo_action_window_mins: "Kullanıcıya tanınan, bir gönderide yapılan yeni aksiyonları (beğenme, bayraklama, vs) geri alabilme dakika süresi" + must_approve_users: "Siteye erişimlerine izin verilmeden önce tüm yeni kullanıcı hesaplarının görevliler tarafından onaylanması gerekir." + ga_tracking_code: "Google analytics (ga.js) takip kodu, ör: UA-12345678-9; bakınız http://google.com/analytics" + ga_domain_name: "Google analytics (ga.js) alan adı, ör: mysite.com; bakınız http://google.com/analytics" + ga_universal_tracking_code: "Google Universal Analytics (analytics.js) takip kodu, ör: UA-12345678-9; bakınız http://google.com/analytics" + ga_universal_domain_name: "Google Universal Analytics (analytics.js) alan adı, ör: mysite.com; bakınız http://google.com/analytics" + enable_escaped_fragments: "Webcrawler algılanmadığı zaman Google'ın Ajax-Crawling API'sine başvur. Bakınız https://support.google.com/webmasters/answer/174992?hl=en" + enable_noscript_support: "Noscript etiketi üzerinden standart webcrawler arama motoru desteğini etkinleştir" + allow_moderators_to_create_categories: "Moderatörlerin yeni kategoriler oluşturmasına izin ver" + cors_origins: "Cross-origin isteklerin (CORS) izin verilen originler. Her origin http:// veya https:// içermeli. CORS'u etkinleştirebilmek için DISCOURSE_ENABLE_CORS env değişkeni doğru olarak ayarlanmalı." + top_menu: "Anasayfa navigasyonunda nelerin, hangi sırada yer alacağını belirleyin. Örneğin en sonIyeniIokunmamışIkategorilerIen popülerIokunmuşlarIgönderilenlerIişaretlenenler" + post_menu: "Gönderi menüsündeki öğelerin hangi sırada yer alacağını belirleyin. Örnek: beğen|düzenle|bayrakla|sil|paylaş|işaretle|cevapla" + post_menu_hidden_items: "Gönderi menüsündeki maddeler, genişletme üç noktasına tıklanmadığı takdirde otomatik olarak gizlenir. " + share_links: "Paylaşım penceresinde hangi maddelerin ne sırada gözükeceğini belirleyin." + track_external_right_clicks: "URL'leri tekrar yazdığı için, sağ tıklanan dış bağlantıların (ör: yeni bir sekmede aç) takibi varsayılan ayarlarda devre dışı bırakılmıştır." + site_contact_username: "Tüm otomatik özel mesajlar için gönderen kişi olarak gözükecek geçerli bir görevli kullanıcı adı. Boş bırakılırsa varsayılan Sistem hesabı kullanılacak." + send_welcome_message: "Tüm yeni kullanıcılara bir hoşgeldiniz özel mesajı ile beraber hızlı başlangıç kılavuzu gönderin." + suppress_reply_directly_below: "Bu gönderinin direk altında sadece tek bir cevap varsa, gönderideki açılabilir cevap sayısı bölümünü gösterme." + suppress_reply_directly_above: "Bu gönderinin direk üstünde sadece tek bir cevap varsa, gönderideki açılabilir hangi-cevaba-istinaden-cevapla bölümünü gösterme." + suppress_reply_when_quoting: "Gönderi cevabı alıntılarsa, gönderideki açılabilir hangi-cevaba-istinaden-cevapla bölümünü gösterme." + max_reply_history: "Hangi-cevaba-istinaden-cevapla bölümü açılınca gösterilecek en fazla cevap sayısı" + experimental_reply_expansion: "Hangi-cevaba-istinaden-cevapla bölümü açılınca ara cevapları gizle (deneysel)" + topics_per_period_in_top_summary: "Popüler konular özetinde gösterilen popüler konu sayısı." + topics_per_period_in_top_page: "'Daha Fazla Göster' ile genişletilen popüler konular bölümünde gösterilecek popüler konu sayısı. " + redirect_users_to_top_page: "Yeni ve uzun süredir giriş yapmamış kullanıcıları otomatik olarak Popüler sayfasına yönlendir." + show_email_on_profile: "Kullanıcının e-posta adresini profilinde göster (sadece kendilerine ve site görevlilerine gözükür)" + email_token_valid_hours: "Parolamı unuttum / hesap etkinleştirme jetonları (n) saat geçerlidir." + email_token_grace_period_hours: "Parolamı unuttum / hesabı etkinleştir jetonları kullanıldıktan sonra (n) saat boyunca hala geçerlidir." + enable_badges: "Rozet sistemini etkinleştir" + allow_index_in_robots_txt: "robots.txt dosyasında bu sitenin arama motorları tarafından indekslenmesine izin verildiğini belirt." + email_domains_blacklist: "Kullanıcıların kayıt olurken kullanamayacağı e-posta alan adlarının listesi. Örneğin: mailinator.com trashmail.net" + email_domains_whitelist: "Kullanıcıların kayıt olurken kullanmak ZORUNDA olduğu e-posta alan adlarının listesi. UYARI: Bu listede yer almayan e-posta uzantıları kabul edilmeyecektir!" + forgot_password_strict: "Parolamı unuttum alanını kullanırlarken, kullanıcıları hesabın varlığından haberdar etme. " + log_out_strict: "Çıkış yapılırken, kullanıcının tüm cihazlardaki TÜM seanslarını sonlandır" + version_checks: "Discourse Hub'a versiyon güncellemeleri için ping yolla ve yeni versiyon mesajlarına /admin gösterge panelinde yer ver" + new_version_emails: "Discourse'un yeni versiyonu çıktığında contact_email adresine e-posta gönder." + port: "SADECE YAZILIMCILAR İÇİN! UYARI! Varsayılan 80 portu yerine bu HTTP portunu kullanın. Varsayılan 80'i kullanmak için boş bırakın. " + force_hostname: "SADECE YAZILIMCILAR İÇİN! DİKKAT! URL'de bir bilgisayar adı belirleyin. Varsayılan için boş bırakın." + invite_expiry_days: "Kullanıcı davet anahtarlarının geçerlilik süresi, gün olarak" + invite_passthrough_hours: "Daha önce kabul edilmiş davetiye anahtarının kullanım süresi, saat olarak" + invite_only: "Halka açık kayıt sistemi devre dışı bırakıldı, tüm yeni kullanıcıların bir üye ya da görevli tarafından davet edilmesi gerekir. " + login_required: "Bu sitede içerik görüntülenebilmesi için kimlik doğrulamayı zorunlu kıl, isimsiz girişe izin verme." + min_username_length: "Kullanıcı adında olması gereken en az karakter sayısı. UYARI: HALİHAZIRDA BUNDAN DAHA KISA BİR KULLANICI ADINA SAHİP OLAN KULLANICILAR SİTEYE GİRİŞ YAPAMAYACAKLAR. " + max_username_length: "Kullanıcı adında olabilecek en fazla karakter sayısı. UYARI: HALİHAZIRDA BUNDAN DAHA UZUN BİR KULLANICI ADINA SAHİP OLAN KULLANICILAR SİTEYE GİRİŞ YAPAMAYACAKLAR. " + min_password_length: "En az parola uzunluğu." + block_common_passwords: "En çok kullanılan 10,000 parola arasında yer alan parolalara izin verme." + enable_sso: "Dış bir site aracılığı ile tek oturum açma sistemini etkinleştir. (UYARI: etkinleştirildiği takdirde, doğru yapılandırılmamışsa bazı kişilerin giriş yapmasını engelleyebilir; ayrıca davetiye sistemini de devre dışı bırakır)" + enable_sso_provider: " /session/sso_provider son noktasında Discourse SSO uygula, sso_secret değerinin seçilmiş olmasını gerektirir" + sso_url: "Bitiş noktasındaki tek oturum açma URL'i" + sso_secret: "TOA bilgisinin kritopgrafik şekilde doğrulanması için kullanılan gizli string, 10 karakter veya daha uzun olduğundan emin olun" + sso_overrides_email: "SSO yararlı yükündeki dış site e-postasını yerel e-postanın üzerine yazar (DİKKAT: yerel e-postaların normalizasyon sürecinde uyuşmazlıklar doğabilir)" + sso_overrides_username: "SSO yararlı yükündeki dış site kullanıcı adını yerel kullanıcı adının üzerine yazar (DİKKAT: kullanıcı adı uzunlukları/gereksinimleri arasındaki farklılıklardan ötürü uyuşmazlıklar yaşanabilir)" + sso_overrides_name: "SSO yararlı yükündeki dış site ismini yerel ismin üzerine yazar (DİKKAT: yerel isimlerin normalizasyon sürecinde uyuşmazlıklar doğabilir)" + sso_overrides_avatar: "SSO yararlı yükündeki dış site avatarını kullanıcı avatarının üzerine yazar Eğer etkinleştirildiyse, allow_uploaded_avatars ayarının devre dışı bırakılması şiddetle önerilir" + enable_local_logins: "Yerel kullanıcı adı ve parola bazlı hesap girişlerini etkinleştir. (Not: davetiyelerin çalışabilmesi için bu ayarın etkinleştirilmesi gerekli)" + allow_new_registrations: "Yeni kayıtlara izin ver. Yeni hesap oluşturulmasını engellemek için burayı işaretlemeyin." + enable_google_logins: "(ıskartaya çıkartılmış) Google doğrumalasını etkinleştir. Bu Google'ın artık ıskartaya çıkardığı OPENID doğrulama metodudur. Yeni kurulumlar bununla çalışmayacaktır. Yerine Google Oauth2 kullanın. Varolan kurulumlar 20 Nisan 2015 tarihine kadar Google Oauth2'ye geçmeli." + enable_yahoo_logins: "Yahoo doğrulamasını etkinleştir" + enable_google_oauth2_logins: "Google Oauth2 doğrulamasını etkinleştir. Bu Google'ın şu an desteklediği doğrulama metodu. Anahtar ve secret gerektirir." + google_oauth2_client_id: "Google uygulamanıza ait Müşteri ID'si." + google_oauth2_client_secret: "Google uygulamanıza ait client secret değeri." + enable_twitter_logins: "Twitter doğrulamasını etkinleştir. twitter_consumer_key ve twitter_consumer_secret gereklidir" + twitter_consumer_key: "Twitter doğrulaması için gereken, http://dev.twitter.com adresinde kayıtlı consumer key" + twitter_consumer_secret: "Twitter doğrulaması için gereken, http://dev.twitter.com adresinde kayıtlı consumer secret" + enable_facebook_logins: "Facebook doğrulamasını etkinleştir. facebook_app_id ve facebook_app_secret gerekir" + facebook_app_id: "Facebook kimlik doğrulama için gerekli, https://developers.facebook.com/apps sayfasında bulunan app id" + facebook_app_secret: "Facebook kimlik doğrulama için gerekli, https://developers.facebook.com/apps sayfasında bulunan app secret" + enable_github_logins: "Github doğrulamasını etkinleştirin, github_client_id ve github_client_secret gereklidir" + github_client_id: "Github doğrulaması için gereken, https://github.com/settings/applications adresinde kayıtlı client id" + github_client_secret: "Github doğrulaması için gereken, https://github.com/settings/applications adresinde kayıtlı client secret" + allow_restore: "Geri almaya izin ver. Tüm sitedeki verileri değiştirebilir! Bir yedeklemeyi geri yüklemeyi planlamıyorsanız devre dışı bırakın." + maximum_backups: "Diskte tutulacak en fazla yedek sayısı. Eski yedekler otomatik olarak silinir." + backup_daily: "Siteyi günde bir kere otomatik olarak yedekle." + enable_s3_backups: "Tamamlanınca yedeklemeleri S3'e yükle. ÖNEMLİ: Dosyalar ayarında doğru S3 girilmesini gerektirir" + s3_backup_bucket: "Yedeklemelerin yüklenmesi için uzak biriktirme yeri. UYARI: Özel bir biriktirme yeri olduğundan emin olun" + active_user_rate_limit_secs: "'last_seen_at' alanını ne kadar sıklıkta güncelliyoruz, saniye olarak" + verbose_localization: "UI'da genişletilmiş yerelleştirme ipuçlarını göster" + previous_visit_timeout_hours: "Ziyaretin üzerinden burada belirtilen saat kadar süre geçtiğinde, ziyareti \"bir önceki\" olarak nitelendir" + rate_limit_create_topic: "Bir konu oluşturduktan sonra, başka bir konu oluşturmak için kullanıcılar (n) saniye beklemeli. " + rate_limit_create_post: "Bir gönderi oluşturduktan sonra, başka bir gönderi oluşturmak için kullanıcılar (n) saniye beklemeli. " + rate_limit_new_user_create_topic: "Bir konu oluşturduktan sonra, başka bir konu oluşturmak için yeni kullanıcılar (n) saniye beklemeli. " + rate_limit_new_user_create_post: "Bir gönderi oluşturduktan sonra, başka bir gönderi oluşturmak için yeni kullanıcılar (n) saniye beklemeli. " + max_likes_per_day: "Bir günde, bir kullanıcının verilebileceği en fazla beğeni sayısı." + max_flags_per_day: "Bir günde, bir kullanıcının verebileceği en fazla bayrak sayısı. " + max_bookmarks_per_day: "Kullanıcı başına düşen günlük en fazla işaretleme sayısı." + max_edits_per_day: "Bir günde, bir kullanıcının yapabileceği en fazla düzenleme sayısı." + max_topics_per_day: "Bir günde, bir kullanıcının oluşturabileceği en fazla konu sayısı." + max_private_messages_per_day: "Bir günde, bir kullanıcının oluşturabileceği en fazla özel mesaj sayısı." + max_invites_per_day: "Bir günde, bir kullanıcının yollayabileceği maksimum davet sayısı." + suggested_topics: "Konunun en altında yer alan, önerilen konu sayısı. " + limit_suggested_to_category: "Önerilen konularda sadece kullanıcının bulunduğu kategoriye ait konuları göster." + clean_up_uploads: "Yasadışı barındırmayı engellemek için artık referanssız yüklemeleri kaldır. UYARI: Bu ayarı etkinleştirmeden önce /uploads dizinini yedeklemeyi isteyebilirsiniz" + clean_orphan_uploads_grace_period_hours: "Artık yüklemeler kaldırılmadan önce tanınan süre (saat olarak)." + purge_deleted_uploads_grace_period_days: "Silinen yüklemelerin kaldırılması için tanınan süre (gün olarak)." + purge_unactivated_users_grace_period_days: "Hesabını etkinleştirmemiş bir kullanıcının sistemden silinmesi için tanınan süre (gün olarak)." + enable_s3_uploads: "Yüklemeleri Amazon S3 belleklerinde sakla. ÖNEMLİ: Doğru S3 bilgileri (erişim anahtarı id & gizli erişim anahtarı) gerektirir" + s3_use_iam_profile: 'Anahtarları almak için AWS EC2 IAM rolünü kullan. NOT: Etkinleştirilmesi "s3 access key id" and "s3 secret access key" ayarlarının üzerine yazacaktır.' + s3_upload_bucket: "Dosyaların yükleneceği Amazon S3 biriktirme yeri adı. DİKKAT: büyük harfler, nokta ve altçizgi karakterleri kullanılmamalı." + s3_access_key_id: "Resimleri yüklemek için kullanılacak olan Amazon S3 access key id." + s3_secret_access_key: "Resimleri yüklemek için kullanılacak olan Amazon S3 secret access key." + s3_region: "Resimleri yüklemek için kullanılacak olan Amazon S3 region name." + enable_flash_video_onebox: "Kutularda swf ve flv (Adobe Flash) yerleştirmelerine izin ver. UYARI: güvenlik açıkları doğurabilir" + default_invitee_trust_level: "Davet edilen kullanıcılar için varsayılan güven seviyesi (0-4)." + default_trust_level: "Tüm yeni kullanıcılar için varsayılan güven seviyesi (0-4)." + tl1_requires_topics_entered: "Yeni bir kullanıcının güven seviyesi 1'e yükseltilmeden önce oluşturması gereken konu sayısı." + tl1_requires_read_posts: "Yeni bir kullanıcının güven seviyesi 1'e yükseltilmeden önce okuması gereken gönderi sayısı." + tl1_requires_time_spent_mins: "Yeni bir kullanıcının güven seviyesi 1'e yükseltimeden önce gönderi okuyarak geçirmesi gereken dakika miktarı." + tl2_requires_topics_entered: "Bir kullanıcının güven seviyesi 2'ye yükseltimeden önce oluşturması gereken konu sayısı." + tl2_requires_read_posts: "Bir kullanıcının güven seviyesi 2'ye yükseltimeden önce okuması gereken gönderi sayısı." + tl2_requires_time_spent_mins: "Bir kullanıcının güven seviyesi 2'ye yükseltimeden önce gönderi okuyarak geçirmesi gereken dakika miktarı." + tl2_requires_days_visited: "Bir kullanıcının güven seviyesi 2'ye yükseltilmeden önce siteye girmesi gereken gün sayısı. " + tl2_requires_likes_received: "Bir kullanıcının güven seviyesi 2'ye yükseltilmeden önce alması gereken beğeni sayısı." + tl2_requires_likes_given: "Bir kullanıcının güven seviyesi 2'ye yükseltilmeden önce vermesi gereken beğeni sayısı." + tl2_requires_topic_reply_count: "Bir kullanıcının güven seviyesi 2'ye yükseltilmeden önce cevaplaması gereken konu sayısı." + tl3_requires_days_visited: "Bir kullanıcının güven seviyesi 3'e yükseltimeye hak kazanması için, son 100 günde siteyi ziyaret etmesi gereken en az gün sayısı. (0 - 100)" + tl3_requires_topics_replied_to: "Bir kullanıcının güven seviyesi 3'e yükseltilmeye hak kazanması için son 100 günde cevap yazması gereken en az konu sayısı. (0 ya da daha fazla)" + tl3_requires_topics_viewed: "Bir kulanıcının güven seviyesi 3'e yükseltilmeye hak kazanması için, son 100 gün içinde oluşturulmuş konulardan görüntülemesi gereken yüzde oranı. (0 ile 100 arası)" + tl3_requires_posts_read: "Bir kulanıcının güven seviyesi 3'e yükseltilmeye hak kazanması için, son 100 gün içinde oluşturulmuş gönderilerden görüntülemesi gereken yüzde oranı. (0 ile 100 arası)" + tl3_requires_topics_viewed_all_time: "Bir kullanıcının güven seviyesi 3'e yükselmeye hak kazanması için görüntülemesi gereken toplam en az konu sayısı." + tl3_requires_posts_read_all_time: "Bir kullanıcının güven seviyesi 3'e yükselmeye hak kazanması için görüntülemesi gereken toplam en az gönderi sayısı." + tl3_requires_max_flagged: "Bir kullanıcının güven seviyesi 3'e yükseltilmeye hak kazanması için, son 100 gün içinde x farklı kullanıcı tarafından x adetten fazla gönderisinin bayraklanmamış olmaması gerekir. x bu ayara verilen değerdir. (0 ya da daha yüksek) " + tl3_promotion_min_duration: "Bir kullanıcının güven seviyesi 2'ye düşürülebilmesi için güven seviyesi 3'te geçirmesi gereken en az gün sayısı." + tl3_requires_likes_given: "Bir kullanıcının güven seviyesi 3'e yükseltilmeye hak kazanması için son 100 günde vermesi gereken en az beğeni sayısı. " + tl3_requires_likes_received: "Bir kullanıcının güven seviyesi 3'e yükseltilmeye hak kazanması için son 100 günde alması gereken en az beğeni sayısı. " + tl3_links_no_follow: "Güven seviyesi 3'teki kullanıcılar tarafından paylaşılan linklerdeki rel=nofollow'u kaldırma." + min_trust_to_create_topic: "Yeni bir konu oluşturmak için gereken en az güven seviyesi. " + min_trust_to_edit_wiki_post: "Wiki olarak işaretlenmiş bir gönderiyi düzenleyebilmek için gereken en az güven seviyesi." + newuser_max_links: "Yeni bir kullanıcının bir gönderiye ekleyebileceği bağlantı sayısı." + newuser_max_images: "Yeni bir kullanıcının bir gönderiye ekleyebileceği resim sayısı." + newuser_max_attachments: "Yeni bir kullanıcının bir gönderiye ekleyebileceği dosya sayısı." + newuser_max_mentions_per_post: "Yeni bir kullanıcının bir gönderi içinde kullanabileceği en fazla @isim bildiri sayısı." + newuser_max_replies_per_topic: "Yeni bir kullanıcının tek bir konu içerisinde, başka bir kullanıcı ona cevap yazana kadar, girebileceği en fazla cevap sayısı. " + max_mentions_per_post: "Bir kullanıcının bir gönderi içinde kullanabileceği en fazla @isim bildiri sayısı." + create_thumbnails: "Gönderiye sığmayacak kadar büyük olan görseller için küçük resimler ve lightbox resimleri oluştur." + email_time_window_mins: "Kullanıcılara, gönderilerini düzenlemelerine ve tamamlamalarına fırsat vermek için herhangi bir bildiri e-postası göndermeden önce (n) dakika bekleyin." + email_posts_context: "Genel durumu göstermek amaçlı, bildiri e-postalarında yer alacak önceki cevap sayısı." + flush_timings_secs: "Zaman verisinin sunucuya atılma sıklığı, saniye olarak." + max_word_length: "Konu başlığında bulunabilecek en fazla karakter sayısı." + title_min_entropy: "Konu başlığı için gereken en az entropi (tekil karakter, ingilizce-dışı karakterler daha fazla sayılır)" + body_min_entropy: "Gönderi içeriği için gereken en az entropi (tekil karakter, ingilizce-dışı karakterler daha fazla sayılır)" + title_fancy_entities: "SmartyPants tarzında, konu başlıklarında ASCII karakterlerini süslü HTML öğelerine çevir: http://daringfireball.net/projects/smartypants/" + min_title_similar_length: "Bir başlığın benzer konular için kontrolünün yapılmasından önce sahip olması gereken en az uzunluk." + min_body_similar_length: "Bir gönderi metninin benzer konular için kontrolünün yapılmasından önce sahip olması gereken en az uzunluk." + category_colors: "Kategoriler için izin verilen onaltılı renk değerlerinin listesi" + category_style: "Kategori rozetleri için görsel stil." + max_image_size_kb: "KB cinsinden yüklenebilecek en fazla resim büyüklüğü. Bu nginx (client_max_body_size) / apache veya proxyde de ayarlanmalı." + max_attachment_size_kb: "KB cinsinden yüklenebilecek en fazla eklenti dosyası büyüklüğü. Bu nginx (client_max_body_size) / apache veya proxyde de ayarlanmalı." + authorized_extensions: "Yüklenebilecek dosya uzantılarının listesi (tüm dosya türlerini etkinleştirmek için '*' kullanın)" + max_similar_results: "Yeni bir konu oluştururken, düzenleyicinin üzerinde gösterilecek benzer konuların sayısı. Karşılaştırmalar başlık ve içerik üzerinden yapılır." + title_prettify: "Tümü büyük harf, ilk karakteri küçük harf, çoklu ! ve ?, sonda ekstra . kullanımı gibi, sık yapılan yazım hatalarını önle." + topic_views_heat_low: "Bu kadar görüntülemeden sonra, görüntülemeler alanı hafifçe vurgulanacak." + topic_views_heat_medium: "Bu kadar görüntülemeden sonra, görüntülemeler alanı kısmen vurgulanacak." + topic_views_heat_high: "Bu kadar görüntülemeden sonra, görüntülemeler alanı belirgin şekilde vurgulanacak." + cold_age_days_low: "Sohbet başlangıcından bu kadar süre geçtiğinde, son aktivite tarihi hafifçe vurgulanır." + cold_age_days_medium: "Sohbet başlangıcından bu kadar süre geçtiğinde, son aktivite tarihi kısmen vurgulanır." + cold_age_days_high: "Sohbet başlangıcından bu kadar süre geçtiğinde, son aktivite tarihi belirgin şekilde vurgulanır." + history_hours_low: "Bu kadar saat içerisinde düzenlenen bir gönderinin, düzenlendiğini belirten gösterge hafifçe vurgulanacak. " + history_hours_medium: "Bu kadar saat içerisinde düzenlenen bir gönderinin, düzenlendiğini belirten gösterge kısmen vurgulanacak. " + history_hours_high: "Bu kadar saat içerisinde düzenlenen bir gönderinin, düzenlendiğini belirten gösterge belirgin şekilde vurgulanacak. " + topic_post_like_heat_low: "Beğeni:gönderi oranı bu oranı geçtiğinde, gönderi sayısı alanı hafifçe vurgulanır. " + topic_post_like_heat_medium: "Beğeni:gönderi oranı bu oranı geçtiğinde, gönderi sayısı alanı kısmen vurgulanır." + topic_post_like_heat_high: "Beğeni:gönderi oranı bu oranı geçtiğinde, gönderi sayısı alanı belirgin şekilde vurgulanır." + faq_url: "Başka yerde barındırılan bir SSS'i kullanmak istiyorsanız, ilgili URL adresini buraya eksiksiz şekilde girin." + tos_url: "Başka yerde barındırılan bir Üyelik Sözleşmesi dökümanını kullanmak istiyorsanız, ilgili URL adresini buraya eksiksiz şekilde girin." + privacy_policy_url: "Başka yerde barındırılan bir Gizlilik İlkeleri dökümanını kullanmak istiyorsanız, ilgili URL adresini buraya eksiksiz şekilde girin." + newuser_spam_host_threshold: "Yeni bir kullanıcı, `newuser_spam_host_posts` gönderileri içerisinde aynı makineye, kaç kere bağlantı paylaşınca spam olarak algılansın." + white_listed_spam_host_domains: "Spam barındırma testinden hariç tutulan alan adı listesi. Yeni kullanıcıların, bu alan adlarına bağlantı içeren gönderi oluşturmaları hiç bir zaman engellenmeyecek." + staff_like_weight: "Görevlilerin beğenilere verilecek ekstra ağırlık faktörü." + levenshtein_distance_spammer_emails: "Spam e-postaları eşleştirilirken, bulanık eşleşme için tahammül edilecek karakter sayısı farklılığı." + max_new_accounts_per_registration_ip: "Eğer bu IP'den güven seviyesi 0 olan halihazırda (n) hesap varsa (hiçbiri görevli, GS2 ya da daha yüksek seviyede biri değilse), bu IP'den yeni üyelik kabul etme. " + min_ban_entries_for_roll_up: "Topla butonuna tıklandığında, (N) adetten fazla giriş varsa yeni bir subnet engelleme girişi yaratılacak." + max_age_unmatched_emails: "Taranmış e-posta kayıtlarından karşılığı olmayanları (N) gün sonunda sil. " + max_age_unmatched_ips: "Taranmış IP girişlerinden karşılığı olmayanları (N) gün sonunda sil." + num_flaggers_to_close_topic: "Bir konunun moderatör müdahalesi için otomatik olarak durdurulmadan önce alması gereken en az tekil bayrak sayısı" + num_flags_to_close_topic: "Bir konunun moderatör müdahalesi için otomatik olarak durdurulmadan önce alması gereken en az etkin bayrak sayısı " + reply_by_email_enabled: "Konulara e-posta üzerinden cevap yazmayı etkinleştir." + reply_by_email_address: "Email ile cevapla özelliği için gelen e-posta adresi şablonu, örnek: %{reply_key}@reply.example.com or replies+%{reply_key}@example.com" + disable_emails: "Discourse'un herhangi bir e-posta göndermesine izin verme" + strip_images_from_short_emails: "2800 Byte boyutundan küçük e-postalardan resimleri çıkar" + short_email_length: "Kısa e-postanın uzunluğu, Byte olarak" + pop3_polling_enabled: "E-posta cevapları için POP3 üzerinden sorgula" + pop3_polling_ssl: "POP3 sunucusuna bağlanırken SSL kullanın. (Tavsiye edilir)" + pop3_polling_period_mins: "Periyodik olarak POP3 hesabının e-posta için kontrol edilmesinin dakika bazında sıklığı. NOT: yeniden başlatma gerektirir" + pop3_polling_port: "POP3 hesabının sorgulanacağı port" + pop3_polling_host: "POP3 üzerinden e-postaların sorgulanacağı sunucu" + pop3_polling_username: "E-postaların sorgulanacağı POP3 hesabının kullanıcı adı." + pop3_polling_password: "E-postaların sorgulanacağı POP3 hesabının parolası." + email_in: "Kullanıcıların e-posta aracılığıyla yeni konu oluşturabilmesine izin ver (pop3 sorgulaması gerektirir). Adresleri her kategorinin \"Ayarlar\" sekmesinden düzenleyin." + email_in_min_trust: "Bir kullanıcının e-posta aracılığı ile yeni konu oluşturabilmesi için sahip olması gereken en az güven seviyesi." + email_prefix: "E-postaların konu bölümünü belirten [etiket]. Boş bırakılırsa 'title' yazacak." + email_site_title: "Bu siteden giden e-postalarda gönderici olarak kullanılacak sitenin başlığı. Herhangi bir şey girilmezse varsayılan olarak 'title' yazacak. Eğer 'başlık\" içeriğinizde, email göndericisinin stringlerinde izin verilmeyen karakterler varsa, bu ayarı kullanın. " + minimum_topics_similar: "Yeni konu oluşturulurken, benzer konuların gösterilmesi için sitede olması gereken konu sayısı." + relative_date_duration: "Gönderinin üstünden bu kadar gün geçtikten sonra, gönderi tarihi mutlak şekilde değil (20 Şubat) göreceli şekilde (7g) gösterilecek." + delete_user_max_post_age: "İlk gönderisini (x) günden eski olan kullanıcıların silinmesine izin verme." + delete_all_posts_max: "Tüm Gönderileri Sil butonuna basıldığında tek seferde silinebilecek en fazla gönderi sayısı. Eğer bir kullanıcının gönderi sayısı bu sayıdan fazlaysa, gönderilerin hepsi tek seferde silinemez ve bu kullanıcı silinemez." + username_change_period: "Kayıt sonrası, kullanıcı adınının değiştirilebileceği gün sayısı. (Kullanıcı adının değiştirilebilmesini devre dışı bırakmak için 0 girin)" + email_editable: "Kullanıcıların kayıt olduktan sonra e-posta adreslerini değiştirmesine izin ver." + logout_redirect: "Çıkış yaptıktan sonra tarayıcının yönlendirileceği sayfa, ÖRN: (http://somesite.com/logout)" + allow_uploaded_avatars: "Kullanıcıların kendilerine özel avatarlar yüklemesine izin ver. " + allow_animated_avatars: "Kullanıcıların animasyonlu gif avatarları kullanmalarına izin ver. UYARI: bu ayarı değiştirdikten sonra avatars:refresh rake görevini çalıştırın." + allow_animated_thumbnails: "Animasyonlu giflerin, animasyonlu küçük resmini oluşturur." + automatically_download_gravatars: "Hesap oluşturma veya e-posta değişikliği esnasında kullanıcılar için Gravatarları indir" + digest_topics: "Özet e-postalarda yer alacak en fazla konu sayısı. " + digest_min_excerpt_length: "Özet e-postalarında, gönderi alıntılarında olması gereken en az karakter sayısı." + default_digest_email_frequency: "Varsayılan olarak, özet e-postalar hangi sıklıkta gönderilsin? Üyeler, ayarlar sayfasından bu değeri değiştirebilir." + suppress_digest_email_after_days: "Siteye (n) günden fazla süredir uğramayan kullanıcılar için özel email gönderimini durdur" + disable_digest_emails: "Tüm kullanıcılar için özet emaillarını devre dışı bırak." + default_external_links_in_new_tab: "Dış bağlantıları yeni sekmede aç. Üyeler, ayarlar sayfasından bu ayarı değiştirebilir." + detect_custom_avatars: "Olsun ya da olmasın kullanıcıların özel avatar yükleyip yüklemediğini kontrol et." + max_daily_gravatar_crawls: "Discourse'un gün içinde özel avatarlar için Gravatar'ı en fazla kaç kere kontrol edeceği." + public_user_custom_fields: "Kullanıcıların herkes tarafından görüntülenebilir özel alanlar oluşturmaları için beyaz liste." + allow_profile_backgrounds: "Kullanıcıların profillerine arkaplan eklemesine izin ver." + sequential_replies_threshold: "Kullanıcının ardarda çok fazla cevap gönderdiğine dair uyarı alması için, bir konuda üstüste yapması gereken gönderi sayısı. " + enable_mobile_theme: "Mobil cihazlar mobil uyumlu temayı kullanır, dilerse masaüstü görünüme geçebilirler. Eğer özel, duyarlı bir stil kullanıyorsanız bunu devredışı bırakın." + dominating_topic_minimum_percent: "Konuyu domine ettiğine dair uyarı almadan önce konudaki gönderilerin yüzde kaçının kullanıcıya ait olması gerekir." + suppress_uncategorized_badge: "Kategorisiz konular için olan rozeti konu listesinde gösterme." + global_notice: "Tüm ziyaretçilere İVEDİ ACİL DURUM global manşet uyarısı göster, saklamak için boş bırakın (HTML kullanılabilir)." + disable_edit_notifications: " 'download_remote_images_to_local' etkin olduğunda, sistem kullanıcısından gelen düzenleme bildirilerini devre dışı bırakır" + enable_names: "Kullanıcıların tam adlarını profillerde, kullanıcı kartlarında ve emaillarda göster. Tam adların hiç bir yerde görünmemesi için devre dışı bırak." + display_name_on_posts: "Gönderilerde @kullanıcıadı'na ek olarak kullanıcının tam adını da göster." + invites_per_page: "Varsayılan olarak kullanıcı sayfasında gösterilen davetler." + short_progress_text_threshold: "Bir konudaki gönderi sayısı bu sayının üzerine çıktığında, ilerleme çubuğunda sadece şu anki gönderi sayısını göster. İlerleme çubuğunun kalınlığını değiştirirseniz, bu değeri de değiştirmeniz gerekebilir." + default_code_lang: "GitHub kod bloklarına (lang-auto, ruby, python vs.) uygulanacak, varsayılan programlama dili sözdizimi vurgulaması." + warn_reviving_old_topic_age: "Herhangi bir kullanıcı, son cevabın burada belirtilen gün sayısından daha önce yazıldığı bir konuya cevap yazmaya başladığında, bir uyarı mesajı çıkacak. Bu özelliği devre dışı bırakmak için 0 girin. " + autohighlight_all_code: "Tüm önceden formatlanan kod bloklarına, açıkça dil seçimi yapılmamış olsa da, zorla kod vurgulaması uygula." + embeddable_host: "Bu Discourse forumundan yorumların yerleştirilebileceği sunucu" + feed_polling_enabled: "SADECE YERLEŞTİRME İÇİN: RSS/ATOM beslemesinin gönderi olarak yerleştirilip yerleştirilemeyeceği." + feed_polling_url: "SADECE YERLEŞTİRME İÇİN: Yerleştirilecek RSS/ATOM beslemesinin URL'i." + embed_by_username: "Yerleştirilmiş konuları oluşturan kullanıcıya ait Discourse kullanıcı adı. " + embed_username_key_from_feed: "Beslemeden Discourse kullanıcı adını çekmek için kullanılacak anahtar" + embed_truncate: "Yerleştirilmiş gönderileri kırp." + embed_category: "Yerleştirilmiş konular kategorisi." + embed_post_limit: "Yerleştirilecek en fazla gönderi sayısı." + embed_whitelist_selector: "Yerleştirmelerde kullanılmasına izin verilen öğeler için CSS seçicisi." + embed_blacklist_selector: "Yerleştirmelerden çıkartılmış öğeler için CSS seçicisi." + notify_about_flags_after: "Bu kadar saat geçmesine rağmen hala ilgilenilmemiş bayraklar varsa, contact_email adresine email gönder. Devre dışı bırakmak için 0 girin. " + enable_cdn_js_debugging: "/logs 'ların asli hataları tüm js içeriklerine crossorigin izinleri ekleyerek göstermesine izin ver." + show_create_topics_notice: "Eğer sitede herkese açık konu sayısı 5'den az ise, adminden yeni konular oluşturmasını isteyen bir uyarı mesajı göster. " + vacuum_db_days: "Geçiş sonra DB alanı geri kazanmak için TAM VAKUM ANALİZİ'ni çalıştırın (devre dışı bırakmak için 0 girin)" + prevent_anons_from_downloading_files: "Anonim kullanıcıların eklenti indirebilmesini önle. DİKKAT: Bu ayar, eklenti olarak gönderilen resim-dışı site içeriklerinin de çalışmasını engelleyebilir." + enable_emoji: "Emojiyi aktifleştir" + emoji_set: "Emojinizi nasıl isterdiniz?" + errors: + invalid_email: "Geçersiz e-posta adresi." + invalid_username: "Bu kullanıcı adı ile bir kullanıcı bulunmuyor." + invalid_integer_min_max: "Değer %{min} ile %{max} arasında olmalı." + invalid_integer_min: "Değer %{min} ya da daha fazla olmalı." + invalid_integer_max: "Değer en fazla %{max} olabilir." + invalid_integer: "Değer tam sayı olmalı." + regex_mismatch: "Değer istenen formatla eşleşmiyor." + invalid_string: "Geçersiz değer." + invalid_string_min_max: "%{min} ile %{max} karakter arasında olmalı." + invalid_string_min: "En az %{min} karakter olmalı." + invalid_string_max: "En fazla %{max} karakter olabilir." + invalid_reply_by_email_address: "Değer '%{reply_key}' içermeli ve bildiri emailindan farklı olmalı." + notification_types: + mentioned: "%{display_username} sizden bahsetti: %{link}" + liked: "%{display_username} gönderinizi beğendi: %{link}" + replied: "%{display_username} gönderinize cevap verdi: %{link}" + quoted: "%{display_username} gönderinizi alıntıladı: %{link}" + edited: "%{display_username} gönderinizi düzenledi: %{link}" + posted: "%{display_username} gönderi oluşturdu %{link}" + moved_post: "%{display_username} gönderinizi buraya taşıdı: %{link}" + private_message: "%{display_username} size özel mesaj gönderi: %{link}" + invited_to_private_message: "%{display_username} sizi özel mesaja davet etti: %{link}" + invitee_accepted: "%{display_username} davetinizi kabul etti" + linked: "%{display_username} size %{link} sayfasında bağlantı verdi" + granted_badge: "%{link} kazandınız" + search: + within_post: "%{username} tarafından #%{post_number}" + types: + category: 'Kategoriler' + topic: 'Sonuçlar' + user: 'Kullanıcılar' + sso: + not_found: "Hesap aranamıyor ya da oluşturulamıyor, site admini ile iletişime geçin" + account_not_approved: "Hesap henüz onaylanmamış, onaylandığında e-posta ile haberdar edileceksiniz" + unknown_error: "Bilgi güncellenirken hata oluştu, site admini ile iletişime geçin" + timeout_expired: "Seansınız zaman aşımına uğradı, lütfen tekrar giriş yapmayı deneyin" + original_poster: "Orjinal Poster" + most_posts: "En Çok Gönderi" + most_recent_poster: "En Son Gönderen" + frequent_poster: "En Sık Gönderen" + redirected_to_top_reasons: + new_user: "Hoşgeldiniz! Bunlar en popüler yeni gönderiler." + not_seen_in_a_month: "Hoşgeldiniz! Bir süredir yoktunuz. Bunlar sizin yokluğunuzda en popüler olan konular." + move_posts: + new_topic_moderator_post: + other: "%{count} gönderiyi yeni bir konuya taşıdım: %{topic_link}" + existing_topic_moderator_post: + other: "%{count} gönderiyi var olan konulardan birine taşıdım: %{topic_link}" + change_owner: + post_revision_text: "Sahiplik %{old_user} hesabından %{new_user} hesabına aktarıldı" + emoji: + errors: + name_already_exists: "Üzgünüz, '%{name}' ismi zaten bir başka emoji tarafından kullanılıyor." + error_while_storing_emoji: "Üzgünüz, emoji kaydedilirken bir hata oluştu." + topic_statuses: + archived_enabled: "Konu şimdi arşivlendi. Donduruldu ve herhangi bir şekilde değişiklik yapılamaz." + archived_disabled: "Konu şimdi arşivden çıkarıldı. Artık donmuş değil, değiştirilebilir." + closed_enabled: "Konu şimdi kapatıldı. Artık yeni cevap yazılmasına izin yok. " + closed_disabled: "Konu şimdi açıldı. Yeni cevaplara izin var." + autoclosed_enabled_days: + other: "Bu konu %{count} gün sonunda konu otomatik olarak kapatıldı. Yeni cevap girilmesine izin verilmiyor." + autoclosed_enabled_hours: + other: "%{count} saat sonunda konu otomatik olarak kapatıldı. Yeni cevap girilmesine izin verilmiyor." + autoclosed_enabled_minutes: + other: "%{count} dakika sonunda konu otomatik olarak kapatıldı. Yeni cevap girilmesine izin verilmiyor." + autoclosed_enabled_lastpost_days: + other: "Bu konu son cevaptan %{count} gün sonra otomatik olarak kapatıldı. Yeni cevap girilmesine izin verilmiyor." + autoclosed_enabled_lastpost_hours: + other: "Bu konu son cevaptan %{count} saat sonra otomatik olarak kapatıldı. Yeni cevap girilmesine izin verilmiyor." + autoclosed_enabled_lastpost_minutes: + other: "Bu konu son cevaptan %{count} dakika sonra otomatik olarak kapatıldı. Yeni cevap girilmesine izin verilmiyor." + autoclosed_disabled: "Konu şimdi açıldı. Yeni cevaplara izin var." + autoclosed_disabled_lastpost: "Konu şimdi açıldı. Yeni cevaplara izin var." + pinned_enabled: "Bu konu başa tutturuldu. Görevli tarafından herkes için ya da kullanıcı tarafından kendisi için baştan kaldırılmadığı sürede bulunduğu kategorinin en üstünde yer alacak." + pinned_disabled: "Bu konu baştan kaldırıldı. Artık bulunduğu kategorinin en üstünde yer almayacak." + pinned_globally_enabled: "Bu konu global olarak başa tutturuldu. Görevliler herkes için ya da bireysel kullanıcılar kendileri için başa tutturmayı kaldırana dek, kendi kategorisinin ve tüm konu listelerinin en üstünde belirecek." + pinned_globally_disabled: "Bu konu baştan kaldırıldı. Artık bulunduğu kategorinin en üstünde yer almayacak." + visible_enabled: "Bu gönderi şimdi listelendi. Artık konu listesinde yer alacak." + visible_disabled: "Bu konu listeden çıkartıldı. Artık, konu listesinde yer almayacak. Bu konuya sadece kendi bağlantısından ulaşılabilir. " + login: + not_approved: "Hesabını henüz onaylanmadı. Giriş yapmak için hazır olduğunuzda e-posta ile bilgilendirileceksiniz." + incorrect_username_email_or_password: "Yanlış kullanıcı adı, e-posta ya da parola" + wait_approval: "Üye olduğunuz için teşekkürler. Hesabınız onaylandığında sizi haberdar edeceğiz." + active: "Hesabınız etkinleştirildi ve kullanıma hazır." + activate_email: "

Az kaldı! %{email} adresine bir etkinleştirme e-postası gönderdik. Hesabınızı etkinleştirmek için lütfen e-postadaki yönlendirmeleri takip edin.

Eğer e-postayı almadıysanız, gereksiz klasörünüzü kontrol edin ya da başka bir etkinleştirme e-postası daha göndermek için tekrar giriş yapmayı deneyin.

" + not_activated: "Henüz giriş yapamazsınız. Size bir etkinleştirme e-postası gönderdik. Hesabınızı etkinleştirmek için lütfen e-postadaki yönlendirmeleri takip edin." + not_allowed_from_ip_address: "Bu IP adresinden %{username} kullanıcı adı ile giriş yapamazsınız." + suspended: "%{date} tarihine kadar giriş yapamazsınız." + suspended_with_reason: "%{date} tarihine kadar giriş yapamazsınız. Uzaklaştırılma nedeniniz: %{reason}" + errors: "%{errors}" + not_available: "Uygun değil. Bunu denemeye ne dersiniz? %{suggestion}" + something_already_taken: "Bir şeyler ters gitti. Kullanıcı adı veya e-posta zaten kayıtlı olabilir. Parolamı Unuttum bağlantısını deneyin." + omniauth_error: "Üzgünüz, hesabınız doğrulanırken bir hata oluştu. Doğrulama işlemini onaylamamış olabilir misiniz?" + omniauth_error_unknown: "Girişiniz yapılırken bir şeyler ters gitti, lütfen tekrar deneyin." + new_registrations_disabled: "Şu an yeni hesap oluşturulamıyor. " + password_too_long: "Parolalar maksimum 200 karakter olabilir." + missing_user_field: "Kullanıcı alanlarının tamamını doldurmadınız" + close_window: "Doğrulama tamamlandı. Devam etmek için bu pencereyi kapatın." + user: + no_accounts_associated: "İlgili hesap yok" + username: + short: "en az %{min} karakter olmalı" + long: "%{max} karakterden fazla olmamalı" + characters: "sadece rakam, harf ve altçizgi bulundurabilir " + unique: "özgün olmalı" + blank: "bulunmalı" + must_begin_with_alphanumeric: "harf ya da sayı ile başlamalı" + email: + not_allowed: "için o e-posta sağlayıcısına izin verilmiyor. Lütfen başka bir email adresi kullanın. " + blocked: "için izin yok." + ip_address: + blocked: "engellenmiş." + invite_mailer: + subject_template: "%{invitee_name} sizi %{site_domain_name} sitesindeki '%{topic_title}' adlı konuya davet etti. " + text_body_template: "%{invitee_name} sizi \n\n> %{site_title} -- %{site_description} \n\nsitesindeki\n\n> **%{topic_title}**\n>\n> %{topic_excerpt}\n\ntartışmasına davet ediyor.\n\nEğer ilgileniyorsanız, aşağıdaki bağlantıya tıklayın:\n\n%{invite_link}\n\nBu davet güvenilir bir kullanıcı tarafından gönderilmiştir, cevap yazarak tartışmaya hemen katılabilirsiniz.\n" + invite_forum_mailer: + subject_template: "%{invitee_name} sizi %{site_domain_name} sitesine katılmanız için davet etti" + text_body_template: | + %{invitee_name} sizi + + > **%{site_title}** + > + > %{site_description} + + sitesine katılmaya davet etti. Eğer ilgileniyorsanız, aşağıdaki bağlantıya tıklayın: + + %{invite_link} + + Bu davet güvenilir bir kullanıcı tarafından gönderilmiştir. O nedenle giriş yapmanız gerekmeyecek. + invite_password_instructions: + subject_template: "%{site_name} hesabınız için parola oluşturun" + text_body_template: | + %{site_name} sitesine olan davetinizi kabul ettiğiniz için teşekkür ederiz -- hoşgeldiniz! + + Tekrar giriş yapmak için, aşağıdaki bağlantıya tıklayarak parola seçin: + %{base_url}/users/password-reset/%{email_token} + test_mailer: + subject_template: "[%{site_name}] E-posta Ulaştırma Testi" + text_body_template: "Bu aşağıdaki adresten gönderilen bir test emailidir\n\n[**%{base_url}**][0]\n\nEmailların ulaştırılması karışık bir meseledir. Öncelikle dikkat etmeniz gereken bir kaç önemli nokta:\n\n- Site ayarlarınızda 'bildiri emailları' için gönderen adresini doğru ayarladığınıza emin olun. **Yolladığınız emaillarda \"gönderen\" adresi olarak belirlediğiniz alan adı, emaillarınızın doğrulanacağı alan adıdır.**\n\n- Email başlıklarındaki önemli ipuçlarını yakalayabilmek için email istemcinizde emailların kaynak kodunu nasıl görüntüleyebileceğinizi öğrenin. Gmail'da, her emailin sağ üstündeki açılır menüden \"show original\" opsiyonuna tıklayabilirsiniz.\n\n- **ÖNEMLİ:** ISP'nizde email yollamak için kullanıdığınız alan adlarıyla IP adreslerinin eşleşmesini sağlayacak bir reverse DNS kaydı var mı? Buradan [reverse PTR kayıtlarınızı test edin][2]. Eğer ISP'niz doğru reverse DNS pointer kaydı girmezse, büyük ihtimal emaillarınızın hiç biri yerine ulaşmayacaktır.\n\n- Alan adınızın [SPF kaydı][8] doğru mu? Buradan [SPF kaydınızı test edin][1]. SPF için doğru resmi kayıt tipinin TXT olduğunu unutmayın.\n\n- Alan adınızın [DKIM kaydı][3] doğru mu? Bu emailların ulaştırılabilirliğini ciddi şekilde artıracaktır. Buradan [DKIM kaydınızı test edin][7].\n\n- Kendi email sunucunuzu kullanıyorsanız, email sunucunuzun IPlerinin [hiç bir email karalistesine][4] alınmadığına emin olun. Sunucunuzun, kesinlikle, HELLO mesajında DNS olarak çözümlenen tam tanımlanmış bilgisayar adı da gönderdiğinden emin olun. Göndermemesi, emailinizin bir çok email servisi tarafından reddedilmesine sebep olacaktır.\n\n(En kolayı, küçük topluluklar için rahat rahat yetecek sayıda bedava email yollama paketleri içeren, [Mandrill][md] veya [Mailgun][mg] veya [Mailjet][mj]'te ücretsiz hesap açmak. Tabi, gene DNS ayarlarınızda SPF ve DKIM kayıtlarını oluşturmanız gerekecek.)\n\nUmarız bu email ulaştırma testini başarıyla atlatmışsınızdır. \n\nİyi şanslar,\n\n[Discourse](http://www.discourse.org)'tan arkadaşlarınız\n\n[0]: %{base_url}\n[1]: http://www.kitterman.com/spf/validate.html\n[2]:\ + \ http://mxtoolbox.com/ReverseLookup.aspx\n[3]: http://www.dkim.org/\n[4]: http://whatismyipaddress.com/blacklist-check\n[7]: http://dkimcore.org/tools/dkimrecordcheck.html\n[8]: http://www.openspf.org/SPF_Record_Syntax\n[md]: http://mandrill.com\n[mg]: http://www.mailgun.com/\n[mj]: http://www.mailjet.com/pricing\n" + new_version_mailer: + subject_template: "[%{site_name}] Yeni Discourse versiyonu, güncelleme var" + text_body_template: |2 + [Discourse'un](http://www.discourse.org) yeni versiyonu hazır. + + Sizin kullandığınız versiyon: %{installed_version} + Yeni versiyon: **%{new_version}** + + Aşağıdakileri uygulayabilirsiniz: + + - Yenilikleri [GitHub değişiklikler listesinde] görüntüleyebilirsiniz (https://github.com/discourse/discourse/commits/master). + + - [%{base_url}/admin/upgrade](%{base_url}/admin/upgrade) sayfasını ziyaret ederek ve "Güncelle" butonuna basarak yeni versiyona geçebilirsiniz. + + - Haberler, tartışmalar ve Discourse ile ilgili destek için burayı ziyaret edebilirsiniz: [meta.discourse.org](http://meta.discourse.org) + new_version_mailer_with_notes: + subject_template: "[%{site_name}] güncellemesi var" + text_body_template: |+ + [Discourse'un](http://www.discourse.org) yeni versiyonu hazır. + + Sizin kullandığınız versiyon: %{installed_version} + Yeni versiyon: **%{new_version}** + + Aşağıdakileri uygulayabilirsiniz: + + - Yenilikleri [GitHub değişiklikler listesinde] görüntüleyebilirsiniz (https://github.com/discourse/discourse/commits/master). + + - [%{base_url}/admin/upgrade](%{base_url}/admin/upgrade) sayfasını ziyaret ederek ve "Güncelle" butonuna basarak yeni versiyona geçebilirsiniz. + + - Haberler, tartışmalar ve Discourse ile ilgili destek için burayı ziyaret edebilirsiniz: [meta.discourse.org](http://meta.discourse.org) + + ### Yeni sürüm notları + + %{notes} + + flags_reminder: + flags_were_submitted: + other: "Bu bayraklar %{count} saat önce verildi." + please_review: "Lütfen onları inceleyin." + post_number: "gönderi" + how_to_disable: '"Bayraklarla ilgili bilgilendirme süresi" ayarından bu hatırlatma e-postasının gönderimini devre dışı bırakabilir ya da sıklığını değiştirebilirsiniz. ' + subject_template: + other: "İlgilenilmesi gereken %{count} bayrak var" + flag_reasons: + off_topic: "Gönderiniz **konu-dışı** olarak bayraklandı: topluluk, gönderinizin konu başlığı ve ilk gönderi ile tanımlanan konu içeriğine uygun olmadığını düşünüyor. " + inappropriate: "Gönderiniz **uygunsuz** olarak bayraklandı: topluluk gönderinizin saldırgan, kaba ya da [topluluk yönergelerine](/guidelines) aykırı olduğunu düşünüyor." + spam: "Gönderiniz **spam** olarak bayraklandı: topluluk, gönderinizin mevcut konuya uygun veya yararlı olmaktansa fazlasıyla promosyonel ve reklam içerikli olduğunu düşünüyor." + notify_moderators: "Gönderiniz **moderatör kontrolü için** bayraklandı: topluluk, gönderinizle ilgili bir şeyin moderatör tarafından kontrol edilmesi gerektiğini düşünüyor." + flags_dispositions: + agreed: "Bizi bilgilendirdiğiniz için teşekkür ederiz. Bir sorun olduğu konusunda size katılıyoruz ve ilgileniyoruz." + agreed_and_deleted: "Bizi bilgilendirdiğiniz için teşekkür ederiz. Bir sorun olduğu konusunda size katılıyoruz, gönderiyi kaldırdık. " + disagreed: "Bizi bilgilendirdiğiniz için teşekkür ederiz. Konu ile ilgileniyoruz." + deferred: "Bizi bilgilendirdiğiniz için teşekkür ederiz. Konu ile ilgileniyoruz." + deferred_and_deleted: "Bizi bilgilendirdiğiniz için teşekkür ederiz. Gönderiyi kaldırdık." + temporarily_closed_due_to_flags: "Bu konu, topluluk tarafından bayraklanma sayısı çok olduğu için geçici olarak kapatıldı" + system_messages: + post_hidden: + subject_template: "Topluluk tarafından bayraklandığı için gönderiniz gizlendi" + text_body_template: "Merhaba,\n\nBu, gönderinizin gizlendiğini haber vermek için %{site_name} sitesinden gönderilmiş otomatik bir mesajdır.\n\n%{base_url}%{url}\n\n%{flag_reason}\n\nGönderiniz gizlenmeden önce birden fazla kullanıcı tarafından bayraklandı. Lütfen, bu geri bildirimler doğrultusunda gönderinizi nasıl düzelteceğinizi düşünün. **Gönderinizi %{edit_delay} dakika sonra düzenleyebilirsiniz, otomatik olarak tekrar yayına alınacaktır.** Bu sistemdeki güven seviyenizi yükseltecektir. \n\nAncak, eğer gönderiniz kullanıcılar tarafından ikinci kere gizlenirse, görevliler tarafından incelenene kadar gizli kalacaktır - ve hesabınızın askıya alınması dahil olmak üzere farklı önlemler alınabilir.\n\nDaha fazla bilgi için, lütfen [topluluk yönergelerine bakın](%{base_url}/guidelines).\n\n" + welcome_user: + subject_template: "%{site_name} sitesine hoşgeldiniz!" + welcome_invite: + subject_template: "%{site_name} sitesine hoşgeldiniz!" + backup_succeeded: + subject_template: "Yedekleme başarıyla tamamlandı" + text_body_template: "Yedekleme başarıyla tamamlandı.\n\n[admin > backup section](/admin/backups) bölümünden yeni yedeği indirebilirsiniz." + backup_failed: + subject_template: "Yedekleme başarısız" + text_body_template: "Yedekleme başarısız oldu. \n\nKayıtlar burada:\n\n```\n\n%{logs}\n\n```\n\n" + restore_succeeded: + subject_template: "Geri alma başarıyla tamamlandı" + text_body_template: "Geri alma başarıyla tamamlandı." + restore_failed: + subject_template: "Geri alma başarısız" + text_body_template: | + Geri alma başarısız oldu. + + Kayıtlar burada: + + ``` + + %{logs} + + ``` + bulk_invite_succeeded: + subject_template: "Toplu davet gönderimi başarıyla tamamlandı" + text_body_template: "Toplu davet dosyanız işlendi, %{sent} davetiye gönderildi. " + bulk_invite_failed: + subject_template: "Toplu davet gönderiminde hata oluştu" + text_body_template: | + Toplu davet dosyanız işlendi, %{sent} davetiye %{failed} hata(lar) ile gönderildi. + + + Kayıtları aşağıda bulabilirsiniz: + + + ``` + + %{logs} + + ``` + csv_export_succeeded: + subject_template: "Dışarı veri aktarımı tamamlandı" + text_body_template: | + Dışarı veri aktarımı başarılı! :dvd: + + %{file_name} (%{file_size}) + + Bu bağlantı 48 saat boyunca geçerli olacak. + csv_export_failed: + subject_template: "Dışarı veri aktarımı başarısız oldu" + text_body_template: "Üzgünüz, dışarı veri aktarımı başarısız oldu. Lütfen kayıtları inceleyin veya bir görevli ile iletişime geçin." + email_reject_trust_level: + subject_template: "E-posta sorunu -- Yetersiz Güven Seviyesi" + text_body_template: "Üzgünüz, ama %{destination} (titled %{former_title}) adresine göndermeye çalıştığınız e-posta başarısız oldu.\n\nBu e-posta adresine yeni gönderiler oluşturmanız için, hesabınız yeterli güven seviyesine sahip değil. Bir hata olduğunu düşünüyorsanız, bir görevli ile iletişime geçin.\n \n" + email_reject_no_account: + subject_template: "E-posta sorunu -- Bilinmeyen Hesap " + text_body_template: | + Üzgünüz, ama %{destination} (titled %{former_title}) adresine göndermeye çalıştığınız e-posta başarısız oldu. + + Bu e-posta adresine sahip bir kullanıcı bulunamadı. Başka bir e-posta adresinden göndermeyi deneyin ya da bir görevli ile iletişime geçin. + email_reject_empty: + subject_template: "E-posta sorunu -- İçerik Yok" + text_body_template: | + Üzgünüz, ama %{destination} (titled %{former_title}) adresine göndermeye çalıştığınız e-posta başarısız oldu. + + E-postada herhangi bir içerik bulamadık. Cevabınızın e-postanın en üstünde yer aldığından emin olun -- aralardaki cevapları işleme alamıyoruz. + + E-postanızda içerik olduğu halde bu mesajı aldıysanız, bir sefer de HTML içeriği ile göndermeyi deneyin (sadece düz metin olarak değil). + email_reject_parsing: + subject_template: "E-posta sorunu -- Bilinmeyen içerik" + text_body_template: | + Üzgünüz, ama %{destination} (titled %{former_title}) adresine göndermeye çalıştığınız e-posta başırısız oldu. + + E-postada herhangi bir cevap bulamadık. **Cevabınızın e-posta'nın en üstünde yer aldığından emin olun** -- aralardaki cevapları işleyemiyoruz. + email_reject_post_error: + subject_template: "E-posta sorunu -- Gönderim hatası" + text_body_template: | + Üzgünüz, ama %{destination} (titled %{former_title}) adresine göndermeye çalıştığınız e-posta başarısız oldu. + + Bir kaç muhtemel neden: karmaşık format, çok uzun mesaj, çok kısa mesaj. Lütfen tekrar deneyin, sorun devam ederse websitesi üzerinden gönderin. + email_reject_post_error_specified: + subject_template: "E-posta sorunu -- Gönderim hatası" + text_body_template: | + Üzgünüz, ama %{destination} (titled %{former_title}) adresine göndermeye çalıştığınız e-posta başarısız oldu. + + Sebep: + + %{post_error} + + Sorunu düzeltebilirseniz, lütfen tekrar deneyin. + email_reject_reply_key: + subject_template: "E-posta sorunu -- Bilinmeyen Cevap Anahtarı" + text_body_template: | + Üzgünüz, ama %{destination} (titled %{former_title}) adresine göndermeye çalıştığınız e-posta başarısız oldu. + + Cevap anahtarı geçersiz ya da bulunamıyor, bu nedenle de bu mesajın neye cevaben gönderildiğini bilemiyoruz. Bir görevli ile iletişime geçin. + email_reject_destination: + subject_template: "E-posta sorunu -- Bilinmeyen Alıcı Adresi" + text_body_template: | + Üzgünüz, ama %{destination} (titled %{former_title}) adresine göndermeye çalıştığınız e-posta başarısız oldu. + + Gönderilen adreslerinin hiçbiri bilinmiyor. Site adresinin To: kısmında olduğundan (Cc: ya da Bcc: değil) ve görevliler tarafından sağlanan doğru email adresine yolladığınızdan emin olun. + email_reject_topic_not_found: + subject_template: "E-posta sorunu -- Konu Bulunamadı" + text_body_template: | + Üzgünüz, ama %{destination} (titled %{former_title}) adresine göndermek istediğiniz e-posta başarısız oldu. + + Konu bulunamadı, silinmiş olabilir. Yanlışlık olduğunu düşünüyorsanız, bir görevli ile iletişime geçin. + email_reject_topic_closed: + subject_template: "E-posta sorunu -- Konu Kapatıldı" + text_body_template: | + Üzgünüz, ama %{destination} (titled %{former_title}) adresine göndermek istediğiniz e-posta başarısız oldu. + + Konu kapatılmış. Yanlışlık olduğunu düşünüyorsanız, bir görevli ile iletişime geçin. + email_error_notification: + subject_template: "E-posta sorunu -- POP doğrulama hatası" + text_body_template: | + Mailler POP sunucusundan çağrılırken bir doğrulama hatası oluştu. + + Lütfen, [site ayarlarında](%{base_url}/admin/site_settings/category/email) POP bilgilerinin doğru şekilde düzenlendiğinden emin olun. + too_many_spam_flags: + subject_template: "Yeni hesap engellendi" + text_body_template: | + Merhaba. + + Bu, %{site_name} sitesinin, gönderilerinizin topluluk tarafından bayraklanması sonucu otomatik olarak gizlendiğini bildirmek için gönderdiği otomatik bir mesajdır. + + Önlem olarak, bir görevli tarafından incelenene kadar yeni hesabınız üzerinden yeni cevap ya da konu oluşturmanız engellendi. + + Daha fazla bilgi için lütfen [topluluk yönergelerine](%{base_url}/guidelines) bakın. + blocked_by_staff: + subject_template: "Hesap engellendi" + text_body_template: | + Merhaba. + + Bu, %{site_name} sitesinin, hesabınızın bir görevli tarafından engellendiğini bildirmek için gönderdiği otomatik bir mesajdır. + + Daha fazla bilgi için lütfen [topluluk yönergelerine](%{base_url}/guidelines) bakın. + user_automatically_blocked: + subject_template: "Topluluk bayrakları nedeniyle yeni kullanıcı %{username} engellendi" + text_body_template: | + Bu bir otomatik mesajdır. + + Yeni kullanıcı [%{username}](%{base_url}%{user_url}), gönderisi(leri) farklı kullanıcılar tarafından bayrakladığı için otomatik olarak engellendi. + + Lütfen [bayrakları inceleyin](%{base_url}/admin/flags). %{username} adlı kullancının haksız yere gönderi oluşturabilmesi engellendiyse, [bu kullanıcıya ait admin sayfasında](%{base_url}%{user_url}) engeli kaldır butonuna tıklayın. + + Bu tür engellemeleri tetikleyen eşik, site ayarları sayfasında `block_new_user` bölümünden değiştirilebilir. + spam_post_blocked: + subject_template: "Üst üste aynı bağlantıların paylaşılmasından ötürü %{username} adlı yeni kullanıcının gönderileri engelledi" + text_body_template: "Bu bir otomatik mesajdır.\n\n[%{username}](%{base_url}%{user_url}) adlı yeni kullanıcı %{domains} sitelerine bağlantı içeren birçok farklı gönderi oluşturmaya çalıştı, ancak bu gönderiler spam yaratmamaları için engellendi. Kullanıcı hala %{domains} sitelerine bağlantılı içermeyen yeni gönderiler oluşturabiliyor. \n\nLütfen [kullanıcıyı inceleyin](%{base_url}%{user_url}).\n\nBu tür engellemeleri tetikleyen `newuser_spam_host_threshold` ve `white_listed_spam_host_domains` değerleri site ayarları sayfasından değiştirilebilir.\n" + unblocked: + subject_template: "Hesabın engeli kaldırıldı" + text_body_template: | + Merhaba, + + Bu, %{site_name} sitesinin, görevlilerin değerlendirmeleri sonucunda hesabınızındaki engelin kaldırıldığını bildirmek için gönderdiği otomatik bir mesajdır. + + Artık, eskisi gibi yeni cevaplar ve konular oluşturabilirsiniz. + pending_users_reminder: + subject_template: + other: "%{count} kullanıcı onay bekliyor" + text_body_template: "Bu foruma giriş yapabilmek için onayınızı (ya da reddinizi) bekleyen yeni kullanıcı kayıtları var. \n\n[Lütfen admin bölümünde değerlendirmenizi yapın](%{base_url}/admin/users/list/pending).\n" + download_remote_images_disabled: + subject_template: "Uzaktaki resimlerin indirilmesi devre dışı bırakıldı" + text_body_template: "`download_remote_images_to_local` ayarı harddisk alanı limiti `download_remote_images_threshold` aşıldığı için devre dışı bırakıldı." + unsubscribe_link: "Bu e-postalara aboneliğinizi iptal etmek için [kullanıcı ayarları](%{user_preferences_url}) sayfanızı ziyaret edin." + subject_re: "Cvp:" + subject_pm: "[ÖM]" + user_notifications: + previous_discussion: "Önceki Cevaplar" + unsubscribe: + title: "Aboneliği İptal Et" + description: "Bu e-postalarla ilgilenmiyor musunuz? Sorun değil! Aşağıya tıklayarak aboneliğinizi hemen iptal edebilirsiniz:" + reply_by_email: "Karşılık vermek için, bu e-postayı cevaplayın ya da %{base_url}%{url} sayfasını internet tarayıcınızda ziyaret edin." + visit_link_to_respond: "Karşılık vermek için, %{base_url}%{url} sayfasını internet tarayıcınızda ziyaret edin." + posted_by: "%{post_date} tarihinde %{username} tarafından gönderildi" + user_invited_to_private_message_pm: + subject_template: "[%{site_name}] %{username} sizi '%{topic_title}' başlıklı özel mesaja davet etti" + text_body_template: | + %{username} sizi %{site_name} sitesinde, '%{topic_title}' başlıklı bir özel mesaja davet etti: + + Konuyu görüntülemek için lütfen bu bağlantıyı ziyaret edin: %{base_url}%{url} + user_replied: + subject_template: "[%{site_name}] %{topic_title}" + text_body_template: | + %{message} + + %{context} + + --- + %{respond_instructions} + user_quoted: + subject_template: "[%{site_name}] %{topic_title}" + text_body_template: | + %{message} + + + %{context} + + + --- + + %{respond_instructions} + user_mentioned: + subject_template: "[%{site_name}] %{topic_title}" + text_body_template: | + %{message} + + + %{context} + + --- + + %{respond_instructions} + user_posted: + subject_template: "[%{site_name}] %{topic_title}" + text_body_template: | + %{message} + + + %{context} + + + --- + + %{respond_instructions} + user_posted_pm: + subject_template: "[%{site_name}] [ÖM] %{topic_title}" + text_body_template: | + %{message} + + + %{context} + + + --- + + %{respond_instructions} + digest: + why: "%{last_seen_at} tarihindeki son girişinizden beri %{site_link} sitesine olanların kısa bir özeti" + subject_template: "[%{site_name}] Özeti %{date}" + new_activity: "Konu ve gönderilerinizdeki yeni aktiviteler:" + top_topics: "Popüler gönderiler" + other_new_topics: "Popüler konular" + unsubscribe: "Bu özet %{site_link} sitesini bir süre ziyaret etmediğinizde gönderilir. Almak istemiyorsanız, ayrılmak için %{unsubscribe_link}." + click_here: "buraya tıklayın" + from: "%{site_name} özeti" + read_more: "Devamını Oku" + more_topics: "%{new_topics_since_seen} tane daha yeni konu vardı." + more_topics_category: "Daha fazla yeni konu:" + posts: + other: "%{count} gönderi" + forgot_password: + subject_template: "[%{site_name}] Parola sıfırlama" + text_body_template: | + [%{site_name}](%{base_url}) sitesinde, biri parolanızın sıfırlanması için talepte bulundu. + + Eğer talepte bulunan siz değilseniz, bu e-postayı görmezden gelebilirsiniz. + + Yeni parola oluşturmak için aşağıdaki bağlantıya tıklayın: + %{base_url}/users/password-reset/%{email_token} + set_password: + subject_template: "[%{site_name}] Parola Oluşturun" + text_body_template: | + Biri, [%{site_name}](%{base_url}) sitesindeki hesabınıza parola ekleme talebinde bulundu. Dilerseniz, bu doğrulanmış email hesabınızla eşleşen, desteklediğiniz herhangi bir online servisi (Google, Facebook, vs.) kullanarak da giriş yapabilirsiniz. + + Eğer istekte bulunan siz değilseniz, bu e-postayı görmezden gelebilirsiniz. + + Parola oluşturmak için aşağıdaki bağlantıya tıklayın: + %{base_url}/users/password-reset/%{email_token} + account_created: + subject_template: "[%{site_name}] Yeni Hesabınız" + text_body_template: | + %{site_name} sitesinde sizin için yeni bir hesap oluşturuldu. + + Yeni hesabınıza ait bir parola oluşturmak için aşağıdaki bağlantıya tıklayın: + %{base_url}/users/password-reset/%{email_token} + authorize_email: + subject_template: "[%{site_name}] Yeni e-posta adresinizi onaylayın" + text_body_template: | + Aşağıdaki bağlantıya tıklayarak %{site_name} sitesindeki yeni e-posta adresinizi onaylayın: + + %{base_url}/users/authorize-email/%{email_token} + signup_after_approval: + subject_template: "%{site_name} sitesinde hesabınız onaylandı!" + signup: + subject_template: "[%{site_name}] Yeni hesabınızı etkinleştirin" + text_body_template: | + %{site_name} sitesine hoşgeldiniz! + + Hesabınızı onaylamak ve etkinleştirmek için aşağıdaki bağlantıya tıklayın: + %{base_url}/users/activate-account/%{email_token} + + Eğer yukarıdaki bağlantı tıklanabilir değilse, bağlantıyı kopyalayıp tarayıcınızın adres çubuğuna yapıştırmayı deneyin. + page_not_found: + title: "Aradığınız sayfa bulunmuyor ya da gizli." + popular_topics: "Popüler" + recent_topics: "Yeni" + see_more: "Daha fazla" + search_title: "Bu sitede ara" + search_google: "Google" + login_required: + welcome_message: | + #[Hoşgeldiniz %{title}](#welcome) + Hesap oluşturmanız gerekiyor. Devam etmek için lütfen bir hesap oluşturun ya da giriş yapın. + terms_of_service: + title: "Üyelik Sözleşmesi" + signup_form_message: 'Üyelik Sözleşmesini okudum ve kabul ediyorum.' + deleted: 'silindi' + upload: + edit_reason: "resimlerin yerel kopyaları indirildi" + unauthorized: "Üzgünüz, yüklemeye çalıştığınız dosya izinli değil (authorized extensions: %{authorized_extensions})." + pasted_image_filename: "Yapıştırılan resim" + store_failure: "#%{user_id} kullanıcısı için yükleme #%{upload_id} kaydedilemedi." + attachments: + too_large: "Üzgünüz, yüklemeye çalıştığınız dosya çok büyük (en fazla %{max_size_kb}KB olabilir)." + images: + too_large: "Üzgünüz, yüklemeye çalıştığınız resim çok büyük (en fazla %{max_size_kb}KB olabilir), lütfen küçültüp tekrar deneyin." + fetch_failure: "Üzgünüz, resim çekilirken hata oluştu." + unknown_image_type: "Üzgünüz ama yüklemeye çalıştığınız dosya resim dosyası değil. " + size_not_found: "Üzgünüz, resminizin büyüklüğünü tespit edemedik. Dosya bozuk olabilir mi?" + flag_reason: + sockpuppet: "Yeni bir kullanıcı bir konu oluşturdu ve aynı IP adresinden başka bir yeni kullanıcı cevap yazdı. Site ayarları sayfasında flag_sockpuppets değerine göz atın." + spam_hosts: "Bu yeni kullanıcı aynı alan adına bağlantı içeren birden fazla gönderi oluşturmaya çalıştı. Site ayarları sayfasındaki newuser_spam_host_threshold değerine göz atın." + email_log: + no_user: "%{user_id} id'sine sahip bir kullanıcı bulunamadı" + suspended_not_pm: "Kullanıcı uzaklaştırıldı, özel mesaj değil" + seen_recently: "Kullanıcı kısa süre önce görüldü" + post_not_found: "%{post_id} id'sine sahip bir gönderi bulunamadı" + notification_already_read: "Bu e-postanın içerdiği bildiri önceden okundu" + topic_nil: "post.topic boş" + post_deleted: "gönderi sahibi tarafından silindi" + user_suspended: "kullanıcı uzaklaştırıldı" + already_read: "kullanıcı bu gönderiyi önceden okumuş" + message_blank: "mesaj boş" + message_to_blank: "message.to boş" + text_part_body_blank: "text_part.body boş" + body_blank: "içerik boş" + color_schemes: + base_theme_name: "Baz" + guidelines: "Yönergeler" + privacy: "Gizlilik İlkeleri" + edit_this_page: "Bu sayfayı düzenle" + csv_export: + boolean_yes: "Evet" + boolean_no: "Hayır" + static_topic_first_reply: |2 + %{page_name} sayfasının içeriğini değiştirmek için bu konuya ait ilk gönderiyi düzenleyin. + guidelines_topic: + title: "SSS/Yönergeler" + body: | + + + ## [This is a Civilized Place for Public Discussion](#civilized) + + Please treat this discussion forum with the same respect you would a public park. We, too, are a shared community resource — a place to share skills, knowledge and interests through ongoing conversation. + + These are not hard and fast rules, merely aids to the human judgment of our community. Use these guidelines to keep this a clean, well-lighted place for civilized public discourse. + + + + ## [Improve the Discussion](#improve) + + Help us make this a great place for discussion by always working to improve the discussion in some way, however small. If you are not sure your post adds to the conversation, think over what you want to say and try again later. + + The topics discussed here matter to us, and we want you to act as if they matter to you, too. Be respectful of the topics and the people discussing them, even if you disagree with some of what is being said. + + One way to improve the discussion is by discovering ones that are already happening. Please spend some time browsing the topics here before replying or starting your own, and you’ll have a better chance of meeting others who share your interests. + + + + ## [Be Agreeable, Even When You Disagree](#agreeable) + + You may wish to respond to something by disagreeing with it. That’s fine. But, remember to _criticize ideas, not people_. Please avoid: + + * Name-calling. + * Ad hominem attacks. + * Responding to a post’s tone instead of its actual content. + * Knee-jerk contradiction. + + Instead, provide reasoned counter-arguments that improve the conversation. + + + + ## [Your Participation Counts](#participate) + + The conversations we have here set the tone for everyone. Help us influence the future of this community by choosing to engage in discussions that make this forum an interesting place to be — and avoiding those that do not. + + Discourse provides tools that enable the community to collectively identify the best (and worst) contributions: favorites, bookmarks, likes, flags, replies, edits, and so forth. Use these tools to improve your own experience, and everyone else’s, too. + + Let’s try to leave our park better than we found it. + + + + ## [If You See a Problem, Flag It](#flag-problems) + + Moderators have special authority; they are responsible for this forum. But so are you. With your help, moderators can be community facilitators, not just janitors or police. + + When you see bad behavior, don’t reply. It encourages the bad behavior by acknowledging it, consumes your energy, and wastes everyone’s time. _Just flag it_. If enough flags accrue, action will be taken, either automatically or by moderator intervention. + + In order to maintain our community, moderators reserve the right to remove any content and any user account for any reason at any time. Moderators do not preview new posts in any way; the moderators and site operators take no responsibility for any content posted by the community. + + + + ## [Always Be Civil](#be-civil) + + Nothing sabotages a healthy conversation like rudeness: + + * Be civil. Don’t post anything that a reasonable person would consider offensive, abusive, or hate speech. + * Keep it clean. Don’t post anything obscene or sexually explicit. + * Respect each other. Don’t harass or grief anyone, impersonate people, or expose their private information. + * Respect our forum. Don’t post spam or otherwise vandalize the forum. + + These are not concrete terms with precise definitions — avoid even the _appearance_ of any of these things. If you’re unsure, ask yourself how you would feel if your post was featured on the front page of the New York Times. + + This is a public forum, and search engines index these discussions. Keep the language, links, and images safe for family and friends. + + + + ## [Keep It Tidy](#keep-tidy) + + Make the effort to put things in the right place, so that we can spend more time discussing and less cleaning up. So: + + * Don’t start a topic in the wrong category. + * Don’t cross-post the same thing in multiple topics. + * Don’t post no-content replies. + * Don’t divert a topic by changing it midstream. + * Don’t sign your posts — every post has your profile information attached to it. + + Rather than posting “+1” or “Agreed”, use the Like button. Rather than taking an existing topic in a radically different direction, use Reply as a Linked Topic. + + + + ## [Post Only Your Own Stuff](#stealing) + + You may not post anything digital that belongs to someone else without permission. You may not post descriptions of, links to, or methods for stealing someone’s intellectual property (software, video, audio, images), or for breaking any other law. + + + + ## [Powered by You](#power) + + This site is operated by your [friendly local staff](/about) and *you*, the community. If you have any further questions about how things should work here, open a new topic in the [meta category](/c/meta) and let's discuss! If there's a critical or urgent issue that can't be handled by a meta topic or flag, contact us via the [staff page](/about). + + + + ## [Terms of Service](#tos) + + Yes, legalese is boring, but we must protect ourselves – and by extension, you and your data – against unfriendly folks. We have a [Terms of Service](/tos) describing your (and our) behavior and rights related to content, privacy, and laws. To use this service, you must agree to abide by our [TOS](/tos). + tos_topic: + title: "Üyelik Sözleşmesi" + body: | + The following terms and conditions govern all use of the %{company_domain} website and all content, services and products available at or through the website, including, but not limited to, %{company_domain} Forum Software, %{company_domain} Support Forums and the %{company_domain} Hosting service ("Hosting"), (taken together, the Website). The Website is owned and operated by %{company_full_name} ("%{company_name}"). The Website is offered subject to your acceptance without modification of all of the terms and conditions contained herein and all other operating rules, policies (including, without limitation, %{company_domain}’s [Privacy Policy](/privacy) and [Community Guidelines](/faq)) and procedures that may be published from time to time on this Site by %{company_name} (collectively, the "Agreement"). + + Please read this Agreement carefully before accessing or using the Website. By accessing or using any part of the web site, you agree to become bound by the terms and conditions of this agreement. If you do not agree to all the terms and conditions of this agreement, then you may not access the Website or use any services. If these terms and conditions are considered an offer by %{company_name}, acceptance is expressly limited to these terms. The Website is available only to individuals who are at least 13 years old. + + + + ## [1. Your %{company_domain} Account](#1) + + If you create an account on the Website, you are responsible for maintaining the security of your account and you are fully responsible for all activities that occur under the account. You must immediately notify %{company_name} of any unauthorized uses of your account or any other breaches of security. %{company_name} will not be liable for any acts or omissions by you, including any damages of any kind incurred as a result of such acts or omissions. + + + + ## [2. Responsibility of Contributors](#2) + + If you post material to the Website, post links on the Website, or otherwise make (or allow any third party to make) material available by means of the Website (any such material, "Content"), You are entirely responsible for the content of, and any harm resulting from, that Content. That is the case regardless of whether the Content in question constitutes text, graphics, an audio file, or computer software. By making Content available, you represent and warrant that: + + * the downloading, copying and use of the Content will not infringe the proprietary rights, including but not limited to the copyright, patent, trademark or trade secret rights, of any third party; + * if your employer has rights to intellectual property you create, you have either (i) received permission from your employer to post or make available the Content, including but not limited to any software, or (ii) secured from your employer a waiver as to all rights in or to the Content; + * you have fully complied with any third-party licenses relating to the Content, and have done all things necessary to successfully pass through to end users any required terms; + * the Content does not contain or install any viruses, worms, malware, Trojan horses or other harmful or destructive content; + * the Content is not spam, is not machine- or randomly-generated, and does not contain unethical or unwanted commercial content designed to drive traffic to third party sites or boost the search engine rankings of third party sites, or to further unlawful acts (such as phishing) or mislead recipients as to the source of the material (such as spoofing); + * the Content is not pornographic, does not contain threats or incite violence, and does not violate the privacy or publicity rights of any third party; + * your content is not getting advertised via unwanted electronic messages such as spam links on newsgroups, email lists, blogs and web sites, and similar unsolicited promotional methods; + * your content is not named in a manner that misleads your readers into thinking that you are another person or company; and + * you have, in the case of Content that includes computer code, accurately categorized and/or described the type, nature, uses and effects of the materials, whether requested to do so by %{company_name} or otherwise. + + + + ## [3. User Content License](#3) + + User contributions are licensed under a [Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License](http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_US). Without limiting any of those representations or warranties, %{company_name} has the right (though not the obligation) to, in %{company_name}’s sole discretion (i) refuse or remove any content that, in %{company_name}’s reasonable opinion, violates any %{company_name} policy or is in any way harmful or objectionable, or (ii) terminate or deny access to and use of the Website to any individual or entity for any reason, in %{company_name}’s sole discretion. %{company_name} will have no obligation to provide a refund of any amounts previously paid. + + + + + ## [4. Payment and Renewal](#4) + + ### General Terms + + Optional paid services or upgrades may be available on the Website. When utilizing an optional paid service or upgrade, you agree to pay %{company_name} the monthly or annual subscription fees indicated. Payments will be charged on a pre-pay basis on the day you begin utilizing the service or upgrade and will cover the use of that service or upgrade for a monthly or annual subscription period as indicated. These fees are not refundable. + + ### Automatic Renewal + + Unless you notify %{company_name} before the end of the applicable subscription period that you want to cancel a service or upgrade, your subscription will automatically renew and you authorize us to collect the then-applicable annual or monthly subscription fee (as well as any taxes) using any credit card or other payment mechanism we have on record for you. Subscriptions can be canceled at any time. + + + + ## [5. Services](#5) + + ### Hosting, Support Services + + Optional Hosting and Support services may be provided by %{company_name} under the terms and conditions for each such service. By signing up for a Hosting/Support or Support services account, you agree to abide by such terms and conditions. + + + + ## [6. Responsibility of Website Visitors](#6) + + %{company_name} has not reviewed, and cannot review, all of the material, including computer software, posted to the Website, and cannot therefore be responsible for that material’s content, use or effects. By operating the Website, %{company_name} does not represent or imply that it endorses the material there posted, or that it believes such material to be accurate, useful or non-harmful. You are responsible for taking precautions as necessary to protect yourself and your computer systems from viruses, worms, Trojan horses, and other harmful or destructive content. The Website may contain content that is offensive, indecent, or otherwise objectionable, as well as content containing technical inaccuracies, typographical mistakes, and other errors. The Website may also contain material that violates the privacy or publicity rights, or infringes the intellectual property and other proprietary rights, of third parties, or the downloading, copying or use of which is subject to additional terms and conditions, stated or unstated. %{company_name} disclaims any responsibility for any harm resulting from the use by visitors of the Website, or from any downloading by those visitors of content there posted. + + + + ## [7. Content Posted on Other Websites](#7) + + We have not reviewed, and cannot review, all of the material, including computer software, made available through the websites and webpages to which %{company_domain} links, and that link to %{company_domain}. %{company_name} does not have any control over those non-%{company_domain} websites and webpages, and is not responsible for their contents or their use. By linking to a non-%{company_domain} website or webpage, %{company_name} does not represent or imply that it endorses such website or webpage. You are responsible for taking precautions as necessary to protect yourself and your computer systems from viruses, worms, Trojan horses, and other harmful or destructive content. %{company_name} disclaims any responsibility for any harm resulting from your use of non-%{company_domain} websites and webpages. + + + + ## [8. Copyright Infringement and DMCA Policy](#8) + + As %{company_name} asks others to respect its intellectual property rights, it respects the intellectual property rights of others. If you believe that material located on or linked to by %{company_domain} violates your copyright, and if this website resides in the USA, you are encouraged to notify %{company_name} in accordance with %{company_name}’s [Digital Millennium Copyright Act](http://en.wikipedia.org/wiki/Digital_Millennium_Copyright_Act) ("DMCA") Policy. %{company_name} will respond to all such notices, including as required or appropriate by removing the infringing material or disabling all links to the infringing material. %{company_name} will terminate a visitor’s access to and use of the Website if, under appropriate circumstances, the visitor is determined to be a repeat infringer of the copyrights or other intellectual property rights of %{company_name} or others. In the case of such termination, %{company_name} will have no obligation to provide a refund of any amounts previously paid to %{company_name}. + + + + ## [9. Intellectual Property](#9) + + This Agreement does not transfer from %{company_name} to you any %{company_name} or third party intellectual property, and all right, title and interest in and to such property will remain (as between the parties) solely with %{company_name}. %{company_name}, %{company_domain}, the %{company_domain} logo, and all other trademarks, service marks, graphics and logos used in connection with %{company_domain}, or the Website are trademarks or registered trademarks of %{company_name} or %{company_name}’s licensors. Other trademarks, service marks, graphics and logos used in connection with the Website may be the trademarks of other third parties. Your use of the Website grants you no right or license to reproduce or otherwise use any %{company_name} or third-party trademarks. + + + + ## [10. Advertisements](#10) + + %{company_name} reserves the right to display advertisements on your content unless you have purchased an Ad-free Upgrade or a Services account. + + + + ## [11. Attribution](#11) + + %{company_name} reserves the right to display attribution links such as ‘Powered by %{company_domain},’ theme author, and font attribution in your content footer or toolbar. Footer credits and the %{company_domain} toolbar may not be removed regardless of upgrades purchased. + + + + ## [12. Changes](#12) + + %{company_name} reserves the right, at its sole discretion, to modify or replace any part of this Agreement. It is your responsibility to check this Agreement periodically for changes. Your continued use of or access to the Website following the posting of any changes to this Agreement constitutes acceptance of those changes. %{company_name} may also, in the future, offer new services and/or features through the Website (including, the release of new tools and resources). Such new features and/or services shall be subject to the terms and conditions of this Agreement. + + + + ## [13. Termination](#13) + + %{company_name} may terminate your access to all or any part of the Website at any time, with or without cause, with or without notice, effective immediately. If you wish to terminate this Agreement or your %{company_domain} account (if you have one), you may simply discontinue using the Website. All provisions of this Agreement which by their nature should survive termination shall survive termination, including, without limitation, ownership provisions, warranty disclaimers, indemnity and limitations of liability. + + + + ## [14. Disclaimer of Warranties](#14) + + The Website is provided "as is". %{company_name} and its suppliers and licensors hereby disclaim all warranties of any kind, express or implied, including, without limitation, the warranties of merchantability, fitness for a particular purpose and non-infringement. Neither %{company_name} nor its suppliers and licensors, makes any warranty that the Website will be error free or that cess thereto will be continuous or uninterrupted. If you’re actually reading this, here’s [a treat](http://www.newyorker.com/online/blogs/shouts/2012/12/the-hundred-best-lists-of-all-time.html). You understand that you download from, or otherwise obtain content or services through, the Website at your own discretion and risk. + + + + ## [15. Limitation of Liability](#15) + + In no event will %{company_name}, or its suppliers or licensors, be liable with respect to any subject matter of this agreement under any contract, negligence, strict liability or other legal or equitable theory for: (i) any special, incidental or consequential damages; (ii) the cost of procurement for substitute products or services; (iii) for interruption of use or loss or corruption of data; or (iv) for any amounts that exceed the fees paid by you to %{company_name} under this agreement during the twelve (12) month period prior to the cause of action. %{company_name} shall have no liability for any failure or delay due to matters beyond their reasonable control. The foregoing shall not apply to the extent prohibited by applicable law. + + + + ## [16. General Representation and Warranty](#16) + + You represent and warrant that (i) your use of the Website will be in strict accordance with the %{company_name} [Privacy Policy](/privacy), [Community Guidelines](/guidelines), with this Agreement and with all applicable laws and regulations (including without limitation any local laws or regulations in your country, state, city, or other governmental area, regarding online conduct and acceptable content, and including all applicable laws regarding the transmission of technical data exported from the country in which this website resides or the country in which you reside) and (ii) your use of the Website will not infringe or misappropriate the intellectual property rights of any third party. + + + + ## [17. Indemnification](#17) + + You agree to indemnify and hold harmless %{company_name}, its contractors, and its licensors, and their respective directors, officers, employees and agents from and against any and all claims and expenses, including attorneys’ fees, arising out of your use of the Website, including but not limited to your violation of this Agreement. + + + + ## [18. Miscellaneous](#18) + + This Agreement constitutes the entire agreement between %{company_name} and you concerning the subject matter hereof, and they may only be modified by a written amendment signed by an authorized executive of %{company_name}, or by the posting by %{company_name} of a revised version. Except to the extent applicable law, if any, provides otherwise, this Agreement, any access to or use of the Website will be governed by the laws of the state of California, U.S.A., excluding its conflict of law provisions, and the proper venue for any disputes arising out of or relating to any of the same will be the state and federal courts located in San Francisco County, California. Except for claims for injunctive or equitable relief or claims regarding intellectual property rights (which may be brought in any competent court without the posting of a bond), any dispute arising under this Agreement shall be finally settled in accordance with the Comprehensive Arbitration Rules of the Judicial Arbitration and Mediation Service, Inc. (“JAMS”) by three arbitrators appointed in accordance with such Rules. The arbitration shall take place in San Francisco, California, in the English language and the arbitral decision may be enforced in any court. The prevailing party in any action or proceeding to enforce this Agreement shall be entitled to costs and attorneys’ fees. If any part of this Agreement is held invalid or unenforceable, that part will be construed to reflect the parties’ original intent, and the remaining portions will remain in full force and effect. A waiver by either party of any term or condition of this Agreement or any breach thereof, in any one instance, will not waive such term or condition or any subsequent breach thereof. You may assign your rights under this Agreement to any party that consents to, and agrees to be bound by, its terms and conditions; %{company_name} may assign its rights under this Agreement without condition. This Agreement will be binding upon and will inure to the benefit of the parties, their successors and permitted assigns. + + This document is CC-BY-SA. It was last updated May 31, 2013. + + Originally adapted from the [WordPress Terms of Service](http://en.wordpress.com/tos/). + privacy_topic: + title: "Gizlilik İlkeleri" + body: | + + + ## [What information do we collect?](#collect) + + We collect information from you when you register on our site and gather data when you participate in the forum by reading, writing, and evaluating the content shared here. + + When registering on our site, you may be asked to enter your name and e-mail address. You may, however, visit our site without registering. Your e-mail address will be verified by an email containing a unique link. If that link is visited, we know that you control the e-mail address. + + When registered and posting, we record the IP address that the post originated from. We also may retain server logs which include the IP address of every request to our server. + + + + ## [What do we use your information for?](#use) + + Any of the information we collect from you may be used in one of the following ways: + + * To personalize your experience — your information helps us to better respond to your individual needs. + * To improve our site — we continually strive to improve our site offerings based on the information and feedback we receive from you. + * To improve customer service — your information helps us to more effectively respond to your customer service requests and support needs. + * To send periodic emails — The email address you provide may be used to send you information, notifications that you request about changes to topics or in response to your user name, respond to inquiries, and/or other requests or questions. + + + + ## [How do we protect your information?](#protect) + + We implement a variety of security measures to maintain the safety of your personal information when you enter, submit, or access your personal information. + + + + ## [What is your data retention policy?](#data-retention) + + We will make a good faith effort to: + + * Retain server logs containing the IP address of all requests to this server no more than 90 days. + * Retain the IP addresses associated with registered users and their posts no more than 5 years. + + + + ## [Do we use cookies?](#cookies) + + Yes. Cookies are small files that a site or its service provider transfers to your computer's hard drive through your Web browser (if you allow). These cookies enable the site to recognize your browser and, if you have a registered account, associate it with your registered account. + + We use cookies to understand and save your preferences for future visits and compile aggregate data about site traffic and site interaction so that we can offer better site experiences and tools in the future. We may contract with third-party service providers to assist us in better understanding our site visitors. These service providers are not permitted to use the information collected on our behalf except to help us conduct and improve our business. + + + + ## [Do we disclose any information to outside parties?](#disclose) + + We do not sell, trade, or otherwise transfer to outside parties your personally identifiable information. This does not include trusted third parties who assist us in operating our site, conducting our business, or servicing you, so long as those parties agree to keep this information confidential. We may also release your information when we believe release is appropriate to comply with the law, enforce our site policies, or protect ours or others rights, property, or safety. However, non-personally identifiable visitor information may be provided to other parties for marketing, advertising, or other uses. + + + + ## [Third party links](#third-party) + + Occasionally, at our discretion, we may include or offer third party products or services on our site. These third party sites have separate and independent privacy policies. We therefore have no responsibility or liability for the content and activities of these linked sites. Nonetheless, we seek to protect the integrity of our site and welcome any feedback about these sites. + + + + ## [Children's Online Privacy Protection Act Compliance](#coppa) + + Our site, products and services are all directed to people who are at least 13 years old or older. If this server is in the USA, and you are under the age of 13, per the requirements of COPPA ([Children's Online Privacy Protection Act](http://en.wikipedia.org/wiki/Children)), do not use this site. + + + + ## [Online Privacy Policy Only](#online) + + This online privacy policy applies only to information collected through our site and not to information collected offline. + + + + ## [Your Consent](#consent) + + By using our site, you consent to our web site privacy policy. + + + + ## [Changes to our Privacy Policy](#changes) + + If we decide to change our privacy policy, we will post those changes on this page. + + This document is CC-BY-SA. It was last updated May 31, 2013. + static: + search_help: | +

Öneriler

+

+

    +
  • Başlık eşleşmelerine öncelik verilir, emin olmadığınız zaman, başlık için arama yapın
  • +
  • Her zaman tekil, nadir kelimeler en iyi sonuçları verir
  • +
  • Mümkünse, aramanızı belirli bir kategori, kullanıcı, veya konuya daraltın
  • +
+

+

Seçenekler

+

+ + + + + + +
order:viewsorder:latest
status:openstatus:closedstatus:archivedstatus:norepliesstatus:singleuser
category:foouser:foo
in:likesin:postedin:watchingin:trackingin:private
in:bookmarks
+

+

+ gökkuşağı category:parklar status:open order:latest "parklar" kategorisinde, kapalı veya arşivlenmiş olmayan konular arasında "gökkuşağı" kelimesi içerenleri arar ve sonuçları gönderi tarihi sırasına göre gösterir. +

diff --git a/config/locales/server.uk.yml b/config/locales/server.uk.yml index 9419fea080..f993cc96cb 100644 --- a/config/locales/server.uk.yml +++ b/config/locales/server.uk.yml @@ -10,11 +10,6 @@ uk: short_date_no_year: "D MMM" short_date: "D MMM, YYYY" long_date: "MMMM D, YYYY h:mma" - time: - formats: - short: "%m-%d-%Y" - short_no_year: "%B %-d" - date_only: "%b %-d, %Y" title: "Discourse" topics: "Теми" posts: "дописи" @@ -126,10 +121,6 @@ uk: is_invalid: "is invalid; try to be a little more descriptive" has_already_been_used: "вже було використано" models: - topic: - attributes: - archetype: - cant_send_pm: "Даруйте, Ви не можете надіслати приватне повідомлення цьому користувачу." user: attributes: ip_address: @@ -316,15 +307,7 @@ uk: s3_config_warning: 'The server is configured to upload files to s3, but at least one the following setting is not set: s3_access_key_id, s3_secret_access_key or s3_upload_bucket. Go to the Site Settings and update the settings. See "How to set up image uploads to S3?" to learn more.' image_magick_warning: 'The server is configured to create thumbnails of large images, but ImageMagick is not installed. Install ImageMagick using your favorite package manager or download the latest release.' failing_emails_warning: 'There are %{num_failed_jobs} email jobs that failed. Check your config/discourse.conf file and ensure that the mail server settings are correct. See the failed jobs in Sidekiq.' - default_logo_warning: "You haven't customized the logo images for your site. Update logo_url, logo_small_url, and favicon_url in the Site Settings." - contact_email_missing: "You haven't provided a contact email for your site. Please update contact_email in the Site Settings." - contact_email_invalid: "The site contact email is invalid. Please update contact_email in the Site Settings." - title_nag: "The title Site Setting is still set to the default value. Please update it with your site's title in the Site Settings." - site_description_missing: "The site_description setting is blank. Write a brief description of this forum in the Site Settings." consumer_email_warning: "Your site is configured to use Gmail (or another consumer email service) to send email. Gmail limits how many emails you can send. Consider using an email service provider like mandrill.com to ensure email deliverability." - access_password_removal: "Your site was using the access_password setting, which has been removed. The login_required and must_approve_users settings have been enabled, which should be used instead. You can change them in the Site Settings. Be sure to approve users in the Pending Users list. (This message will go away after 2 days.)" - site_contact_username_warning: "The site_contact_username setting is blank. Please update it in the Site Settings. Set it to the username of an admin user who should be the sender of system messages." - notification_email_warning: "The notification_email setting is blank. Please update it in the Site Settings." content_types: education_new_reply: title: "Навчання новачка: Перші відповіді" @@ -382,7 +365,6 @@ uk: ga_universal_tracking_code: "Код відстеження Google Universal Analytics (analytics.js), напр.: UA-12345678-9; див. http://google.com/analytics" ga_universal_domain_name: "Доменне ім'я Google Universal Analytics (analytics.js), напр.: mysite.com; див. http://google.com/analytics" enable_noscript_support: "Увімкнути стандартну підтримку пошукових роботів через теґ noscript" - top_menu: "Визначає, які елементи з'являються в навігації головної сторінки, та в якому порядку. Наприклад, latest|new|unread|starred|categories|top|read|posted" post_menu: "Визначає, які елементи з'являються в меню допису, та в якому порядку. Наприклад, like|edit|flag|delete|share|bookmark|reply" track_external_right_clicks: "Track external links that are right clicked (eg: open in new tab) disabled by default because it rewrites URLs" port: "ТІЛЬКИ ДЛЯ РОЗРОБНИКІВ! УВАГА! Використовувати цей HTTP-порт замість звичайного порту 80. Лишіть порожнім, щоб використовувати значення за замовчуванням 80." @@ -390,7 +372,6 @@ uk: invite_expiry_days: "Скільки днів ключі запрошення користувачів лишаються чинними" min_password_length: "Мінімальна довжина пароля." sso_url: "URL of single sign on endpoint" - sso_secret: "Secret string used to encrypt/decrypt SSO information, be sure it is 10 chars or longer" sso_overrides_email: "Overrides local email with external site email from SSO payload (WARNING: discrepancies can occur due to normalization of local emails)" sso_overrides_username: "Overrides local username with external site username from SSO payload (WARNING: discrepancies can occur due to differences in username length/requirements)" sso_overrides_name: "Overrides local name with external site name from SSO payload (WARNING: discrepancies can occur due to normalization of local names)" @@ -427,7 +408,6 @@ uk: default_external_links_in_new_tab: "Відкривати нові посилання у новій вкладці. Користувачі можуть змінити це у своїх налаштуваннях." allow_profile_backgrounds: "Дозволити користувачам завантажувати фони профілю." enable_mobile_theme: "Мобільні пристрої використовуватимуть тему, дружню для них, з можливістю перемикатися на повний сайт. Відключіть це, якщо хочете використовувати власну, повністю чутливу, таблицю стилів." - enable_names: "Дозволити показувати повні імена користувачів. Відключити, щоб сховати повні імена." display_name_on_posts: "Показувати повні імена користувачів на їх дописах у додаток до їх @username." invites_per_page: "Запрошення по замовчуванню показані на сторінці користувача." short_progress_text_threshold: "Після того, як кількість дописів в темі перевищить це число, індикатор просування по темі показуватиме тільки номер поточного допису. Якщо Ви зміните ширину індикатора, Вам, ймовірно, знадобиться змінити це значення." @@ -482,7 +462,6 @@ uk: errors: "%{errors}" not_available: "Not available. Try %{suggestion}?" something_already_taken: "Щось пішло не так, можливо, ім'я користувача або електронна скринька вже зареєстровані. Спробуйте посилання \"Я забув пароль\"." - omniauth_error: "Sorry, there was an error authorizing your %{strategy} account. Perhaps you did not approve authorization?" omniauth_error_unknown: "Something went wrong processing your log in, please try again." user: username: @@ -653,10 +632,7 @@ uk: unauthorized: "Sorry, the file you are trying to upload is not authorized (authorized extensions: %{authorized_extensions})." pasted_image_filename: "Вставлене зображення" store_failure: "Не вдалося зберегти завантаження #%{upload_id} для користувача #%{user_id}." - attachments: - too_large: "Sorry, the file you are trying to upload is too big (maximum size is %{max_size_kb}%kb)." images: - too_large: "Sorry, the image you are trying to upload is too big (maximum size is %{max_size_kb}%kb), please resize it and try again." fetch_failure: "Sorry, there has been an error while fetching the image." unknown_image_type: "Sorry, but the file you tried to upload doesn't appear to be an image." size_not_found: "Sorry, but we couldn't determine the size of the image. Maybe your image is corrupted?" diff --git a/config/locales/server.zh_CN.yml b/config/locales/server.zh_CN.yml index ea595c2c3a..f61ca6fbfd 100644 --- a/config/locales/server.zh_CN.yml +++ b/config/locales/server.zh_CN.yml @@ -20,26 +20,54 @@ zh_CN: short_date_no_year: "MMMDo" short_date: "ll" long_date: "lll" - time: - formats: - short: "%Y年%m月%d日" - short_no_year: "%B %-d" - date_only: "%b %-d,%Y" title: "Discourse" topics: "主题列表" posts: "帖子" loading: "载入中" - powered_by_html: '源自论坛 - Discourse,请启用 JavaScript 以获得最佳效果' - log_in: "登陆" + powered_by_html: '采用 Discourse,启用 JavaScript 以获得最佳效果' + log_in: "登录" via: "%{username} 自 %{site_name}" is_reserved: "保留权利" - purge_reason: "自动删除长期未使用或者未验证" + purge_reason: "自动删除被遗弃、未激活账户" disable_remote_images_download_reason: "磁盘空间不足,远程图像下载已经被禁用。" errors: + format: '%{attribute} %{message}' messages: - too_long_validation: "最多只能有 %{max} 个字符;您已经输入了 %{length} 个字符。" + too_long_validation: "最多只能有 %{max} 个字符;你已经输入了 %{length} 个字符。" invalid_boolean: "无效布尔值。" taken: "已经被使用" + accepted: 必须被接受 + blank: 不能为空 + present: 必须为空 + confirmation: "不匹配 %{attribute}" + empty: 不能为空 + equal_to: 必须等于 %{count} + even: 必须为奇数 + exclusion: 被保留 + greater_than: 必须大于 %{count} + greater_than_or_equal_to: 必须大于或等于 %{count} + inclusion: 不包括在列表中 + invalid: 无效 + less_than: 必须小于 %{count} + less_than_or_equal_to: 必须小于等于 %{count} + not_a_number: 不是数字 + not_an_integer: 必须是数字 + odd: 必须是偶数 + record_invalid: '验证失败:%{errors}' + restrict_dependent_destroy: + one: "因为独立的%{record}存在不能删除记录" + many: "因为独立的%{record}存在不能删除记录" + too_long: + other: 太长(最长 %{count} 字符) + too_short: + other: 太短(最长 %{count} 字符) + wrong_length: + other: 长度错误(应为 %{count} 字符) + other_than: "必须不是 %{count}" + template: + body: '下列栏目有问题:' + header: + other: 有 %{count} 错误使得 %{model} 无法被保存 embed: load_from_remote: "载入帖子时遇到错误。" bulk_invite: @@ -48,7 +76,7 @@ zh_CN: operation_already_running: "一个操作正在运行。目前无法开始新的操作。" backup_file_should_be_tar_gz: "备份文件应为 .tar.gz 存档文件。" not_enough_space_on_disk: "没有足够的磁盘空间可供备份文件上传。" - not_logged_in: "您需要登录后才能这么做。" + not_logged_in: "你需要登录后才能这么做。" read_only_mode_enabled: "这个站点正处于只读模式。交互功能已禁用。" too_many_replies: other: "我们非常抱歉,但是新用户被临时限制为在同一个主题只能回复 %{count} 次" @@ -64,9 +92,9 @@ zh_CN: replies: other: "%{count} 个回复" too_many_mentions: - zero: "抱歉,您无法提到其他用户。" - one: "抱歉,您一次仅能提到 1 个用户。" - other: "抱歉,您一次仅能提到 %{count} 个用户。" + zero: "抱歉,你无法提到其他用户。" + one: "抱歉,你一次仅能提到 1 个用户。" + other: "抱歉,你一次仅能提到 %{count} 个用户。" too_many_mentions_newuser: zero: "抱歉,新用户不能提到其他用户。" one: "抱歉,新用户仅能提到 1 个用户。" @@ -83,16 +111,16 @@ zh_CN: zero: "抱歉,游客无法贴链接。" one: "抱歉,游客一次仅能贴 1 条链接。" other: "抱歉,游客一次仅能贴 %{count} 条链接。" - spamming_host: "抱歉,您不能添加一个链接到那个地址的链接。" + spamming_host: "抱歉,你不能添加一个链接到那个地址的链接。" user_is_suspended: "被封禁的用户不允许发贴。" - just_posted_that: "与您最近发表的帖子太过相似" + just_posted_that: "与你最近发表的帖子太过相似" has_already_been_used: "已经被使用" invalid_characters: "包含无效字符" is_invalid: "无效;请描述得更清楚一点" next_page: "下一页 →" prev_page: "← 上一页" - page_num: "#{num} 页" - topics_in_category: "分类 '%{category}' 中的主题" + page_num: "%{num} 页" + topics_in_category: "'%{category}'分类中的主题" rss_posts_in_topic: "'%{topic}' 的 RSS 内容聚合" rss_topics_in_category: "分类 '%{category}' 中主题的 RSS 内容聚合" author_wrote: "%{author} 写道:" @@ -104,9 +132,10 @@ zh_CN: latest: "最新主题" hot: "最热主题" too_late_to_edit: "这个主题在很早之前创建。不能被编辑或者被删除。" + excerpt_image: "图片" groups: errors: - can_not_modify_automatic: "您不能修改一个自动组" + can_not_modify_automatic: "你不能修改一个自动组" default_names: everyone: "任何人" admins: "管理面板" @@ -121,31 +150,31 @@ zh_CN: until_posts: other: "%{count} 个帖子" new-topic: | - 欢迎来到 %{site_name} — **衷心感谢您开启一段新的讨论!** + 欢迎来到%{site_name} — **衷心感谢你开启一段新的讨论!** - 标题是否清晰明了地描述了主题?看起来有意思么? - - 主题是关于什么的?谁会感兴趣?它为什么重要?您期望从社区中获得什么样的回应? + - 主题是关于什么的?谁会感兴趣?它为什么重要?你期望从社区中获得什么样的回应? - - 选择恰当的词句以便别人能找到您的主题。若想要给主题分组,选择一个分类。 + - 选择恰当的词句以便别人能找到你的主题。若想要给主题分组,选择一个分类。 - 更多的帮助信息,[请查看我们的社群准则](/guidelines)。此信息面板只在您发布前 %{education_posts_text}时显示。 + 更多的帮助信息,[请查看我们的社群准则](/guidelines)。此信息面板只在你发布前 %{education_posts_text}时显示。 new-reply: | - 欢迎来到 %{site_name} &mdash;**感谢您的贡献!** + 欢迎来到%{site_name} — **感谢你的贡献!** - - 您的回复是否以某种方式改善了这段讨论? + - 你的回复是否以某种方式改善了讨论? - - 请给予论坛其他成员同等的尊重。 + - 请给予其他社群成员同等的尊重。 - - 持批判态度是可以的,但是请对事不对人。 + - 欢迎有建设性的批评,但是请批判观点,而不是人身攻击。 - 更多的帮助信息,[请查看我们的社群准则](/guidelines)。此信息面板只在您的最早的 %{education_posts_text} 显示。 + 欲查看更多,[请查看我们的社群准则](/guidelines)。此信息面板只会在你发表前 %{education_posts_text} 时显示。 avatar: | - ### 为您的账户添加一张图片吧? + ### 为你的账户添加一张图片吧? - 您已经发表了一些主题和回复,但是您的头像不能展示您的独一无二——您使用的是新用户默认的头像。 + 你已经发表了一些主题和回复,但是你的头像不能展示你的独一无二——你使用的是新用户默认的头像。 - 您考虑**[访问您的用户资料页面](%{profile_path})**并且上传一个展示您的自定义头像吗? + 你考虑**[访问你的用户资料页面](%{profile_path})**并且上传一个展示你的自定义头像吗? 当每一个人都有一个独立的头像时更容易跟随社群讨论并且在交流中找到有意思的人们! sequential_replies: | @@ -153,17 +182,17 @@ zh_CN: 与其连续地回复主题,请考虑在一个单独的回复中引用或者 @name 引用前面的帖子。 - 您可以编辑您之前的回复以添加引用。添加引用需要选择文字并且点击出现的引用回复按钮。 + 你可以编辑你之前的回复以添加引用。添加引用需要选择文字并且点击出现的引用回复按钮。 当主题中的帖子不是许多小的和独立的回复,而是更少的更有深度的帖子时,更容易让我们去阅读这个主题。 dominating_topic: | ### 让其他人加入讨论 - 很明显这个主题对您很重要 – 您已经发表的帖子占该主题 %{percent}% 的回复。 + 很明显这个主题对你很重要 – 你已经发表的帖子占该主题 %{percent}% 的回复。 - 您确定您也让其他人自由地表达他们的观点了吗? + 你确定你也让其他人自由地表达他们的观点了吗? too_many_replies: | - ### 您发表的回复数量已经达到该主题的回复上限。 + ### 你发表的回复数量已经达到该主题的回复上限。 非常抱歉,但新用户被临时限制,只能在同一主题中回复 %{newuser_max_replies_per_topic} 次。 @@ -171,9 +200,9 @@ zh_CN: reviving_old_topic: | ### 复活这个主题? - 这个主题的最后一个回复距今已有 %{days} 天了。您的回复将让主题重新出现在列表的顶端并通知原先参与讨论的人。 + 这个主题的最后一个回复距今已有 %{days} 天了。你的回复将让主题重新出现在列表的顶端并通知原先参与讨论的人。 - 您确定要继续这个老的话题吗? + 你确定要继续这个老的话题吗? activerecord: attributes: category: @@ -190,10 +219,10 @@ zh_CN: topic: attributes: base: - warning_requires_pm: "您只能使用私信警告。" - too_many_users: "您一次只能发送警告给一个用户。" - archetype: - cant_send_pm: "抱歉,您不能向该用户发送私信。" + warning_requires_pm: "你只能使用私信警告。" + too_many_users: "你一次只能发送警告给一个用户。" + cant_send_pm: "抱歉,你不能向该用户发送私信。" + no_user_selected: "你必须选择一个有效的用户。" user: attributes: password: @@ -205,7 +234,7 @@ zh_CN: hex: invalid: "不是有效颜色值" user_profile: - no_info_me: "
您的资料中,关于我部分目前还是空白,您想要写些什么吗?
" + no_info_me: "
你的资料中,关于我部分目前还是空白,你想要写些什么吗?
" no_info_other: "
%{name} 尚未在他们的资料中的关于我部分填写任何信息
" vip_category_name: "贵宾室" vip_category_description: "高于信任等级3的用户参与讨论的分类。" @@ -213,20 +242,20 @@ zh_CN: meta_category_description: "讨论该站点的站务、组织、如何运作和如何改善它。" staff_category_name: "职员" staff_category_description: "职员私有的分类。只有管理员和版主才能阅览主题。" - assets_topic_body: "这是一个永久主题,仅对职员可见,用于存储论坛设计使用的图像和文件。不要删除它!\n\n\n详细教程:\n\n\n1. 回复到这个主题。\n2. 上传您想用作站点标志、图标和其他所有图片到这儿。(使用帖子编辑器工具栏中的上传图标,或者拖拽或者粘贴图像。)\n3. 提交您的回复添加帖子。\n4. 在您的新帖子里右键点击图片获得这些已上传图片的链接,或者点击编辑按钮来编辑帖子,获得到图片的相对地址。复制这些相对地址。\n5. 粘贴这些图像的路径到[基础设置](/admin/site_settings/category/required)。\n\n\n如果您需要运行额外的上传文件类型,在[文件设置](/admin/site_settings/category/files)中编辑 `authorized_extensions`。" + assets_topic_body: "这是一个永久主题,仅对职员可见,用于存储论坛设计使用的图像和文件。不要删除它!\n\n\n详细教程:\n\n\n1. 回复到这个主题。\n2. 上传你想用作站点标志、图标和其他所有图片到这儿。(使用帖子编辑器工具栏中的上传图标,或者拖拽或者粘贴图像。)\n3. 提交你的回复添加帖子。\n4. 在你的新帖子里右键点击图片获得这些已上传图片的链接,或者点击编辑按钮来编辑帖子,获得到图片的相对地址。复制这些相对地址。\n5. 粘贴这些图像的路径到[基础设置](/admin/site_settings/category/required)。\n\n\n如果你需要运行额外的上传文件类型,在[文件设置](/admin/site_settings/category/files)中编辑 `authorized_extensions`。" lounge_welcome: title: "欢迎来到贵宾室" body: |2 恭喜! :confetti_ball: - 如果您看到了这个主题,说明您已经被提升至**常规**(信任等级3)了。 + 如果你看到了这个主题,说明你已经被提升至**常规**(信任等级3)了。 - 您现在可以… + 你现在可以… * 编辑任何主题的标题 * 改变任何主题的分类 - * 让您的链接设置为 follow 属性([自动 nofollow](http://en.wikipedia.org/wiki/Nofollow)限制已经移除) + * 让你的链接设置为 follow 属性([自动 nofollow](http://en.wikipedia.org/wiki/Nofollow)限制已经移除) * 访问一个只有信任等级3及更高才能见到的贵宾室分类 * 一次标记即可隐藏垃圾信息 @@ -239,12 +268,12 @@ zh_CN: [trust]: https://meta.discourse.org/t/what-do-user-trust-levels-do/4924 category: topic_prefix: "关于分类:%{category}" - replace_paragraph: "[用一段简短的话描述分类,并替换这第一段的内容。这段文字将出现在分类选择区域,所以尽量不要超过200个字符。当您编辑了这段文字或者再次分类创建了一个主题后,分类才会出现在分类列表中。]" + replace_paragraph: "[用一段简短的话描述分类,并替换这第一段的内容。这段文字将出现在分类选择区域,所以尽量不要超过200个字符。当你编辑了这段文字或者再次分类创建了一个主题后,分类才会出现在分类列表中。]" post_template: "%{replace_paragraph}\n\n在接下来的一段文字中输入分类的详细描述信息,可以在这里包含在此分类下讨论的规则、内容导向等等。\n\n考虑这些事情:\n\n- 这个分类用来做什么? 为什么人们选择这个分类寻找主题?\n\n- 这个分类和其他已有分类有什么不同?\n\n- 我们需要这个分类么?\n\n- 我们应该和已有分类合并吗?还是分离成更多的分类?\n" errors: uncategorized_parent: "未分类不能有一个父分类" self_parent: "一个子分类不能属于它自己。" - depth: "您不能在一个子分类下再包含一个子分类。" + depth: "你不能在一个子分类下再包含一个子分类。" cannot_delete: uncategorized: "不能删除未分类。" has_subcategories: "不能删除该分类,因为它有子分类" @@ -262,10 +291,10 @@ zh_CN: title: "常规" elder: title: "领导" - change_failed_explanation: "您尝试将 %{user_name} 降至 '%{new_trust_level}'。然而他们的信任等级已经是 '%{current_trust_level}'。%{user_name} 将仍是 '%{current_trust_level}' —— 如果你想要降级用户,先锁定信任等级" + change_failed_explanation: "你尝试将 %{user_name} 降至 '%{new_trust_level}'。然而他们的信任等级已经是 '%{current_trust_level}'。%{user_name} 将仍是 '%{current_trust_level}' —— 如果你想要降级用户,先锁定信任等级" rate_limiter: - slow_down: "您已经重复这一操作太多次了,请一会再重试" - too_many_requests: "您的请求过于频繁,请等待 %{time_left} 之后再试。" + slow_down: "你已经重复这一操作太多次了,请一会再重试" + too_many_requests: "你的请求过于频繁,请等待 %{time_left} 之后再试。" hours: other: "%{count} 小时" minutes: @@ -274,29 +303,29 @@ zh_CN: other: "%{count} 秒" datetime: distance_in_words: - half_a_minute: "< 1m" + half_a_minute: "< 1 分" less_than_x_seconds: - other: "< %{count}s" + other: "< %{count} 秒" x_seconds: - other: "%{count}s" + other: "%{count} 秒" less_than_x_minutes: - other: "< %{count}m" + other: "< %{count} 分" x_minutes: - other: "%{count}m" + other: "%{count} 分" about_x_hours: - other: "%{count}h" + other: "%{count} 小时" x_days: - other: "%{count}d" + other: "%{count} 天" about_x_months: other: "%{count} 分钟" x_months: other: "%{count} 分钟" about_x_years: - other: "%{count}y" + other: "%{count} 年" over_x_years: - other: "> %{count}y" + other: "> %{count} 年" almost_x_years: - other: "%{count}y" + other: "%{count} 年" distance_in_words_verbose: half_a_minute: "刚刚" less_than_x_seconds: @@ -322,26 +351,26 @@ zh_CN: almost_x_years: other: "将近 %{count} 年前" password_reset: - no_token: "抱歉,您的令牌已过期,请尝试再次重置您的密码。" + no_token: "抱歉,你的令牌已过期,请尝试再次重置你的密码。" choose_new: "请选择一个新密码" choose: "请选择一个密码" update: '更新密码' save: '设置密码' title: '重置密码' - success: "您的密码已经修改成功,您现在已经登录。" - success_unapproved: "您的密码已经修改成功。" + success: "你的密码已经修改成功,你现在已经登录。" + success_unapproved: "你的密码已经修改成功。" continue: "转入到 %{site_name}" change_email: - confirmed: "您的电子邮箱已被更新。" + confirmed: "你的电子邮箱已被更新。" please_continue: "转入到 %{site_name}" - error: "在修改您的电子邮箱地址时出现了错误,可能此邮箱已经在论坛中使用了?" + error: "在修改你的电子邮箱地址时出现了错误,可能此邮箱已经在论坛中使用了?" activation: - action: "激活您的帐号" - already_done: "抱歉,此帐号激活链接已经失效。可能您的帐号已经被激活了?" - please_continue: "您的新帐号已激活;即将转到主页。" + action: "激活你的帐号" + already_done: "抱歉,此帐号激活链接已经失效。可能你的帐号已经被激活了?" + please_continue: "你的新帐号已激活;即将转到主页。" continue_button: "转入到 %{site_name}" welcome_to: "欢迎来到%{site_name}!" - approval_required: "您的新帐号需要由一位论坛版主手动批准方可使用。一旦您的帐号获得批准,您将收到一封电子邮件通知!" + approval_required: "你的新帐号需要由一位论坛版主手动批准方可使用。一旦你的帐号获得批准,你将收到一封电子邮件通知!" post_action_types: off_topic: title: '题外话' @@ -351,6 +380,8 @@ zh_CN: title: '垃圾' description: '此帖为广告。它不包含任何对当前讨论有帮助的内容,只有促销信息。' long_form: '标记为垃圾' + email_title: '“%{title}”被标记为垃圾' + email_body: "%{link}\n\n%{message}" inappropriate: title: '不当内容' description: '此帖内容包含对他人的攻击、侮辱、仇视语言或违反了我们的社群准则。' @@ -359,13 +390,13 @@ zh_CN: title: '私信 @{{username}}' description: '此帖包含一些我想与该用户私下直接交流的内容。不能执行标记操作。' long_form: '已私信用户' - email_title: '您在“%{title}”中的帖子' + email_title: '你在“%{title}”中的帖子' email_body: "%{link}\n\n%{message}" notify_moderators: title: "其他事项" description: '此帖需要版主按照以上未列出的原因做出处理。' long_form: '标记为需版主注意' - email_title: '"%{title}" 主题需要您的关注' + email_title: '"%{title}" 主题需要你的关注' email_body: "%{link}\n\n%{message}" bookmark: title: '书签' @@ -392,11 +423,11 @@ zh_CN: title: "其他内容" description: '该主题需要版主依据社群准则服务条款(TOS)或其它未列出的原因来给予关注。' long_form: '标记为需版主注意' - email_title: '"%{title}" 主题需要您的关注' + email_title: '"%{title}" 主题需要你的关注' email_body: "%{link}\n\n%{message}" flagging: - you_must_edit: '

您的帖子被社群成员标记了。请查看您的私信

' - user_must_edit: '

您的帖子已经被社群标记并被临时隐藏。

' + you_must_edit: '

你的帖子被社群成员标记了。请查看你的私信

' + user_must_edit: '

你的帖子已经被社群标记并被临时隐藏。

' archetypes: regular: title: "常规主题" @@ -406,16 +437,16 @@ zh_CN: remove: "本主题已经不再是横幅主题。它将不在每个页面的顶部显示。" unsubscribed: title: '取消订阅' - description: "您已经取消订阅,我们不会再发送邮件给您!" - oops: "如果您并非想要取消订阅,请点击下面。" + description: "你已经取消订阅,我们不会再发送邮件给你!" + oops: "如果你并非想要取消订阅,请点击下面。" error: "取消订阅时出错" - preferences_link: "您也可以在您的参数设置页面取消订阅摘要邮件" - different_user_description: "您现在正以另一个用户的身份登录,而非摘要邮件寄给的用户。请登出并再试一次。" - not_found_description: "抱歉,我们无法帮您取消订阅。可能您的邮件里的链接已过期。" + preferences_link: "你也可以在你的参数设置页面取消订阅摘要邮件" + different_user_description: "你现在正以另一个用户的身份登录,而非摘要邮件寄给的用户。请登出并再试一次。" + not_found_description: "抱歉,我们无法帮你取消订阅。可能你的邮件里的链接已过期。" resubscribe: action: "重新订阅" title: "已重新订阅!" - description: "您已经重新订阅了。" + description: "你已经重新订阅了。" reports: visits: title: "用户访问量" @@ -492,33 +523,72 @@ zh_CN: title: "最高引用主题" xaxis: "主题" num_clicks: "点击数" + page_view_anon_reqs: + title: "匿名" + xaxis: "天" + yaxis: "匿名页面访问量" + page_view_logged_in_reqs: + title: "已登录" + xaxis: "天" + yaxis: "已登录用户页面访问量" + page_view_crawler_reqs: + title: "Web 爬虫" + xaxis: "天" + yaxis: "Web 爬虫页面访问量" + page_view_total_reqs: + title: "总量" + xaxis: "天" + yaxis: "总页面访问量" + http_background_reqs: + title: "后台" + xaxis: "天" + yaxis: "实时更新和追踪的请求" + http_2xx_reqs: + title: "状态码 2xx(正常)" + xaxis: "天" + yaxis: "正常的请求(状态码 2xx)" + http_3xx_reqs: + title: "HTTP 3xx(重定向)" + xaxis: "天" + yaxis: "重定向请求(状态码 3xx)" + http_4xx_reqs: + title: "HTTP 4xx(客户端错误)" + xaxis: "天" + yaxis: "客户端错误(状态码 4xx)" + http_5xx_reqs: + title: "HTTP 5xx(服务器错误)" + xaxis: "天" + yaxis: "服务器错误(状态码 5xx)" + http_total_reqs: + title: "总量" + xaxis: "天" + yaxis: "总请求数" dashboard: - rails_env_warning: "您的服务器运行在 %{env} 模式。" - ruby_version_warning: "您正在使用 Ruby 2.0.0,这个版本有较多的问题。请升级至 p247 或更新版。" - host_names_warning: "您的 config/database.yml 文件使用的是缺省的 localhost 主机名。请更改成您的站点主机名。" - gc_warning: '您的服务器使用的是缺省的 ruby 垃圾回收参数,这个配置无法提供最高性能。请阅读此性能调优文档:Tuning Ruby and Rails for Discourse。' + rails_env_warning: "你的服务器运行在 %{env} 模式。" + ruby_version_warning: "你正在使用 Ruby 2.0.0,这个版本有较多的问题。请升级至 p247 或更新版。" + host_names_warning: "你的 config/database.yml 文件使用的是缺省的 localhost 主机名。请更改成你的站点主机名。" + gc_warning: '你的服务器使用的是缺省的 ruby 垃圾回收参数,这个配置无法提供最高性能。请阅读此性能调优文档:Tuning Ruby and Rails for Discourse。' sidekiq_warning: 'Sidekiq 不在运行。很多任务,例如发送电子邮件,是异步的被 sidekiq 调度执行的。请确保至少运行一个 sidekiq 进程。了解 Sidekiq。' queue_size_warning: '队列中的任务数为 %{queue_size},任务较多。这可能因为 Sidekiq 进程出问题导致,或者需要更多的 Sidekiq 进程。' - memory_warning: '您的服务器环境内存少于 1GB,我们建议至少要有 1GB 内存。' - enable_google_logins_warning: "您正在使用已被废弃的 Google OpenID 验证方式。Google 将在2015年4月20日停止支持 OpenID 登录。尽早开始使用 Google Oauth2。点击了解更多" - both_googles_warning: "您在站点设置中同时启用了 enable_google_logins 和 enable_google_oauth2_logins。禁用 enable_google_logins。" + memory_warning: '你的服务器环境内存少于 1GB,我们建议至少要有 1GB 内存。' + enable_google_logins_warning: "你正在使用已被废弃的 Google OpenID 验证方式。Google 将在2015年4月20日停止支持 OpenID 登录。尽早开始使用 Google Oauth2。点击了解更多" + both_googles_warning: "你在站点设置中同时启用了 enable_google_logins 和 enable_google_oauth2_logins。禁用 enable_google_logins。" google_oauth2_config_warning: '服务器允许使用 Google Oauth2 登录(enable_google_oauth2_logins),但是 client id 和 client secret 没有被设定。 到站点设置更新此设定。 参考设定指南。' facebook_config_warning: '服务器允许使用 Facebook 账号登录(enable_facebook_logins),但是 app id 和 app secret 没有被设定。 到站点设置更新此设定。参考设定指南。' twitter_config_warning: '服务器允许使用 Twitter 账号登录(enable_twitter_logins),但是 key 和 secret 没有被设定。 到站点设置更新此设定。参考设定指南。' github_config_warning: '服务器允许使用 GitHub 账号登录(enable_github_logins),但是 client id 和 secret 没有被设定。 到站点设置更新此设定。参考设定指南。' s3_config_warning: '服务器被配置为上传文件到 s3,但是至少有一个值未被设定: s3_access_key_id, s3_secret_access_key 或 s3_upload_bucket。到站点设置更新此设定。参考如何设置图片上传至 S3。' s3_backup_config_warning: '服务器被配置为上传备份到 s3,但是至少有一个值未被设定: s3_access_key_id, s3_secret_access_key 或 s3_upload_bucket。到站点设置更新此设定。参考如何设置图片上传至 S3。' - image_magick_warning: '服务器被设置为给大图片创建缩略图,但是 ImageMagick 没有被安装。用您最喜爱的包管理器安装 ImageMagick 或下载最新版。' + image_magick_warning: '服务器被设置为给大图片创建缩略图,但是 ImageMagick 没有被安装。用你最喜爱的包管理器安装 ImageMagick 或下载最新版。' failing_emails_warning: '%{num_failed_jobs} 个 email 任务失败。请检查 config/discourse.conf 文件是否正确配置了邮件服务器。在 Sidekiq 中查看失败的任务。' - default_logo_warning: "您没有自定义您的站点 logo。请在站点设置中配置logo_url、logo_small_url 和 favicon_url。" - contact_email_missing: "您没有为站点指定一个联系邮箱。请在站点设置配置 contact_email。" - contact_email_invalid: "站点联系邮箱不正确。请在站点设置修改。" - title_nag: "站点标题在站点设置中仍为缺省值。请在站点设置中将其设置为您的标题。" - site_description_missing: "站点描述 site_description 没有设置。在站点设置中写一段简短的论坛介绍。" - consumer_email_warning: "您的站点被设置为使用 Gmail 或者其他客户邮箱服务发送邮件。Gmail 限制每日邮件发送数量。请考虑使用其他邮件服务商来保证邮件的成功发送,例如 mandrill.com。" - access_password_removal: "您的站点设定了 access_password,这已经被移除了。请使用 login_required 和 must_approve_users 替代。您可以在站点设置修改它们。请一定要在待批准用户列表里批准用户。(这些消息将在 2 天后消失。)" - site_contact_username_warning: "site_contact_username 设定为空。请在站点设置中更新。请将它设定为一个管理员的用户名,他将成为系统消息的发送人。" - notification_email_warning: "otification_email 设定为空。请在站点设置中更新。" + default_logo_warning: "设置你站点的图形logo。请在站点设置中配置logo_url、logo_small_url和favicon_url。" + contact_email_missing: "输入一个站点联系人的邮件地址,这样在用户在网站出现紧急状况时能够找到你。在站点设置中更新它。" + contact_email_invalid: "站点联系邮箱不正确。在站点设置中修改。" + title_nag: "为站点指定一个名字。在站点设置中更新标题。" + site_description_missing: "输入一句话作为你站点的简介,将出现在搜索结果中。在站点设置中更新 site_description。" + consumer_email_warning: "你的站点被设置为使用 Gmail 或者其他客户邮箱服务发送邮件。Gmail 限制每日邮件发送数量。请考虑使用其他邮件服务商来保证邮件的成功发送,例如 mandrill.com。" + site_contact_username_warning: "输入一个友善的职员账户名,并以他的名义发送重要的自动私信。在站点设置中更新 site_contact_username。" + notification_email_warning: "通知邮件不是从你域名的一个有效地址发出的;邮件分发将会变得危险和不可靠。请在站点设置中将 notification_email 设置为一个有效的本地邮件地址。" content_types: education_new_reply: title: "新用户培训:第一个回复" @@ -567,13 +637,10 @@ zh_CN: allow_duplicate_topic_titles: "允许主题有相同,重复的标题" unique_posts_mins: "多少分钟之后才允许一个用户再次发表包含相同内容的帖子" educate_until_posts: "当用户开始键入他们的前几个(n)新帖子时,在编辑器上显示教育面板弹窗。" - title: "本站点的简短标题,用于 HTML 标题标签(title)" - site_description: "用一句话描述这个论坛,用于 meta 描述标签。" - contact_email: "本站关键联系人的电子邮件地址。Discourse.org 可能把重要更新的通知消息发送到该邮箱。" - company_full_name: "废弃。不再使用并且将被移除。运行本站点的公司全称,用于法律文档,例如服务条款 /tos 和 /privacy" - company_short_name: "废弃。不再使用并且将被移除。运行本站点的公司短名,用于法律文档,例如服务条款 /tos 和 /privacy" - company_domain: "废弃。不再使用并且将被移除。运行本站点的公司域名,用于法律文档,例如服务条款 /tos 和 /privacy" - queue_jobs: "仅限开发者设定!警告!默认使用 sidekiq 完成队列工作。如果禁用,您的站点将无法使用。" + title: "站点名字,用于 title 标签。" + site_description: "用一句话描述这个论坛,用于 meta description 标签。" + contact_email: "本站负责人的电子邮件地址。用于紧急事项的通知,如未处理的标记,以及作为 /about 页面中的紧急联系人。" + queue_jobs: "仅限开发者设定!警告!默认使用 sidekiq 完成队列工作。如果禁用,你的站点将无法使用。" crawl_images: "允许从第三方 URL 获取图片来插入宽、高数值" download_remote_images_to_local: "下载一份主题中链接的外部图片到本地;以防图片损坏。" download_remote_images_threshold: "本地最小可用下载外部图片到本地的空间(百分比)" @@ -586,17 +653,17 @@ zh_CN: max_image_height: "帖子中图片允许的最大缩略图宽度" category_featured_topics: "在分类 /categories 页面中每个分类显示的主题数目。在修改这个值之后,分类页面需要 15 分钟后完成更新。" show_subcategory_list: "进入分类时显示子分类列表而不是主题列表。" - fixed_category_positions: "如果选择,您将可以设置一个固定的分类顺序。如果不选择,分类将按照活动的顺序显示。" - add_rel_nofollow_to_user_content: "添加 rel nofollow 属性到所有的用户内容,除了内部链接(包括父域名)。如果您更改了这个,您必须重新调制所有帖子,以该命令:“rake posts:rebake”" + fixed_category_positions: "如果选择,你将可以设置一个固定的分类顺序。如果不选择,分类将按照活动的顺序显示。" + add_rel_nofollow_to_user_content: "添加 rel nofollow 属性到所有的用户内容,除了内部链接(包括父域名)。如果你更改了这个,你必须重新调制所有帖子,以该命令:“rake posts:rebake”" exclude_rel_nofollow_domains: "不添加 nofollow 标签到一列逗号分割的域名(使用父域名,例如 tld.com,将自动允许子域名,例如 sub.tld.com)" post_excerpt_maxlength: "帖子摘要的最大字符长度" post_onebox_maxlength: "Onebox 处理后的 Discourse 帖子的最大字符长度" onebox_domains_whitelist: "对这些站点启用 Onebox 的域名列表;这些域名应该支持 OpenGraph 或 oEmbed。在此测试他们:http://iframely.com/debug" - logo_url: "出现在您站点左上角的标志图片,例如:http://example.com/logo.png" + logo_url: "出现在您站点左上角的标志图片;如果留空,将显示站点标题文字。" digest_logo_url: "站点邮件摘要使用的标志。如果留空,则使用 `logo_url`。例如:http://example.com/logo.png" - logo_small_url: "出现在您站点左上角的小号标志图片,当滚动后可见例如:http://example.com/logo-small.png" - favicon_url: "您的站点图标(favicon),参考 http://zh.wikipedia.org/wiki/Favicon" - mobile_logo_url: "移动浏览器左上角固定的 logo。如果留空,`logo_url`将被使用。例如:http://example.com/uploads/default/logo.png" + logo_small_url: "出现在您站点左上角的小号标志图片,当滚动后可见。如果留空,将显示主页图标。" + favicon_url: "你的站点图标(favicon),参考 http://zh.wikipedia.org/wiki/Favicon" + mobile_logo_url: "移动浏览器左上角的固定标志图片。如果留空,将显示站点标题文字。" apple_touch_icon_url: "Apple 触摸设备使用的图标。推荐 144x144 大小。" notification_email: "这个表格:被用于发送所有重要系统邮件的邮箱地址。指定的域名必须正确设置 SPF、DKIM 和反向 PTR 记录以发送邮件。" email_custom_headers: "一个逗号分离的自定义邮件头部" @@ -635,14 +702,12 @@ zh_CN: enable_noscript_support: "通过 no script 标签启用对标准爬虫的支持。" allow_moderators_to_create_categories: "允许版主创建新的分类" cors_origins: "允许跨源请求(CORS)。每个源必须包括 http:// 或 https://。DISCOURSE_ENABLE_CORS 环境变量必须设置为 true 才能启用 CORS 政策。" - top_menu: "确定在主页导航条包含哪些条目,以及排列顺序。例如:latest|new|unread|starred|categories|top|read|posted" + top_menu: "确定在主页导航条包含哪些条目,以及排列顺序。例如:latest|new|unread|categories|top|read|posted|bookmarks" post_menu: "确定在帖子菜单条包含哪些条目,以及排列顺序。例如:like|edit|flag|delete|share|bookmark|reply" post_menu_hidden_items: "帖子菜单中默认隐藏的按钮,点击省略号后显示。" share_links: "决定分享对话框中出现的按钮及显示顺序。" track_external_right_clicks: "追踪外部链接的右键点击(例如:在浏览器新标签打开),缺省是禁用的,因为它会重写URL" - topics_per_page: "在主题列表页面,缺省载入多少个主题,以及滚动至什么时候载入更多主题" - posts_chunksize: "载入主题时默认载入多少帖子,以及向下滚动时载入多少帖子" - site_contact_username: "论坛给用户发送自动私信时使用的用户名;如果留空将使用默认的系统账户。" + site_contact_username: "论坛给用户自动发送私信时使用的用户名;如果留空将使用默认的系统账户。" send_welcome_message: "给所有用户发送快速开始指导的私信" suppress_reply_directly_below: "当一个帖子只有一个回复时,不显示帖子回复数量。" suppress_reply_directly_above: "当一个帖子只有一个回复时,不显示回复到该贴的回复。" @@ -660,6 +725,7 @@ zh_CN: email_domains_blacklist: "一个邮箱域名黑名单列表,其中的域名将不能用来注册账户,例如:mailinator.com trashmail.net" email_domains_whitelist: "一个电子邮箱域名的列表,用户只能使用这些邮箱域名注册。警告:用户使用不包含在这个列表里的邮箱域名,将无法成功注册。" forgot_password_strict: "在找回密码对话框中不告知用户账户的存在。" + log_out_strict: "登出时,登出用户所有设备的会话" version_checks: "访问 Discourse Hub 来检查版本更新,并在管理面板 /admin 显示新版本信息" new_version_emails: "当新版本发布时,发送一封邮件至 contact_email 设置的地址。" port: "仅限开发者设定!警告!使用此 HTTP 端口而不是80端口。留空为使用默认 80 端口" @@ -672,9 +738,10 @@ zh_CN: max_username_length: "最大用户名长度。警告:所有现用户名大于此限制长度的用户都将无法访问站点。" min_password_length: "最小密码长度。" block_common_passwords: "不允许使用 10,000 个最常用的密码。" - enable_sso: "启用外部站点单点登录(注意:禁用邀请功能)" + enable_sso: "启用通过外部站点单点登录(注意:如果没有正确配置就启用可能会使得所有人都无法登陆;同时禁用邀请功能)" + enable_sso_provider: "在 /session/sso_provider endpoint 实现 Discourse SSO 协议,要求设置 sso_secret" sso_url: "单点登录 URL 入口点" - sso_secret: "秘密字符串,用于加密/解密 SSO 信息,请保证有 10 个字符或者更长" + sso_secret: "秘密字符串,用于验证秘密的 SSO 信息,请保证是 10 个字符或者更长" sso_overrides_email: "用 SSO 信息中的外部邮件地址覆盖本地邮件地址(警告:因为对本地邮件的统一处理,这个邮件地址可能不同)" sso_overrides_username: "用 SSO 信息中的外部用户名覆盖用户名(警告:因为对本地用户名的统一处理,用户名的长度或者要求可能不同)" sso_overrides_name: "用 SSO 信息中的外部名字覆盖本地名字(警告:因为对本地名字的统一处理,这个名字可能不同)" @@ -695,7 +762,7 @@ zh_CN: enable_github_logins: "启用 Github 帐号验证登录,需要 github_client_id 和 github_client_secret" github_client_id: "Github 帐号验证的客户端帐号(Client id),到 https://github.com/settings/applications 来注册获取" github_client_secret: "Github 帐号验证的客户端密码(Client secret),到 https://github.com/settings/applications 来注册获取" - allow_restore: "允许导入数据,这将能替换所有全站数据!除非您计划导入数据,否则请保持设置为 false" + allow_restore: "允许导入数据,这将能替换所有全站数据!除非你计划导入数据,否则请保持设置为 false" maximum_backups: "磁盘保存的最大备份数量。老的备份将自动删除" backup_daily: "每天自动创建站点备份。" enable_s3_backups: "当完成备份后上传备份到 S3。重要:需要在文件设置中填写有效的 S3 验证资料。" @@ -711,15 +778,15 @@ zh_CN: max_flags_per_day: "每个用户每天能标记的数量的最大值。" max_bookmarks_per_day: "每个用户每天能做书签数量的最大值。" max_edits_per_day: "每个用户每天能编辑的次数的最大值。" - max_stars_per_day: "每个用户每天能星标的主题数量的最大值。" max_topics_per_day: "每个用户每天能创建的主题数量的最大值。" max_private_messages_per_day: "每个用户每天能发私信数量的最大值。" + max_invites_per_day: "每个用户每天能创建的邀请数量的最大值。" suggested_topics: "在一个主题底部显示的推荐主题的数量。" limit_suggested_to_category: "只在当前分类的推荐帖子列表中显示帖子。" - clean_up_uploads: "移除孤立的已上传资料。警告:您可能想要在启用这个设定前备份一下 /uploads 目录。" + clean_up_uploads: "移除孤立的已上传资料。警告:你可能想要在启用这个设定前备份一下 /uploads 目录。" clean_orphan_uploads_grace_period_hours: "删除孤立上传资料的宽限期(单位:小时)" - purge_deleted_uploads_grace_period_days: "移除已删除的孤立上传资料的宽限期(单位:小时)" - purge_inactive_users_grace_period_days: "删除不活跃用户账户的宽限期(单位:天)。" + purge_deleted_uploads_grace_period_days: "彻底删除孤立上传资料的宽限期(单位:天)" + purge_unactivated_users_grace_period_days: "删除未激活用户账户的宽限期(单位:天)。" enable_s3_uploads: "上传至 Amazon S3 存储的地址。重要:需要有效的 S3 验证资料(包括 access key id & secret access key)。" s3_use_iam_profile: '使用 AWS EC2 IAM 角色来获得 key。注意:启用这个会覆盖“S3 access key id”和“S3 secret access key” 设置。' s3_upload_bucket: "上传文件保存于 Amazon S3 的 bucket 名字。警告:必须为小写,无句点,无下划线。" @@ -757,7 +824,7 @@ zh_CN: newuser_max_attachments: "一个新用户可以添加到一个帖子里的附件数量。" newuser_max_mentions_per_post: "一个访问者可以在一个帖子里使用 @name 提及的最大数量。" newuser_max_replies_per_topic: "直至有人回复他们前,新用户在一个帖子里的最大回复数量。" - max_mentions_per_post: "您可以在一个帖子里使用 @name 提及的最大数量。" + max_mentions_per_post: "你可以在一个帖子里使用 @name 提及的最大数量。" create_thumbnails: "为太大而无法恰当地显示在帖子里的图片创建 lightbox 缩略图。" email_time_window_mins: "等待多少(n)分钟才给用户发送通知电子邮件,好让他们有机会自己来编辑和完善他们的帖子。" email_posts_context: "在通知邮件中包含的作为上下文的回复数量。" @@ -769,6 +836,7 @@ zh_CN: min_title_similar_length: "开始检查相似主题的帖子标题的最小长度。" min_body_similar_length: "开始检查相似主题的帖子内容的最小长度。" category_colors: "设置分类颜色的十六进制色彩值列表。" + category_style: "分类图标的视觉样式。" max_image_size_kb: "允许用户上传的最大文件大小(以kB为单位)。确保也在nginx(client_max_body_size),apache 或代理中进行限制文件大小的配置。" max_attachment_size_kb: "允许用户上传的最大文件大小(单位:KB)——同时在 nginx(client_max_body_size),apache 或代理中配置他们。" authorized_extensions: "允许上传文件的扩展名列表('*' 表示允许所有文件类型)" @@ -786,19 +854,24 @@ zh_CN: topic_post_like_heat_low: "在赞与帖子的比例超过此比例后,帖子数量一栏将稍稍高亮。" topic_post_like_heat_medium: "在赞与帖子的比例超过此比例后,帖子数量一栏将明显高亮。" topic_post_like_heat_high: "在赞与帖子的比例超过此比例后,帖子数量一栏将强烈高亮。" - faq_url: "如果您的 FAQ 文档在外部,那么请在此填写其完整 URL 地址。" - tos_url: "如果您的服务条款文档在外部,那么请在此填写其完整 URL 地址。" - privacy_policy_url: "如果您的隐私政策文档在外部,那么请在此填写其完整 URL 地址。" + faq_url: "如果你的 FAQ 文档在外部,那么请在此填写其完整 URL 地址。" + tos_url: "如果你的服务条款文档在外部,那么请在此填写其完整 URL 地址。" + privacy_policy_url: "如果你的隐私政策文档在外部,那么请在此填写其完整 URL 地址。" newuser_spam_host_threshold: "用户在一篇帖子中能添加多少此指向同一主机的链接,取决于之前该用户有`newuser_spam_host_threshold`篇帖子被认为是垃圾帖。" white_listed_spam_host_domains: "广告主机白名单域名列表。新用户可以任意链接至这些域名。" staff_like_weight: "职员赞时的额外权重。" levenshtein_distance_spammer_emails: "当匹配广告邮件时,模糊匹配判断差异的字符数。" + max_new_accounts_per_registration_ip: "如果已经有了从这个 IP 创建的(n)个信任等级0的账户(并且没有一个是职员或者是信任等级2以上的用户),不再允许来自该 IP 地址的注册请求。" + min_ban_entries_for_roll_up: "当点击折叠按钮时,且至少 (N) 条记录时,将会创建一个子网封禁记录" + max_age_unmatched_emails: "在 (N) 天后删除不匹配的邮件地址。" + max_age_unmatched_ips: "在 (N) 天后删除不匹配的 IP 记录。" + num_flaggers_to_close_topic: "要自动终止一个主题的讨论并介入时所需的最小数量的独立标记者" + num_flags_to_close_topic: "要自动终止一个主题的讨论并介入时所需的最小数量的有效标记" reply_by_email_enabled: "启用通过邮件回复。" reply_by_email_address: "通过邮件回复的回复地址模板,例如:%{reply_key}@reply.example.com 或 replies+%{reply_key}@example.com" disable_emails: "禁止 Discourse 发送任何邮件" strip_images_from_short_emails: "从邮件中除去小于 2800 比特的图片" short_email_length: "短邮件地址长度(以比特作为单位)" - enable_email_names: "允许在邮件中显示用户全名。禁用将在邮件中隐藏全名。" pop3_polling_enabled: "轮询 POP3 收取邮件回复。" pop3_polling_ssl: "连接至 POP3 服务器时使用 SSL。(推荐)" pop3_polling_period_mins: "查询用于邮件的 POP3 账户的间隔(以分钟计)。注意:需要重新启动。" @@ -824,21 +897,23 @@ zh_CN: digest_topics: "邮件摘要中显示的最大主题数目。" digest_min_excerpt_length: "在邮件摘要中每个帖子最少显示的字符数量。" default_digest_email_frequency: "用户收到摘要邮件的默认间隔。用户可以在参数设置中更改这个设置。" + suppress_digest_email_after_days: "不发送摘要邮件给超过 (n) 天未出现的用户。" + disable_digest_emails: "为所有用户禁用摘要邮件。" default_external_links_in_new_tab: "在新标签页中打开外部链接。用户可以在参数设置中更改这个设置。" detect_custom_avatars: "是否检测用户上传了自定义头像。" max_daily_gravatar_crawls: "一天内 Discourse 将自动检查 gravatar 自定义头像的次数" public_user_custom_fields: "可公开显示的用户自定义属性白名单" allow_profile_backgrounds: "允许用户上传个人资料背景图片。" sequential_replies_threshold: "用户可以在一个主题内多次回复而不被提醒连续发送多个回复的通知。" - enable_mobile_theme: "为移动设备启用移动友好的主题,但也能切换回完整站点。如果您想要使用自定义的响应式主题请禁用它。" + enable_mobile_theme: "为移动设备启用移动友好的主题,但也能切换回完整站点。如果你想要使用自定义的响应式主题请禁用它。" dominating_topic_minimum_percent: "用户在主题中的帖子占到多少百分比时使得用户主导话题。" suppress_uncategorized_badge: "不要为主题列表中的未分类主题显示徽章。" global_notice: "为所有访客显示“紧急的”全局横幅,留空隐藏它(可以使用 HTML)" disable_edit_notifications: "当 'download_remote_images_to_local' 启用时禁用系统编辑提醒。" - enable_names: "允许用户显示全名。禁止隐藏全名。" + enable_names: "在用户的个人信息、用户卡片和邮件中显示全名。禁用将在所有地方隐藏全名。" display_name_on_posts: "在用户的帖子中显示他们的全名以及他们的 @username。" invites_per_page: "默认在用户页显示邀请。" - short_progress_text_threshold: "在主题中的帖子数超过这个设定后,进度条将只显示当前的帖子数。如果您更改了进度条的宽度,您需要修改这个值。" + short_progress_text_threshold: "在主题中的帖子数超过这个设定后,进度条将只显示当前的帖子数。如果你更改了进度条的宽度,你需要修改这个值。" default_code_lang: "默认语法高亮语言,使用 GitHub 代码块(lang-auto、ruby、python或者其他等等。)" warn_reviving_old_topic_age: "当有人开始回复最后一贴超过一定天数前的主题时,将有一个警告显示,不鼓励他们复活一个老的讨论。将其设置为 0 以禁用。" autohighlight_all_code: "即使未显式设定语言,仍为所有预编排代码块应用语法高亮。" @@ -857,6 +932,8 @@ zh_CN: show_create_topics_notice: "如果站点只有少于 5 篇的公开帖子时,显示一条请管理员创建帖子的提示。" vacuum_db_days: "在数据库迁移后使用完整扫描回收数据库空间(设置 0 为禁用)" prevent_anons_from_downloading_files: "禁止匿名用户下载附件。警告:这将禁止他们访问任何发表在帖子中的非图片资源。" + enable_emoji: "启用绘文字(emoji)" + emoji_set: "您喜欢哪一种 emoji?" errors: invalid_email: "电子邮箱地址无效。" invalid_username: "没有这个用户名的用户。" @@ -869,32 +946,38 @@ zh_CN: invalid_string_min_max: "必须在 %{min} 和 %{max} 字符内。" invalid_string_min: "必须超过 %{min} 个字符。" invalid_string_max: "必须不超过 %{max} 个字符。" + invalid_reply_by_email_address: "值必须包含 '%{reply_key' 并且要与通知邮件不同。" notification_types: - mentioned: "%{display_username} 在 %{link} @ 了您" - liked: "%{display_username} 在 %{link} 赞了您的帖子 " - replied: "%{display_username} 在 %{link} 回复了您的帖子" - quoted: "%{display_username} 在 %{link} 引用了您的帖子" - edited: "%{display_username} 在 %{link} 编辑了您的帖子" + mentioned: "%{display_username} 在 %{link} @ 了你" + liked: "%{display_username} 在 %{link} 赞了你的帖子 " + replied: "%{display_username} 在 %{link} 回复了你的帖子" + quoted: "%{display_username} 在 %{link} 引用了你的帖子" + edited: "%{display_username} 在 %{link} 编辑了你的帖子" posted: "%{display_username} 发表了 %{link}" - moved_post: "%{display_username} 把您的帖子移动到了 %{link}" - private_message: "%{display_username} 发送给您一条私信:%{link}" - invited_to_private_message: "%{display_username} 邀请您进行私信:%{link}" - invitee_accepted: "%{display_username} 接受了您的邀请" - linked: "%{display_username} 在 %{link} 里链接了您" - granted_badge: "您获得了 %{link}" + moved_post: "%{display_username} 把你的帖子移动到了 %{link}" + private_message: "%{display_username} 发送给你一条私信:%{link}" + invited_to_private_message: "%{display_username} 邀请你进行私信:%{link}" + invitee_accepted: "%{display_username} 接受了你的邀请" + linked: "%{display_username} 在 %{link} 里链接了你" + granted_badge: "你获得了 %{link}" search: within_post: "#{{post_number}} 由 {{username}}" types: category: '分类' topic: '结果' user: '用户' + sso: + not_found: "无法找到或创建账户,联系站点管理员" + account_not_approved: "账户正在等待验证,完成后你将收到一封邮件提醒" + unknown_error: "更新信息时错误,联系站点管理员" + timeout_expired: "账户登录超时,请重试登录" original_poster: "原始作者" most_posts: "大部分帖子" most_recent_poster: "当前大部分帖子作者" frequent_poster: "频繁发帖者" redirected_to_top_reasons: new_user: "欢迎来到我们的社区!这是我们最近的热门主题。" - not_seen_in_a_month: "欢迎回来!我们已经好久没见到您了。这些是您不在时的最热门主题。" + not_seen_in_a_month: "欢迎回来!我们已经好久没见到你了。这些是你不在时的最热门主题。" move_posts: new_topic_moderator_post: other: "我已经移动了 %{conut} 个帖子到新的主题:%{topic_link}" @@ -902,6 +985,10 @@ zh_CN: other: "我已经移动了 %{conut} 个帖子到了一个已存在的主题:%{topic_link}" change_owner: post_revision_text: "所有权从 %{old_user} 转移至 %{new_user}" + emoji: + errors: + name_already_exists: "抱歉,名字 '%{name}' 已经被另一个 emoji 使用。" + error_while_storing_emoji: "抱歉,存储 emoji 时发生错误。" topic_statuses: archived_enabled: "本主题已归档,即已经冻结,无法修改。" archived_disabled: "本主题已被解除归档,即不再冻结,可以修改。" @@ -928,23 +1015,23 @@ zh_CN: visible_enabled: "本主题已设置为显示在主题列表中。" visible_disabled: "本主题设置为不显示在主题列表中。只能通过直达链接来访问。" login: - not_approved: "您的帐号尚未获得批准。一旦您的帐号获得批准,您会收到一封电子邮件。" + not_approved: "你的帐号尚未获得批准。一旦你的帐号获得批准,你会收到一封电子邮件。" incorrect_username_email_or_password: "用户名、电子邮箱或密码不正确" - wait_approval: "谢谢注册帐号。我们会在您的帐号获得批准之后通知您。" - active: "您的帐号已经被激活,可以使用了。" - activate_email: "

快完成了!我们发送了一封激活邮件到%{email}。请按照邮件中的步骤来激活您的帐号。

如果您没有收到邮件,请检查您的垃圾邮件收件箱,或者试试再登录一次,看看能不能收到另一封激活邮件。

" - not_activated: "您还不能登录。我们发送了一封激活邮件给您,请按照邮件中的步骤来激活您的帐号。" - not_allowed_from_ip_address: "您不能以 %{username} 身份从该 IP 地址登陆。" - suspended: "您要等到 %{date} 之后才能登录。" - suspended_with_reason: "您要等到 %{date} 之后才能登录。您被封禁的理由是 %{reason}" + wait_approval: "谢谢注册帐号。我们会在你的帐号获得批准之后通知你。" + active: "你的帐号已经被激活,可以使用了。" + activate_email: "

快完成了!我们发送了一封激活邮件到%{email}。请按照邮件中的步骤来激活你的帐号。

如果你没有收到邮件,请检查你的垃圾邮件收件箱,或者试试再登录一次,看看能不能收到另一封激活邮件。

" + not_activated: "你还不能登录。我们发送了一封激活邮件给你,请按照邮件中的步骤来激活你的帐号。" + not_allowed_from_ip_address: "你不能以 %{username} 身份从该 IP 地址登录。" + suspended: "你要等到 %{date} 之后才能登录。" + suspended_with_reason: "你要等到 %{date} 之后才能登录。你被封禁的理由是 %{reason}" errors: "%{errors}" not_available: "不可用,试试 %{suggestion}?" something_already_taken: "出了一些问题,可能此用户名或电子邮箱已经被注册。试试 忘记密码链接吧。" - omniauth_error: "抱歉,在给您的 %{strategy} 帐号授权时发生了错误。有可能您没有批准授权申请?" - omniauth_error_unknown: "在处理您的登录过程中发生了错误,请重试。" + omniauth_error: "抱歉,在验证你的帐号时发生了错误。可能你没有批准授权申请?" + omniauth_error_unknown: "在处理你的登录过程中发生了错误,请重试。" new_registrations_disabled: "现在不允许注册新账户。" password_too_long: "密码不能超过 200 个字符。" - missing_user_field: "您还没有填写完所有用户字段" + missing_user_field: "你还没有填写完所有用户字段" close_window: "验证已经完成。关闭窗口以继续。" user: no_accounts_associated: "无关联账户" @@ -961,9 +1048,9 @@ zh_CN: ip_address: blocked: "被封禁。" invite_mailer: - subject_template: "%{invitee_name} 邀请您参与 %{site_domain_name} 主题 '%{topic_title}' " + subject_template: "%{invitee_name} 邀请你参与 %{site_domain_name} 主题 '%{topic_title}' " text_body_template: | - %{invitee_name} 邀请您参与讨论 + %{invitee_name} 邀请你参与讨论 > **%{topic_title}** > @@ -973,29 +1060,29 @@ zh_CN: > %{site_title} -- %{site_description} - 如果您有兴趣,点击下面的链接: + 如果你有兴趣,点击下面的链接: %{invite_link} - 这封邀请来组于受信任的用户,所以您可以立即回复。 + 这封邀请来组于受信任的用户,所以你可以立即回复。 invite_forum_mailer: - subject_template: "%{invitee_name} 邀请您加入 %{site_domain_name}" + subject_template: "%{invitee_name} 邀请你加入 %{site_domain_name}" text_body_template: | - %{invitee_name} 邀请您加入 + %{invitee_name} 邀请你加入 > **%{site_title}** > > %{site_description} - 如果您感兴趣,请点击下面的链接加入: + 如果你感兴趣,请点击下面的链接加入: %{invite_link} - 您是被一个受信任的用户邀请的,所以您可以不用登录。 + 你是被一个受信任的用户邀请的,所以你可以不用登录。 invite_password_instructions: subject_template: "为 %{site_name} 账户设置密码" text_body_template: | - 感谢您接受来自 %{site_name} 的邀请——欢迎! + 感谢你接受来自 %{site_name} 的邀请——欢迎! 要再次登录,点击下面的链接设置密码: %{base_url}/users/password-reset/%{email_token} @@ -1020,7 +1107,7 @@ zh_CN: - 如果您自己运行邮件服务器,检查确认您发送电子邮件的服务器IP [不在任何邮件黑名单中][4]。验证您的 DNS 记录的 HELO 消息中*确定无疑地*发送了符合要求的主机名。如果没有,那么会导致很多邮件服务商拒绝您的邮件。 - (最简单的方法是在[Mandrill][md]或[Mailgun][mg]或[Mailjet][mj]注册免费账户,他们的免费账户足够小社群使用。如果您希望在中国大陆提供稳定的服务,您可以考虑[SendCloud][sc]不过您仍然需要在 DNS 设置中设定 SPF 和 DKIM 记录!) + (最简单的方法是在[Mandrill][md]或[Mailgun][mg]或[Mailjet][mj]注册免费账户,他们的免费账户对一个小社群是足够的。如果您希望在中国大陆提供稳定的服务,您可以考虑[SendCloud][sc]不过您仍然需要在 DNS 设置中设定 SPF 和 DKIM 记录!) 我们衷心希望您收到这封邮件,成功完成邮件发送测试! @@ -1033,26 +1120,21 @@ zh_CN: [2]: http://mxtoolbox.com/ReverseLookup.aspx [3]: http://www.dkim.org/ [4]: http://whatismyipaddress.com/blacklist-check - [5]: %{base_url}/unsubscribe [7]: http://dkimcore.org/tools/dkimrecordcheck.html [8]: http://www.openspf.org/SPF_Record_Syntax [md]: http://mandrill.com [mg]: http://www.mailgun.com/ [mj]: http://www.mailjet.com/pricing [sc]: http://sendcloud.sohu.com/ - - ---- - - 在每封您发送的邮件底部应该都有取消订阅的链接,让我们模拟一下。这封邮件发送自 中华人民共和国北京市海淀区中关村南大街公司名称,邮编 100081。如果您不希望将来继续收到我们的邮件,[点击此处取消订阅][5]。 new_version_mailer: subject_template: "[%{site_name}] 有新的 Discourse 版本,可供升级" text_body_template: | 有新版本的 Discourse 可供升级。 - 您的版本:%{installed_version} + 你的版本:%{installed_version} 新版本:**%{new_version}** - 您可能想要: + 你可能想要: - 查看 [GitHub 改动日志](https://github.com/discourse/discourse/commits/master) 中的更新内容。 @@ -1064,10 +1146,10 @@ zh_CN: text_body_template: | 有新版本的 [Discourse](http://www.discourse.org) 可供升级。 - 您的版本:%{installed_version} + 你的版本:%{installed_version} 新版本:**%{new_version}** - 您可能想要: + 你可能想要: - 查看[GitHub 改动日志](https://github.com/discourse/discourse/commits/master)中的更新内容。 @@ -1083,35 +1165,36 @@ zh_CN: other: "这些标记在过去 %{count} 内被提交。" please_review: "请核查他们。" post_number: "帖子" - how_to_disable: '您可以通过修改“notify about flags after”设置项以禁用或改变邮件提醒设置。' + how_to_disable: '你可以通过修改“notify about flags after”设置项以禁用或改变邮件提醒设置。' subject_template: other: "%{count} 个标记需要被处理" flag_reasons: - off_topic: "您的帖子被标记为**偏离主题**:社群认为它不适合处于这个主题中,主题应与标题和第一个帖子有关。" - inappropriate: "您的帖子被标记为**不恰当**:社群认为它有冒犯、侮辱或者违反了[社群准则](/guidelines)。" - spam: "您的帖子被标记为**广告**:社群认为它为广告,不包含任何对当前讨论有帮助的内容,只有促销信息。" - notify_moderators: "您的帖子被标记为 **需要管理员关注**:社群认为帖子需要版主的干预。" + off_topic: "您的帖子被标记为 **偏离主题**:鉴于当前的主题标题和第一个帖子,社群成员们感觉它不适合处于这个主题中。" + inappropriate: "您的帖子被标记为 **不恰当**:社群成员感觉它有冒犯或者侮辱的意味,亦或是它违反了[社群准则](/guidelines)。" + spam: "您的帖子被标记为 **广告**:社群成员觉得它是广告,像是在过度地推广着什么,而不是预期中与主题有关的内容。" + notify_moderators: "您的帖子被标记为 **需要版主关注**:社群成员感觉帖子需要职员的人工干预。" flags_dispositions: agreed: "感谢通知我们。我们认为这是一个问题,并且我们正在了解情况。" agreed_and_deleted: "感谢通知我们。我们认为这是一个问题,并且我们已经删除了帖子。" disagreed: "感谢通知我们。我们正在了解情况。" deferred: "感谢通知我们。我们正在调查情况。" deferred_and_deleted: "感谢通知我们。我们已经删除了帖子。" + temporarily_closed_due_to_flags: "帖子因为多次社群标记暂时关闭" system_messages: post_hidden: - subject_template: "%{site_name} 提示:由于论坛用户标记,系统隐藏了您的帖子" + subject_template: "%{site_name} 提示:由于论坛用户标记,系统隐藏了你的帖子" text_body_template: | - 您好, + 你好, - 这是一封从 %{site_name} 自动发出的邮件,通知您的帖子已被隐藏。 + 这是一封从 %{site_name} 自动发出的邮件,通知你的帖子已被隐藏。 %{base_url}%{url} %{flag_reason} - 您的帖子是因为被多个社群成员标记才被隐藏的,所以请根据他们的反馈修改您的帖子。**您可以在 %{edit_delay} 分钟后开始编辑您的帖子,之后它会被自动显示。**这将提高您在论坛的信任等级。 + 你的帖子是因为被多个社群成员标记才被隐藏的,所以请根据他们的反馈修改你的帖子。**你可以在 %{edit_delay} 分钟后开始编辑你的帖子,之后它会被自动显示。**这将提高你在论坛的信任等级。 - 但是,如果帖子再次被社群标记并隐藏,它将被隐藏直至版主处理——并且可能导致进一步的措施,包括禁止您的用户帐号的可能。 + 但是,如果帖子再次被社群标记并隐藏,它将被隐藏直至版主处理——并且可能导致进一步的措施,包括禁止你的用户帐号的可能。 需要了解更多,请查看我们的[社群指引](%{base_url}/guidelines)。 usage_tips: @@ -1134,8 +1217,6 @@ zh_CN: - - ## 我怎么回复? - 要回复整个主题,使用页面最底下的回复按钮 。 @@ -1166,7 +1247,6 @@ zh_CN: 您可以分享一个帖子的链接,或者是收藏一个帖子以在用户也中引用他们。 - ## 谁在跟我交谈? 当有人回复您的帖子,引用您的帖子或者使用`@用户名`提及到您的时候,一个数字将会立即出现在页面右上角。使用它查看**通知**。 @@ -1183,7 +1263,7 @@ zh_CN: - 您可以通过帖子下方的控制选项改变每一个主题的通知状态(也可以设置每一个分类的状态)。要改变您跟踪帖子的方式,或新的定义,详见[您的用户设置](/my/preferences)。 + 您可以通过帖子下方的控制选项改变每一个主题的通知状态(也可以设置每一个分类的状态)。要改变您跟踪帖子的方式,或新的定义,详见[您的用户设置](%{base_url}/my/preferences)。 ## 为什么我不能做某些事? @@ -1191,19 +1271,21 @@ zh_CN: welcome_user: subject_template: "欢迎来到 %{site_name}!" text_body_template: | - 感谢加入 %{site_name},并且欢迎! + 感谢驻足%{site_name},欢迎! %{new_user_tips} - 我们始终相信[文明的社会行为](%{base_url}/guidelines)。 + 我们始终相信[讨论应该文明](%{base_url}/guidelines)。 好好享受你在论坛的时光吧! + + (如果您在新用户级别需要和[职员](%{base_url}/about)私下沟通的话,直接回复这封私信。) welcome_invite: subject_template: "欢迎来到 %{site_name}!" - text_body_template: "感谢您接受邀请加入 %{site_name} —— 欢迎!\n\n我们为您自动生成了一个用户名:**%{username}**,但是您在任何时候都可以访问[您的用户设置][prefs]来修改它。\n\n要再次登入,或者:\n\n1. 使用您喜欢的方式登入——但是必须以**同样的邮件地址**,即您收到邀请的邮箱地址登入。否则我们就无法分辨是不是您本人!\n\n2. 在 %{site_name} 的[用户设置][prefs]页面创建一个密码,然后使用该密码来登入。 \n\n%{new_user_tips}\n\n我们始终相信[讨论应该文明](%{base_url}/guidelines)。\n\n好好享受您在论坛的时光吧!\n\n[prefs]: %{user_preferences_url}\n" + text_body_template: "感谢你接受邀请加入%{site_name} —— 欢迎!\n\n我们为你创建了账号:**%{username}**,同时你已经登录了。你可以在任何时候访问[你的用户设置][prefs]来修改它。\n\n要再次登入,或者:\n\n1. 永远 **使用收到邀请的邮箱地址**登录,。否则我们就无法分辨是不是你本人!\n\n2. 在 %{site_name} 的[用户设置][prefs]页面创建一个密码,然后使用该密码来登入。 \n\n%{new_user_tips}\n\n我们始终相信[讨论应该文明](%{base_url}/guidelines)。\n\n好好享受您在论坛的时光吧!\n\n(如果您在新用户级别需要和[职员](%{base_url}/about)私下沟通的话,直接回复这封私信。)\n\n[prefs]: %{user_preferences_url}\n" backup_succeeded: subject_template: "备份成功完成" - text_body_template: "备份成功。\n访问[管理 > 备份](/admin/backups)下载您的新备份文件。" + text_body_template: "备份成功。\n访问[管理 > 备份](/admin/backups)下载你的新备份文件。" backup_failed: subject_template: "备份失败" text_body_template: | @@ -1229,7 +1311,7 @@ zh_CN: ``` bulk_invite_succeeded: subject_template: "批量用户邀请成功完成" - text_body_template: "您的批量邀请用户已经完成,发送了 %{sent} 个邀请。" + text_body_template: "你的批量邀请用户已经完成,发送了 %{sent} 个邀请。" bulk_invite_failed: subject_template: "批量邀请完成,并有一些错误" text_body_template: | @@ -1241,105 +1323,105 @@ zh_CN: %{logs} ``` csv_export_succeeded: - subject_template: "数据成功导出" + subject_template: "数据导出完成" text_body_template: | - 数据成功导出。 + 数据成功导出!:dvd: - 下载 CSV 文件:%{file_name} + %{file_name}(%{file_size}) - CSV 文件下载链接将在 48 小时后失效。 + 以上的下载链接将在 48 小时后失效。 csv_export_failed: - subject_template: "导出失败" - text_body_template: "导出失败。请检查日志。" + subject_template: "数据导出失败" + text_body_template: "我们很抱歉,但是你的数据导出请求失败了。请检查日志或联系一位职员。" email_reject_trust_level: subject_template: "电子邮件错误 -- 等级不足" text_body_template: | - 我们很抱歉,但是您发送至 %{destination}(名为 %{former_title})的邮件无法发送。 + 我们很抱歉,但是你发送至 %{destination}(名为 %{former_title})的邮件无法发送。 - 您的账户没有足够的信任等级向刚邮件地址发布新主题。如果您坚信这是一个错误,联系一个职员。 + 你的账户没有足够的信任等级向刚邮件地址发布新主题。如果你坚信这是一个错误,联系一个职员。 email_reject_no_account: subject_template: "电子邮件错误 -- 未知账户" text_body_template: | - 我们很抱歉,但是您发送至 %{destination}(名为%{former_title}) 的邮件无法发送。 + 我们很抱歉,但是你发送至 %{destination}(名为%{former_title}) 的邮件无法发送。 该邮件没有与已知的账户关联。试试从不同的邮件地址发送或联系一个职员。 email_reject_empty: subject_template: "电子邮件错误 -- 空内容" text_body_template: | - 我们很抱歉,但是您发送至 %{destination}(名为 %{former_title})的邮件无法发送。 + 我们很抱歉,但是你发送至 %{destination}(名为 %{former_title})的邮件无法发送。 - 我们没有在邮件中找到任何内容。请确定您的回复位于邮件的最上方 —— 我们不能处理行内回复。 + 我们没有在邮件中找到任何内容。请确定你的回复位于邮件的最上方 —— 我们不能处理行内回复。 - 如果您确实包含了内容但还收到了这个通知,试试包含 HTML 内容(而非纯文本)。 + 如果你确实包含了内容但还收到了这个通知,试试包含 HTML 内容(而非纯文本)。 email_reject_parsing: subject_template: "电子邮件错误 -- 内容无法识别" text_body_template: | - 我们很抱歉,但是您发送至 %{destination}(名为 %{former_title})的邮件无法发送。 + 我们很抱歉,但是你发送至 %{destination}(名为 %{former_title})的邮件无法发送。 - 我们不能找到您邮件中的回复。**请确认将您的回复置于邮件顶端**——我们不能处理行内回复。 + 我们不能找到你邮件中的回复。**请确认将你的回复置于邮件顶端**——我们不能处理行内回复。 email_reject_post_error: subject_template: "电子邮件错误 -- 发送出错" text_body_template: | - 我们很抱歉,但是您发送至 %{destination}(名为%{former_title}) 的邮件无法发送。 + 我们很抱歉,但是你发送至 %{destination}(名为%{former_title}) 的邮件无法发送。 一些可能的原因是:复杂的格式、消息超长、消息太短。请再试一次,若还不行,使用网站发表。 email_reject_post_error_specified: subject_template: "邮件问题——发表失败" text_body_template: | - 我们很抱歉,但是您发送至 %{destination}(名为%{former_title}) 的邮件消息无法发送。 + 我们很抱歉,但是你发送至 %{destination}(名为%{former_title}) 的邮件消息无法发送。 援引: %{post_error} - 如果您能解决错误,请再试一次。 + 如果你能解决错误,请再试一次。 email_reject_reply_key: subject_template: "电子邮件错误 -- 未知回复指纹" text_body_template: | - 我们很抱歉,但是您发送至 %{destination}(名为%{former_title}) 的邮件无法发送。 + 我们很抱歉,但是你发送至 %{destination}(名为%{former_title}) 的邮件无法发送。 提供的回复 key 是无效或者未知的,所以我们不知道邮件是回复给谁的。联系一个职员。 email_reject_destination: subject_template: "电子邮件错误 -- 未知的回复至地址" text_body_template: | - 我们很抱歉,但是您发送至 %{destination}(名为 %{former_title})的邮件消息无法发送。 + 我们很抱歉,但是你发送至 %{destination}(名为 %{former_title})的邮件消息无法发送。 - 在论坛中找不到该地址。请确认在您的发送到(而非抄送或密送)中填写的是站点的地址,并且您正在将邮件发送至论坛职员提供的邮件地址。 + 在论坛中找不到该地址。请确认在你的发送到(而非抄送或密送)中填写的是站点的地址,并且你正在将邮件发送至论坛职员提供的邮件地址。 email_reject_topic_not_found: subject_template: "邮件问题——主题未找到" text_body_template: |+ - 十分抱歉,无法将您的信息发表到 %{destination} (titled %{former_title}) 。 + 十分抱歉,无法将你的信息发表到 %{destination} (titled %{former_title}) 。 - 我们没有找到该主题,可能其已被删除,如果您确认有问题,请与我们的工作人员联系。 + 我们没有找到该主题,可能其已被删除,如果你确认有问题,请与我们的工作人员联系。 email_reject_topic_closed: subject_template: "邮件问题 —— 主题已关闭" text_body_template: | - 我们很抱歉,但是您发送至 %{destination}(名为 %{former_title})的邮件无法发送。 + 我们很抱歉,但是你发送至 %{destination}(名为 %{former_title})的邮件无法发送。 - 主题已经关闭。如果您相信这其中有错误,联系一个职员成员。 + 主题已经关闭。如果你相信这其中有错误,联系一个职员成员。 email_error_notification: subject_template: "电子邮件错误 -- POP 验证错误" text_body_template: | 在从 POP 服务器查询邮件时遇到了一个验证错误。 - 请确认您在[站点设置](%{base_url}/admin/site_settings/category/email)中已经正确配置了 POP 验证信息。 + 请确认你在[站点设置](%{base_url}/admin/site_settings/category/email)中已经正确配置了 POP 验证信息。 too_many_spam_flags: subject_template: "新账号被封禁" text_body_template: | - 您好, + 你好, - 这是一封自 %{site_name} 自动发出的邮件,以告知您的帖子已因被社群多次标记而被自动隐藏。 + 这是一封自 %{site_name} 自动发出的邮件,以告知你的帖子已因被社群多次标记而被自动隐藏。 - 处于谨慎的考虑,您的新账户被禁止创建新回复或主题。除非一个职员能复核您的账户。 + 处于谨慎的考虑,你的新账户被禁止创建新回复或主题。除非一个职员能复核你的账户。 欲查看额外的指导,请查看我们的[社群指引](%{base_url}/guidelines)。 blocked_by_staff: subject_template: "账户被封禁" text_body_template: | - 您好, + 你好, - 这是一封自 %{site_name} 自动发出的邮件,以告知您的账户被职员封禁。 + 这是一封自 %{site_name} 自动发出的邮件,以告知你的账户被职员封禁。 欲查看额外的指导,请查看我们的[社群指引](%{base_url}/guidelines)。 user_automatically_blocked: @@ -1365,11 +1447,11 @@ zh_CN: unblocked: subject_template: "账户解封 unblocked" text_body_template: | - 您好, + 你好, - 这是一封自 %{site_name} 自动发出的邮件,以告知您的账户已经在职员审核后被解封。 + 这是一封自 %{site_name} 自动发出的邮件,以告知你的账户已经在职员审核后被解封。 - 您现在又可以创建新的回复和主题了。 + 你现在又可以创建新的回复和主题了。 pending_users_reminder: subject_template: other: "%{count} 个用户等待批准" @@ -1380,7 +1462,7 @@ zh_CN: download_remote_images_disabled: subject_template: "远程图片下载已禁用。" text_body_template: "`download_remote_images_to_local` 设定已被禁用,因为已经达到 ``download_remote_images_threshold` 设定中的磁盘空间限制。" - unsubscribe_link: "如果您希望取消这些邮件的订阅,请访问您的[用户设置](%{user_preferences_url})。" + unsubscribe_link: "如果你希望取消这些邮件的订阅,请访问你的[用户设置](%{user_preferences_url})。" subject_re: "回复:" subject_pm: "[私信]" user_notifications: @@ -1392,9 +1474,9 @@ zh_CN: visit_link_to_respond: "在浏览器中访问 %{base_url}%{url} 回复。" posted_by: "%{username}发表于%{post_date}" user_invited_to_private_message_pm: - subject_template: "[%{site_name}] %{username} 邀请您私下交流:'%{topic_title}'" + subject_template: "[%{site_name}] %{username} 邀请你私下交流:'%{topic_title}'" text_body_template: | - %{username} 邀请您在 %{site_name} 就 '%{topic_title}' 进行私下交流: + %{username} 邀请你在 %{site_name} 就 '%{topic_title}' 进行私下交流: 请访问 %{base_url}%{url} 来查看该主题。 user_replied: @@ -1443,11 +1525,12 @@ zh_CN: --- %{respond_instructions} digest: + why: "在你上一次于 %{last_seen_at} 访问后,在 %{site_link} 上的简洁的摘要。" subject_template: "[%{site_name}] %{date} 的摘要" - new_activity: "在您的主题和帖子里的动态:" + new_activity: "在你的主题和帖子里的动态:" top_topics: "热门帖子" other_new_topics: "热门主题" - unsubscribe: "这封来自 %{site_link} 的摘要邮件是您一段时间没有访问后发送给您的。点击 %{unsubscribe_link} 取消订阅。" + unsubscribe: "这封来自 %{site_link} 的摘要邮件是你一段时间没有访问后发送给你的。点击 %{unsubscribe_link} 取消订阅。" click_here: "点击此处" from: "%{site_name} 摘要" read_more: "阅读更多" @@ -1458,40 +1541,40 @@ zh_CN: forgot_password: subject_template: "[%{site_name}] 密码重置" text_body_template: | - 有人请求重置您在 [%{site_name}](%{base_url}) 上的密码。 + 有人请求重置你在 [%{site_name}](%{base_url}) 上的密码。 - 如果那不是您,您可以直接忽略本邮件。 + 如果那不是你,你可以直接忽略本邮件。 点击下面的链接来选择一个新密码: %{base_url}/users/password-reset/%{email_token} set_password: subject_template: "[%{site_name}] 设置密码" text_body_template: | - 有人请求添加您在 [%{site_name}](%{base_url}) 的密码。除此之外,您可以通过已验证邮件地址的在线服务商(Google、Facebook等等)登录。 + 有人请求添加你在 [%{site_name}](%{base_url}) 的密码。除此之外,你可以通过已验证邮件地址的在线服务商(Google、Facebook等等)登录。 - 如果不是您请求添加密码,您可以直接忽略本邮件。 + 如果不是你请求添加密码,你可以直接忽略本邮件。 点击下面的链接来选择一个新密码: %{base_url}/users/password-reset/%{email_token} account_created: - subject_template: "[%{site_name}] 您的新帐号" + subject_template: "[%{site_name}] 你的新帐号" text_body_template: | - 您在 %{site_name} 上建立了一个新账号 + 你在 %{site_name} 上建立了一个新账号 点击下面的链接来为新账户设置密码: %{base_url}/users/password-reset/%{email_token} authorize_email: - subject_template: "[%{site_name}] 确认您的新电子邮箱地址" + subject_template: "[%{site_name}] 确认你的新电子邮箱地址" text_body_template: | - 点击下面的链接来确认您在 %{site_name} 上的新电子邮箱地址: + 点击下面的链接来确认你在 %{site_name} 上的新电子邮箱地址: %{base_url}/users/authorize-email/%{email_token} signup_after_approval: - subject_template: "您已经被 %{site_name} 论坛批准加入了!" + subject_template: "你已经被 %{site_name} 论坛批准加入了!" text_body_template: | - 欢迎加入 %{site_name}! + 欢迎加入%{site_name}! - 一个职员批准了您在 %{site_name} 的账户。 + 一个职员批准了您在%{site_name}的账户。 点击下面的链接来确认并激活您在 %{site_name} 上的新账号: %{base_url}/users/activate-account/%{email_token} @@ -1503,17 +1586,19 @@ zh_CN: 我们始终相信[讨论应该文明](%{base_url}/guidelines)。 好好享受您在论坛的时光吧! + + (如果您在新用户级别需要和[职员](%{base_url/about)私下沟通的话,直接回复这封私信。) signup: - subject_template: "[%{site_name}] 激活您的新帐号" + subject_template: "[%{site_name}] 激活你的新帐号" text_body_template: | 欢迎来到 %{site_name}! - 点击下面的链接来确认和激活您的新帐号: + 点击下面的链接来确认和激活你的新帐号: %{base_url}/users/activate-account/%{email_token} - 如果上面的链接无法点击,请拷贝该链接并粘贴到您的浏览器的地址栏里。 + 如果上面的链接无法点击,请拷贝该链接并粘贴到你的浏览器的地址栏里。 page_not_found: - title: "您请求的页面不存在或者是私密的。" + title: "你请求的页面不存在或者是私密的。" popular_topics: "热门" recent_topics: "最近" see_more: "更多" @@ -1522,23 +1607,23 @@ zh_CN: login_required: welcome_message: | #[欢迎来到 %{title}](#welcome) - 您需要一个账号。请创建一个账号或者登录以继续。 + 你需要一个账号。请创建一个账号或者登录以继续。 terms_of_service: title: "服务条款" signup_form_message: '我已经阅读并接受 服务条款。' deleted: '已删除' upload: edit_reason: "下载外部图片留作副本" - unauthorized: "抱歉,您没有上传文件的权限(验证扩展:%{authorized_extensions})。" + unauthorized: "抱歉,你没有上传文件的权限(验证扩展:%{authorized_extensions})。" pasted_image_filename: "粘贴的图像" store_failure: "用户#%{user_id} 上传的 #%{upload_id} 保存失败。" attachments: - too_large: "抱歉,您试图上传的文件太大了(最大限制为%{max_size_kb}%kb)。" + too_large: "抱歉,您试图上传的文件太大了(最大限制为%{max_size_kb}%KB)。" images: - too_large: "抱歉,您试图上传的图片太大了(最大限制为%{max_size_kb}%kb),请裁剪它并充实" + too_large: "抱歉,您试图上传的图片太大了(最大限制为%{max_size_kb}%KB),请裁剪它并重试。" fetch_failure: "抱歉,获取图片时发生错误。" - unknown_image_type: "抱歉,您上传的文件似乎不是一张图片。" - size_not_found: "抱歉,我们无法获取图片大小,请检查您的图片是否已损坏。" + unknown_image_type: "抱歉,你上传的文件似乎不是一张图片。" + size_not_found: "抱歉,我们无法获取图片大小,请检查你的图片是否已损坏。" flag_reason: sockpuppet: "新用户创建了主题,而另外一个新用户以同一个 IP 在该主题回复。查看站点设置的 flag_sockpuppets。" spam_hosts: "新用户试图创建链接到同一个域名的多个帖子。查看站点设置的 newuser_spam_host_threshold。" @@ -1561,105 +1646,18 @@ zh_CN: guidelines: "指导" privacy: "隐私" edit_this_page: "编辑页面" + csv_export: + boolean_yes: "是" + boolean_no: "否" static_topic_first_reply: | 编辑本主题的第一帖以改变 %{page_name} 页面的内容。 guidelines_topic: title: "FAQ/指引" - body: | - - - ## [文明讨论的地方](#civilized) - - 请您在论坛表现得像在公共公园。我们也同样分享着一个公共社群资源 — 一个通过不断进行的讨论分享我们技能、知识和兴趣的地方。 - - 这些都不是硬性规定,只是一些帮助社群的人们来判断的规定。试用这些指引来保持干净和充满灵感的公开论坛。 - - - - ## [改善讨论](#improve) - - 帮助我们做一些帮助改善讨论的事,这样能帮助我们让这个地方变成一个参与讨论的好地方。 - - 这里被讨论的主题对我们很重要,并且如果他们对您也很重要,请和我们一样严肃对待他们。即使您不同意别人的话,但是也要尊重正在讨论这些主题的人们。 - - 改进讨论的一个方式是探索已经发生过的事。请在回复或者开始您自己的主题前花一些时间浏览,这样您更有机会遇见和您有共同爱好的人。 - - - - ## [尊重,当您不同意时](#agreeable) - - 您可能想表达您的不同意。这没问题。但是,记住_批评观点,而不是人_。请避免: - - * 指名道姓。 - * 人生攻击。 - * 回应帖子的语气而不是它的真正内容。 - * 下意识的反驳。 - - 相反,提供合理的观点改善讨论。 - - - - ## [您的参与有价值](#participate) - - 我们在这儿的讨论为大家树立了榜样。通过选择参与那些让论坛变成一个有意思的地方,来帮助我们改善社群的未来 — 并且避免那些没有帮助的行为。 - - Discourse 提供了让社群共同鉴别最棒(或最差)的贡献的工具:收藏、书签、赞、标记、回复、编辑或者其他。使用这些工具改善您自己的体验,和其他人的体验。 - - 让我们留下一个比发现它之前更好的社群。 - - - - ## [如果您看见了一个问题,标记它](#flag-problems) - - 版主有特别的权力;他们对论坛负责。但是您也是。通过您的帮助,版主能促进社群的发展,而不是守卫或者警察。 - - 当您见到不合适的行为,不要回应它。这种承认变相鼓励了这种不合适的行为,浪费了您的经历,并且浪费了每一个人的时间。_是要标记它。_如果收到了足够的标记,会有相应的处理,无论是自动地或版主的介入。 - - 为了维护我们的社群,版主保留了任何情况下删除任何内容和其他用户的权力。版主不能预览任何新帖子;版主和站点维护人员对社群里发表的任何言论均不负责任。 - - - - ## [永远文明](#be-civil) - - 粗鲁这样的行为会破坏健康的讨论: - - * 文明。不要发表任何理智的人会认为会冒犯、滥用或招致怨恨的言论。 - * 保持干净。不要发表任何淫秽或性暗示的东西。 - * 尊重其他人。不要骚扰或者让别人难过,检视别人,或暴露他们的个人信息。 - * 尊重我们的论坛。不要发表广告或者其他垃圾信息。 - - 这些条款没有准确的定义 — 避免任何_可能_的这些事。如果您不确定,问问自己这些是否能出现在纽约时报的头版头条上。 - - 这是一个公共论坛,并且搜索引擎会索引这些讨论。注意语言、链接和家庭和朋友的图片的安全。 - - - - ## [保持整洁](#keep-tidy) - - 花一点时间让东西出现在正确的位置,这样我们能花更多的时间在讨论上而非清理格式。所以: - - * 不要在错误的分类开始一个新主题。 - * 不要在多个主题中回复同样的内容。 - * 不要发布没有内容的回复。 - * 不要在中途改变主题。 - * 不要在您的帖子中签名 — 每一贴都附有您的个人信息。 - - 比起发表“+1”或者“同意”,使用赞按钮。比起将帖子带向一个决然不同的方向,使用回复为新主题。 - - - - ## [发表您自己的东西](#stealing) - - 您不能在没有他人授权的情况下发表任何属于他人的数字信息。您可能不能发表关于窃据他人知识产权(软件、视频、音频和图像)的任何简介、链接或方法,或其他任何不符合其他法律的事。 - - - - ## [使用条款](#tos) - - 是的,法律很无聊,但是我们必须保护我们自己 – 引申开来,您和您的数据 – 用于针对不友好的家伙们。我们有一个[使用条款](/tos)描述您的(或者我们)关于内容、隐私和法律的行为和权力。要使用我们的服务,您必须同意遵守[使用条款](/tos)。 + body: "\n\n## [文明讨论的地方](#civilized)\n\n在论坛上请表现得像在公共公园一样得体。我们一群人共享着一个公共社群资源 — 一个通过不断进行讨论以分享我们技能、知识和兴趣的地方。\n\n这些都不是硬性规定,只是一些帮助社群的人们来判断的规定。试用这些指引来保持干净和充满灵感的公开论坛。\n\n\n\n## [改善讨论](#improve)\n\n帮助我们做一些帮助改善讨论的事,这样能帮助我们让这个地方变成一个参与讨论的好地方。\n\n这里被讨论的主题对我们很重要,并且如果他们对您也很重要,请和我们一样严肃对待他们。即使您不同意别人的话,但是也要尊重正在讨论这些主题的人们。\n\n改进讨论的一个方式是探索已经发生过的事。请在回复或者开始您自己的主题前花一些时间浏览,这样您更有机会遇见和您有共同爱好的人。\n\n\n\n## [尊重,当您不同意时](#agreeable)\n\n您可能想表达您的不同意。这没问题。但是,记住_批评观点,而不是人_。请避免:\n\n* 指名道姓。\n* 人生攻击。\n* 回应帖子的语气而不是它的真正内容。\n* 下意识的反驳。\n\n相反,提供合理的观点改善讨论。\n\n\n\n## [您的参与有价值](#participate)\n\n我们在这儿的讨论为大家树立了榜样。通过选择参与那些让论坛变成一个有意思的地方,来帮助我们改善社群的未来 — 并且避免那些没有帮助的行为。\n\nDiscourse 提供了让社群共同鉴别最棒(或最差)的贡献的工具:收藏、书签、赞、标记、回复、编辑或者其他。使用这些工具改善您自己的体验,和其他人的体验。\n\n让我们留下一个比发现它之前更好的社群。\n\n\n\n## [如果您看见了一个问题,标记它](#flag-problems)\n\n版主有特别的权力;他们对论坛负责。但是您也是。通过您的帮助,版主能促进社群的发展,而不是守卫或者警察。\n\n当您见到不合适的行为,不要回应它。这种承认变相鼓励了这种不合适的行为,浪费了您的经历,并且浪费了每一个人的时间。_是要标记它。_如果收到了足够的标记,会有相应的处理,无论是自动地或版主的介入。\n\n为了维护我们的社群,版主保留了任何情况下删除任何内容和其他用户的权力。版主不能预览任何新帖子;版主和站点维护人员对社群里发表的任何言论均不负责任。\n\n\n\n## [永远文明](#be-civil)\n\n粗鲁这样的行为会破坏健康的讨论:\n\n* 文明。不要发表任何理智的人会认为会冒犯、滥用或招致怨恨的言论。\n* 保持干净。不要发表任何淫秽或性暗示的东西。\n* 尊重其他人。不要骚扰或者让别人难过,检视别人,或暴露他们的个人信息。\n* 尊重我们的论坛。不要发表广告或者其他垃圾信息。\n\n这些条款没有准确的定义 — 避免任何_可能_的这些事。如果您不确定,问问自己这些是否能出现在纽约时报的头版头条上。\n\n这是一个公共论坛,并且搜索引擎会索引这些讨论。注意语言、链接和家庭和朋友的图片的安全。\n\n\n\n## [保持整洁](#keep-tidy)\n\n花一点时间让东西出现在正确的位置,这样我们能花更多的时间在讨论上而非清理格式。所以:\n\n* 不要在错误的分类开始一个新主题。\n* 不要在多个主题中回复同样的内容。\n* 不要发布没有内容的回复。\n* 不要在中途改变主题。\n* 不要在您的帖子中签名 — 每一贴都附有您的个人信息。\n\n比起发表“+1”或者“同意”,使用赞按钮。比起将帖子带向一个决然不同的方向,使用“回复为关联主题”。\n\n\n\n## [发表您自己的东西](#stealing)\n\n您不能在没有他人授权的情况下发表任何属于他人的数字信息。您可能不能发表关于窃据他人知识产权(软件、视频、音频和图像)的任何简介、链接或方法,或其他任何不符合其他法律的事。\n\n\n\n## [有你参与](#power) \n\n这个站点由[一群友善的职员](/about)、你和社群一起运营。如果你对这里的事情仍有疑问,在[站务](/c/meta)分类新建一个主题并且开始讨论!如果遇到了重要或紧急的事情,并且不能用站务分类的主题或标记解决,通过[职员页面](/about)联系我们。\n\n\n\n## [使用条款](#tos)\n\n是的,法律很无聊,但是我们必须保护我们自己 – 引申开来,您和您的数据 – 用于针对不友好的家伙们。我们有一个[使用条款](/tos)描述您的(或者我们)关于内容、隐私和法律的行为和权力。要使用我们的服务,您必须同意遵守[使用条款](/tos)。\n" tos_topic: title: "服务条款" - body: "下面的条款及条件适用于 %{company_domain} 网站的所有内容、服务和产品,其方式通过网站,其中包括,但不限于 %{company_domain} 论坛软件、%{company_domain} 支持论坛和在 %{company_domain} 托管服务(“托管”)(一起称为,网站)。该网站属于 %{company_full_name}(“%{COMPANY_NAME}”)并由其运营。该网站要求您必须接受所有本文中所包含的条款和条件以及其他所有的规则、政策(包括修改,但不限于 %{company_domain} 的[隐私政策](/privacy)、[社群准则](/FAQ)和程序(统称“协议”),并可能不时由 %{COMPANY_NAME} 发布。\n\n访问或使用本网站之前,请仔细阅读本协议。访问或使用网站的任何部分,即代表您同意成为受本协议的条款和条件。如果您不同意所有的条款和本协议的条件,那么您可能无法访问网站或使用任何服务。若这些条款和条件被认为是与 %{COMPANY_NAME} 的合同,则您必须接受这些条款。本网站只提供给 13 岁以上个人。\n\n \n\n## [1. 您的 %{company_domain} 帐号](#1) \n\n如果您在网站上创建一个帐户,您有责任维护您的帐号安全,您对帐户下发生的所有活动完全负责。您必须立即通知 %{COMPANY_NAME} 关于您的帐户的任何未经授权或任何其他违反安全的使用。%{COMPANY_NAME} 不会对您的任何行或不作为,包括因该等作为或不作为而导致的任何种类的任何损失承担任何责任。 \n\n \n\n## [2. 提供者的责任](#2) \n\n如果您张贴的材料有关于别的网站的链接,或以其他方式(或允许任何第三方进行)张贴别的网站的材料(不论何等材料,“内容”),您需对此完全负责,包括使用因该内容所造成的任何损害。不管所讨论的内容是文字、图形、音频文件或计算机软件。您声明和保证: \n\n* 下载、复制和使用内容不会侵犯专有权利,包括但不限于著作权,专利,商标或商业秘密权利的任何第三方;\n* 如果您的雇主对您的创造拥有知识产权,您必须以(i)获准从您的雇主发布或提供的内容,包括但不限于任何软件,或(ii)由您的雇主放弃拥有内容的所有权利;\n* 您已完全遵守有关该内容的第三方条约,并同意了所有面向最终用户所需的任何条款;\n* 内容不包含或安装任何病毒、蠕虫、恶意软件、特洛伊木马或其他有害或破坏性的内容;\n* 内容不是垃圾,不是机器的或随机生成的,并且不包含设计来吸引流量到第三方网站或助推第三方网站的搜索引擎排名,或者进一步非法行为不道德或不想要的广告内容(诸如网络钓鱼)或误导接受者为对材料的来源(如欺骗);\n* 内容不色情,不包含威胁或煽动暴力,并没有侵犯任何第三方的隐私或公开权;\n* 您的内容不是不受欢迎的电子讯息,如新闻组垃圾链接、邮件列表、博客和网站,以及类似的不请自来的宣传方式宣传;\n* 您的内容不是在误导读者,以您是另外一个人或公司的方式;和 \n* 你应在内容中包含计算机代码,准确地分类和/或所描述的类型、性质、用途和材料的影响,是否请求 %{COMPANY_NAME} 或以其他方式这样做的情况下。 \n\n \n\n## [3.用户内容授权](#3) \n\n用户贡献使用 [知识共享 署名-非商业性使用-相同方式共享3.0 Unported之许可](http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh_CN)授权。在不限制任何该等陈述或保证的情况下,%{COMPANY_NAME} 有权利(但没有义务)在 %{COMPANY_NAME} 的全权酌情决定下(i)根据 %{COMPANY_NAME} 的合理意见,任何违反 %{COMPANY_NAME} 政策或以任何方式伤害或使他人反感的情况能拒绝或删除任何内容;或(ii)以任何理由终止或拒绝任何个人和组织访问和使用本网站。%{COMPANY_NAME} 有唯一裁量权。%{COMPANY_NAME} 没有义务退还先前支付的任何款项。 \n\n\n \n\n## [4. Payment and Renewal](#4)\n\n### General Terms\n\nOptional paid services or upgrades may be available on the Website. When utilizing an optional paid service or upgrade, you agree to pay %{company_name} the monthly or annual subscription fees indicated.\ + body: "下面的条款及条件适用于 %{company_domain} 网站的所有内容、服务和产品,其方式通过网站,其中包括,但不限于 %{company_domain} 论坛软件、%{company_domain} 支持论坛和在 %{company_domain} 托管服务(“托管”)(一起称为,网站)。该网站属于 %{company_full_name}(“%{COMPANY_NAME}”)并由其运营。该网站要求你必须接受所有本文中所包含的条款和条件以及其他所有的规则、政策(包括修改,但不限于 %{company_domain} 的[隐私政策](/privacy)、[社群准则](/FAQ)和程序(统称“协议”),并可能不时由 %{COMPANY_NAME} 发布。\n\n访问或使用本网站之前,请仔细阅读本协议。访问或使用网站的任何部分,即代表你同意成为受本协议的条款和条件。如果你不同意所有的条款和本协议的条件,那么你可能无法访问网站或使用任何服务。若这些条款和条件被认为是与 %{COMPANY_NAME} 的合同,则你必须接受这些条款。本网站只提供给 13 岁以上个人。\n\n \n\n## [1. 你的 %{company_domain} 帐号](#1) \n\n如果你在网站上创建一个帐户,你有责任维护你的帐号安全,你对帐户下发生的所有活动完全负责。你必须立即通知 %{COMPANY_NAME} 关于你的帐户的任何未经授权或任何其他违反安全的使用。%{COMPANY_NAME} 不会对你的任何行或不作为,包括因该等作为或不作为而导致的任何种类的任何损失承担任何责任。 \n\n \n\n## [2. 提供者的责任](#2) \n\n如果你张贴的材料有关于别的网站的链接,或以其他方式(或允许任何第三方进行)张贴别的网站的材料(不论何等材料,“内容”),你需对此完全负责,包括使用因该内容所造成的任何损害。不管所讨论的内容是文字、图形、音频文件或计算机软件。你声明和保证: \n\n* 下载、复制和使用内容不会侵犯专有权利,包括但不限于著作权,专利,商标或商业秘密权利的任何第三方;\n* 如果你的雇主对你的创造拥有知识产权,你必须以(i)获准从你的雇主发布或提供的内容,包括但不限于任何软件,或(ii)由你的雇主放弃拥有内容的所有权利;\n* 你已完全遵守有关该内容的第三方条约,并同意了所有面向最终用户所需的任何条款;\n* 内容不包含或安装任何病毒、蠕虫、恶意软件、特洛伊木马或其他有害或破坏性的内容;\n* 内容不是垃圾,不是机器的或随机生成的,并且不包含设计来吸引流量到第三方网站或助推第三方网站的搜索引擎排名,或者进一步非法行为不道德或不想要的广告内容(诸如网络钓鱼)或误导接受者为对材料的来源(如欺骗);\n* 内容不色情,不包含威胁或煽动暴力,并没有侵犯任何第三方的隐私或公开权;\n* 你的内容不是不受欢迎的电子讯息,如新闻组垃圾链接、邮件列表、博客和网站,以及类似的不请自来的宣传方式宣传;\n* 你的内容不是在误导读者,以你是另外一个人或公司的方式;和 \n* 你应在内容中包含计算机代码,准确地分类和/或所描述的类型、性质、用途和材料的影响,是否请求 %{COMPANY_NAME} 或以其他方式这样做的情况下。 \n\n \n\n## [3.用户内容授权](#3) \n\n用户贡献使用 [知识共享 署名-非商业性使用-相同方式共享3.0 Unported之许可](http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh_CN)授权。在不限制任何该等陈述或保证的情况下,%{COMPANY_NAME} 有权利(但没有义务)在 %{COMPANY_NAME} 的全权酌情决定下(i)根据 %{COMPANY_NAME} 的合理意见,任何违反 %{COMPANY_NAME} 政策或以任何方式伤害或使他人反感的情况能拒绝或删除任何内容;或(ii)以任何理由终止或拒绝任何个人和组织访问和使用本网站。%{COMPANY_NAME} 有唯一裁量权。%{COMPANY_NAME} 没有义务退还先前支付的任何款项。 \n\n\n \n\n## [4. Payment and Renewal](#4)\n\n### General Terms\n\nOptional paid services or upgrades may be available on the Website. When utilizing an optional paid service or upgrade, you agree to pay %{company_name} the monthly or annual subscription fees indicated.\ \ Payments will be charged on a pre-pay basis on the day you begin utilizing the service or upgrade and will cover the use of that service or upgrade for a monthly or annual subscription period as indicated. These fees are not refundable.\n\n### Automatic Renewal\n\nUnless you notify %{company_name} before the end of the applicable subscription period that you want to cancel a service or upgrade, your subscription will automatically renew and you authorize us to collect the then-applicable annual or monthly subscription fee (as well as any taxes) using any credit card or other payment mechanism we have on record for you. Subscriptions can be canceled at any time.\n\n\n\n## [5. Services](#5)\n\n### Hosting, Support Services\n\nOptional Hosting and Support services may be provided by %{company_name} under the terms and conditions for each such service. By signing up for a Hosting/Support or Support services account, you agree to abide by such terms and conditions.\n\n\n\n## [6. Responsibility of Website Visitors](#6)\n\n%{company_name} has not reviewed, and cannot review, all of the material, including computer software, posted to the Website, and cannot therefore be responsible for that material’s content, use or effects. By operating the Website, %{company_name} does not represent or imply that it endorses the material there posted, or that it believes such material to be accurate, useful or non-harmful. You are responsible for taking precautions as necessary to protect yourself and your computer systems from viruses, worms, Trojan horses, and other harmful or destructive content. The Website may contain content that is offensive, indecent, or otherwise objectionable, as well as content containing technical inaccuracies, typographical mistakes, and other errors. The Website may also contain material that violates the privacy or publicity rights, or infringes the intellectual property and other proprietary rights, of third parties, or the downloading, copying or use of which is subject to additional terms and conditions,\ \ stated or unstated. %{company_name} disclaims any responsibility for any harm resulting from the use by visitors of the Website, or from any downloading by those visitors of content there posted.\n\n\n\n## [7. Content Posted on Other Websites](#7)\n\nWe have not reviewed, and cannot review, all of the material, including computer software, made available through the websites and webpages to which %{company_domain} links, and that link to %{company_domain}. %{company_name} does not have any control over those non-%{company_domain} websites and webpages, and is not responsible for their contents or their use. By linking to a non-%{company_domain} website or webpage, %{company_name} does not represent or imply that it endorses such website or webpage. You are responsible for taking precautions as necessary to protect yourself and your computer systems from viruses, worms, Trojan horses, and other harmful or destructive content. %{company_name} disclaims any responsibility for any harm resulting from your use of non-%{company_domain} websites and webpages.\n\n\n\n## [8. Copyright Infringement and DMCA Policy](#8)\n\nAs %{company_name} asks others to respect its intellectual property rights, it respects the intellectual property rights of others. If you believe that material located on or linked to by %{company_domain} violates your copyright, and if this website resides in the USA, you are encouraged to notify %{company_name} in accordance with %{company_name}’s [Digital Millennium Copyright Act](http://en.wikipedia.org/wiki/Digital_Millennium_Copyright_Act) (\"DMCA\") Policy. %{company_name} will respond to all such notices, including as required or appropriate by removing the infringing material or disabling all links to the infringing material. %{company_name} will terminate a visitor’s access to and use of the Website if, under appropriate circumstances, the visitor is determined to be a repeat infringer of the copyrights or other intellectual property rights of %{company_name} or others. In the case of such termination,\ \ %{company_name} will have no obligation to provide a refund of any amounts previously paid to %{company_name}.\n\n\n\n## [9. Intellectual Property](#9)\n\nThis Agreement does not transfer from %{company_name} to you any %{company_name} or third party intellectual property, and all right, title and interest in and to such property will remain (as between the parties) solely with %{company_name}. %{company_name}, %{company_domain}, the %{company_domain} logo, and all other trademarks, service marks, graphics and logos used in connection with %{company_domain}, or the Website are trademarks or registered trademarks of %{company_name} or %{company_name}’s licensors. Other trademarks, service marks, graphics and logos used in connection with the Website may be the trademarks of other third parties. Your use of the Website grants you no right or license to reproduce or otherwise use any %{company_name} or third-party trademarks.\n\n\n\n## [10. Advertisements](#10)\n\n%{company_name} reserves the right to display advertisements on your content unless you have purchased an Ad-free Upgrade or a Services account.\n\n\n\n## [11. Attribution](#11)\n\n%{company_name} reserves the right to display attribution links such as ‘Powered by %{company_domain},’ theme author, and font attribution in your content footer or toolbar. Footer credits and the %{company_domain} toolbar may not be removed regardless of upgrades purchased.\n\n\n\n## [12. Changes](#12)\n\n%{company_name} reserves the right, at its sole discretion, to modify or replace any part of this Agreement. It is your responsibility to check this Agreement periodically for changes. Your continued use of or access to the Website following the posting of any changes to this Agreement constitutes acceptance of those changes. %{company_name} may also, in the future, offer new services and/or features through the Website (including, the release of new tools and resources). Such new features and/or services shall be subject to the terms and conditions\ @@ -1673,28 +1671,28 @@ zh_CN: ## [我们收集什么信息?](#collect) - 我们从您在我们站点注册开始从您那开始收集信息,并收集关于您在论坛的阅读和写作的数据,并评估分享的内容。 + 我们从你在我们站点注册开始从你那开始收集信息,并收集关于你在论坛的阅读和写作的数据,并评估分享的内容。 - 当在我们站点注册时,您可能被要求输入您的名字和邮件地址。然而你可以在不用注册的情况下访问站点。您的邮件地将通过一个独一无二的链接验证。如果链接被访问了,我们就知道您控制了该邮件地址。 + 当在我们站点注册时,你可能被要求输入你的名字和邮件地址。然而你可以在不用注册的情况下访问站点。你的邮件地将通过一个独一无二的链接验证。如果链接被访问了,我们就知道你控制了该邮件地址。 当已注册和发帖时,我们记录发布帖子时的 IP 地址。我们也可能保留服务器日志,其中包括了每一个向我们服务器的请求。 - ## [我们如何使用您的信息?](#use) + ## [我们如何使用你的信息?](#use) - 从您那收集的任何数据将以以下方式使用: + 从你那收集的任何数据将以以下方式使用: - * 改进您的个人体验 — 您的信息帮助我们更好地满足您的个人需求。 - * 改进我们的站点 — 我们基于信息和我们从您那收到的反馈不断地试图改进我们的站点。 - * 改善我们的客户服务 — 您的信息帮助我们更有效地回应用户服务请求和支持。 - * 用于发送阶段性的邮件 — 您提供的邮件地址可能用于接受信息、您想看到的通知或与您用户名有关的回复和询问,或是其他的请求和问题。 + * 改进你的个人体验 — 你的信息帮助我们更好地满足你的个人需求。 + * 改进我们的站点 — 我们基于信息和我们从你那收到的反馈不断地试图改进我们的站点。 + * 改善我们的客户服务 — 你的信息帮助我们更有效地回应用户服务请求和支持。 + * 用于发送阶段性的邮件 — 你提供的邮件地址可能用于接受信息、你想看到的通知或与你用户名有关的回复和询问,或是其他的请求和问题。 - ## [我们如何保护您的信息?](#protect) + ## [我们如何保护你的信息?](#protect) - 我们实现了一系列的安全措施保证您输入、提交或者访问您个人信息的数据安全。 + 我们实现了一系列的安全措施保证你输入、提交或者访问你个人信息的数据安全。 @@ -1709,27 +1707,27 @@ zh_CN: ## [我们使用 Cookie 吗?](#cookies) - 是的。Cookie 是网站或它的服务商通过网页浏览器存储在您电脑硬盘上的小文件(如果您同意)。这些 Cookie 使站点能分辨您的浏览器,并且,如果您注册了一个账户,与您的注册账户关联。 + 是的。Cookie 是网站或它的服务商通过网页浏览器存储在你电脑硬盘上的小文件(如果你同意)。这些 Cookie 使站点能分辨你的浏览器,并且,如果你注册了一个账户,与你的注册账户关联。 - 我们使用 Cookie 为之后的访问和编译一小段关于站点流量和交互的数据来判断并保存您的个人设置,这样我们可以在之后提供更好的站点体验和工具。我们可能使用第三方服务商来帮助我们更好地理解我们的站点访客。这些服务商是不允许代表我们使用收集的信息,除非是在帮助我们执行和改进我们的站点。 + 我们使用 Cookie 为之后的访问和编译一小段关于站点流量和交互的数据来判断并保存你的个人设置,这样我们可以在之后提供更好的站点体验和工具。我们可能使用第三方服务商来帮助我们更好地理解我们的站点访客。这些服务商是不允许代表我们使用收集的信息,除非是在帮助我们执行和改进我们的站点。 ## [我们会在站外提供任何信息吗?](#disclose) - 我们绝不销售、教育或任何向外转移您个人信息的行为。这不包括帮助我们管理站点、改进站定或给您提供服务的第三方团体,这些团体需要保证对这些信息保密。当我们认为提交您的信息符合法律、我们的站点政策或保护我们或其他人的权利、知识产权或安全时,我们也可能提交您的信息。然而,非个人访问信息可能供其他团体使用,用于市场、广告或其他用途。 + 我们绝不销售、教育或任何向外转移你个人信息的行为。这不包括帮助我们管理站点、改进站定或给你提供服务的第三方团体,这些团体需要保证对这些信息保密。当我们认为提交你的信息符合法律、我们的站点政策或保护我们或其他人的权利、知识产权或安全时,我们也可能提交你的信息。然而,非个人访问信息可能供其他团体使用,用于市场、广告或其他用途。 ## [第三方链接](#third-party) - 偶尔地,根据我们的判断,我们可能在我们的站点上包括或提供第三方团体的产品或服务。这些第三方站点用于独立和不同的隐私政策。因此我们对链接到的站点或活动没有责任和权利。尽管如此,我们寻求保护我们的整个站点冰鞋欢迎您给这些站点反馈。 + 偶尔地,根据我们的判断,我们可能在我们的站点上包括或提供第三方团体的产品或服务。这些第三方站点用于独立和不同的隐私政策。因此我们对链接到的站点或活动没有责任和权利。尽管如此,我们寻求保护我们的整个站点冰鞋欢迎你给这些站点反馈。 ## [儿童在线隐私保护法案合规](#coppa) - 我们的站点、产品和服务提供给 13 岁以上的人们。如果服务器位于美国,并且您小于 13 岁,根据 COPPA([儿童在线隐私保护法案合规 + 我们的站点、产品和服务提供给 13 岁以上的人们。如果服务器位于美国,并且你小于 13 岁,根据 COPPA([儿童在线隐私保护法案合规 ](http://en.wikipedia.org/wiki/Children)),不要使用这个站点。 @@ -1740,9 +1738,9 @@ zh_CN: - ## [您的同意](#consent) + ## [你的同意](#consent) - 您使用站点的同时,代表您同意了我们网站的隐私政策。 + 你使用站点的同时,代表你同意了我们网站的隐私政策。 @@ -1756,20 +1754,21 @@ zh_CN:

小技巧

    -
  • 标题匹配优先,所以有疑问时,搜索标题
  • -
  • 独一无二的、不常见的单词将总是产生最好的结果
  • -
  • 无论何时,限制您的搜索范围至一个特定的分类、用户或主题
  • +
  • 匹配有优先级。所以有疑问时,搜索标题
  • +
  • 独一无二的、不常见的单词将总是产生最好的结果
  • +
  • 只要有可能,限制您的搜索范围至一个特定的分类、用户或主题

选项

- - - - - -
order:viewsorder:latest
status:openstatus:closedstatus:archivedstatus:norepliesstatus:singleuser
category:foouser:foo
in:likesin:postedin:watchingin:tracking
+ + + + + + +
order:viewsorder:latest
status:openstatus:closedstatus:archivedstatus:norepliesstatus:singleuser
category:foouser:foo
in:likesin:postedin:watchingin:trackingin:private
in:bookmarks

- 彩虹 category:公园 status:open order:latest将搜索在“公园”分类中没有关闭或存档中的包含“彩虹”的主题,并按最后一个帖子的日期来排序。 + 彩虹 category:公园 status:open order:latest 将搜索在“公园”分类中没有关闭或存档中的包含“彩虹”的主题,并按最后一个帖子的日期来排序。

diff --git a/config/locales/server.zh_TW.yml b/config/locales/server.zh_TW.yml index 81c8c35ac5..3cd6815fc2 100644 --- a/config/locales/server.zh_TW.yml +++ b/config/locales/server.zh_TW.yml @@ -6,15 +6,20 @@ # https://www.transifex.com/projects/p/discourse-org/ zh_TW: + stringex: + characters: + number: "-" + i18n: + transliterate: + rule: + ț: "t" + Ț: "t" + ș: "s" + Ș: "s" dates: short_date_no_year: "D MMM" short_date: "D MMM, YYYY" long_date: "MMMM D, YYYY h:mma" - time: - formats: - short: "%Y-%m-%d" - short_no_year: "%B %-d" - date_only: "%b %-d, %Y" title: "論壇" topics: "討論話題" posts: "文章" @@ -26,6 +31,7 @@ zh_TW: errors: messages: too_long_validation: "限制 %{max} 個字元,你已經輸入了 %{length} 個字元" + invalid_boolean: "無效的真假值。" embed: load_from_remote: "載入文章時發生了錯誤" bulk_invite: @@ -89,13 +95,16 @@ zh_TW: latest: "最新討論話題" hot: "熱門討論話題" too_late_to_edit: "這文章建立很久了,它已經無法被編輯或刪除" + excerpt_image: "圖片" groups: errors: can_not_modify_automatic: "你無法修改自動群組" default_names: + everyone: "所有人" admins: "管理員" moderators: "板主" staff: "工作人員" + trust_level_0: "信任等級 0" trust_level_1: "信任等級 1" trust_level_2: "信任等級 2" trust_level_3: "信任等級 3" @@ -128,8 +137,10 @@ zh_TW: models: topic: attributes: - archetype: + base: + too_many_users: "你只能同時傳送警告給一位用戶。" cant_send_pm: "抱歉,你不能向該用戶發送私人訊息。" + no_user_selected: "你必須選擇有效用戶。" user: attributes: password: @@ -169,6 +180,12 @@ zh_TW: title: "新用戶" basic: title: "初級用戶" + regular: + title: "成員" + leader: + title: "一般用戶" + elder: + title: "領先用戶" rate_limiter: too_many_requests: "你的瀏覽速度過於頻繁,請等待 %{time_left} 後再試。" hours: @@ -258,9 +275,12 @@ zh_TW: title: '不當內容' long_form: '投訴為不當內容' notify_user: + title: '私人訊息 @{{username}}' email_title: '你的文章在"%{title}"' email_body: "%{link}\n\n%{message}" notify_moderators: + title: "其他" + long_form: '向板主回報問題發文' email_title: '一篇在 "%{title}" 裡的文章需要板主注意' email_body: "%{link}\n\n%{message}" bookmark: @@ -394,10 +414,6 @@ zh_TW: twitter_config_warning: '伺服器允許使用 Twitter 帳號登入 (enable_twitter_logins), 但未有設定 key 和 secret values 。 請在 網站設定 裡更改設定。 設定教學指南。' github_config_warning: '伺服器允許使用 GitHub 帳號登入 (enable_github_logins), 但未有設定 client id 和 secret values。 請在 網站設定 裡更改設定。 設定教學指南。' failing_emails_warning: '%{num_failed_jobs}個 email 任務失敗。請檢查 config/environments/production.rb 內 config.action_mailer 項目是否已正確設定。在 Sidekiq 中檢視失敗的任務。' - default_logo_warning: "你沒有自訂你的網站 logo。請在網站設定中更改 logo_url, logo_small_url 及 favicon_url。" - contact_email_missing: "你沒有為網站指定一個聯絡電郵。請在網站設定中設定 contact_email。" - contact_email_invalid: "網站聯絡電郵不正確。請在網站設定修改。" - title_nag: "正在使用預設的網站標題。請在網站設定更改你的網站標題。" consumer_email_warning: "您的網站正以Gmail或其他服務供應商郵件服務發送郵件。Gmail限制每日郵件發送數量. 請考慮使用其他電郵服務商來保證郵件能成功發送,例如 mandrill.com。" content_types: education_new_reply: @@ -440,9 +456,6 @@ zh_TW: uncategorized_description: "\"未分類\"的分類的描述,留空則無描述" allow_duplicate_topic_titles: "允許話題有相同,重複的標題" unique_posts_mins: "使用者再次發表包含相同內容文章的間隔時間" - title: "網站的簡短標題,用於 HTML 的標題( title )" - site_description: "用簡短的文字描述這個論壇,將使用在 meta description 標籤內" - contact_email: "網站關鍵聯絡人的 Email,discourse.org 可能將把重要的更新通知用此 Email 進行聯絡" queue_jobs: "如果失敗佇列在排隊,使用 Sidekiq 消息引擎對不同的工作排隊" crawl_images: "允許從第三方 URL 取得圖片,來加入寬和高的數值" disabled_image_download_domains: "在此列表的網域名稱的遠端圖片將不會進行下載,用 | 分割多個域名" @@ -455,13 +468,13 @@ zh_TW: fixed_category_positions: "若勾選,你將能調整並固定分類的順序。若不勾選,分類將會依照活躍程度來排序。" exclude_rel_nofollow_domains: "不添加 nofollow 標籤到一列逗號分割的功能變數名稱 (使用父系網域名,例如 tld.com,將自動允許子功能變數名稱,例如 sub.tld.com)" post_excerpt_maxlength: "文章摘要的最大文字長度" - logo_url: "你的網站左上方的Logo圖片,例如 http://example.com/logo.png" favicon_url: "你的網站圖示 (favicon),參考 http://zh.wikipedia.org/wiki/Favicon" apple_touch_icon_url: "Apple 觸碰設備使用的圖示,建議 144 X 144px 大小" use_https: "使用 SSL 安全套接層來瀏覽本站嗎?" summary_posts_required: "如果使用了\"此話題的摘用\",話題顯示時需滿足最小的文章的數量" summary_likes_required: "如果使用了\"此話題的摘用\",話題顯示時需滿足最小得到\"讚\"的數量" summary_percent_filter: "當用戶點擊 \"此話題的摘要\",顯示前面多少 % 的文章" + enable_private_messages: "允許信任等級 1 之用戶建立私人訊息和私下交流" enable_long_polling: "啟用消息匯流排使通知功能可以使用長輪詢(long polling)" anon_polling_interval: "匿名使用者用戶端輪詢時間間隔(單位 毫秒)" auto_track_topics_after: "經過多少毫秒之後一個討論話題就會被自動追蹤的全域預設值,用戶可以覆寫此設定 ( 0 為總是,-1 為永不 )" @@ -477,11 +490,9 @@ zh_TW: ga_domain_name: "穀歌分析功能變數名稱,例如:mysite.com;參考 http://google.com/analytics" enable_noscript_support: "開啟noscript來對網路爬蟲來做標準的支援" allow_moderators_to_create_categories: "允許板主建立新分類" - top_menu: "確定在主頁導航條包含哪些條目,以及排列順序。例如:latest|hot|read|starred|unread|new|posted|categories" post_menu: "確定在文章功能表條包含哪些條目,以及排列順序。例如:like|edit|flag|delete|share|bookmark|reply" share_links: "決定在分享對話方塊裡顯示哪些項目、以什麼順序顯示。" track_external_right_clicks: "追蹤外部連結的右鍵點擊 ( 例如:開啟於瀏覽器的新頁面 ),預設是關閉的,因為它會重寫URLs" - topics_per_page: "預設在話題列表頁載入多少篇,然後捲軸下拉才會出現更多討論話題" send_welcome_message: "給所有的新用戶發送一個快速引導的私訊" topics_per_period_in_top_summary: "預設推薦話題的顯示數量" topics_per_period_in_top_page: "在展開 \"顯示更多\" 推薦話題列表的顯示數量" @@ -500,9 +511,7 @@ zh_TW: login_required: "需要登入才能進入網站,不允許匿名操作" min_password_length: "最小密碼長度" block_common_passwords: "不允許使用 10,000 個最常用的密碼" - enable_sso: "啟用單一登入(single sign on)( 注意:禁用邀請功能 )" sso_url: "單點登入 URL 入口點" - sso_secret: "用於加解密 SSO 訊息的密鑰字串,請確認在 10 個字元以上" enable_local_logins: "啟用網站用戶名稱和密碼驗證 ( 注意:這必須啟用邀請才能有動作 )" allow_new_registrations: "允許新用戶註冊,如果取消選取,則沒有人能夠註冊" enable_google_logins: "( 已作廢 ) 啟用 Google 認證。這是已經被 Google 作廢的 Open ID 認證方式,新的安裝將無法使用。請改用 Google Oauth2。現有的安裝必須在 2015 年 4 月 20 日以前移轉至 Google Oauth2。" @@ -533,7 +542,6 @@ zh_TW: max_flags_per_day: "每個用戶每天最多能\"標記\"的數量" max_bookmarks_per_day: "每個用戶每天最多能建立\"書籤\"的數量" max_edits_per_day: "每個用戶每天最大的\"編輯次數\"的數量" - max_stars_per_day: "每個用戶一天內最多能標星的數量" max_topics_per_day: "每個用戶每天最多建立\"討論話題\"的數量" max_private_messages_per_day: "每個用戶每天最多能發\"私訊\"的數量" suggested_topics: "討論話題下的推薦話題數量" @@ -543,8 +551,12 @@ zh_TW: s3_region: "上傳至 Amazon S3 的 region name,將用於上傳圖片" default_invitee_trust_level: "預設的受邀用戶等級 (0-4)" default_trust_level: "所有新用戶的預設等級 (0-4)" + tl3_links_no_follow: "禁止從信任等級 3 之用戶所發表的連結中刪除 rel=nofollow 標籤" min_trust_to_create_topic: "建立話題最低所需的信任等級" min_trust_to_edit_wiki_post: "編輯被標示為維基的文章所需之最低信任等級。" + newuser_max_links: "新用戶可添加多少連結於一篇文章中" + newuser_max_images: "新用戶可添加多少張圖片於一篇文章中" + newuser_max_attachments: "新用戶可添加多少附件於一篇文章中" title_fancy_entities: "轉換討論話題標題中的 HTML 實體" title_prettify: "防止常見標題裡的錯別字和錯誤,包括全部大寫,首字小寫,多個!和?,結尾多餘的. 等等。" tos_url: "如果你想要使用一個部署在某個地方的服務條款文檔,那麼請在此填寫其完整URL地址。" @@ -561,12 +573,12 @@ zh_TW: automatically_download_gravatars: "當用戶註冊或更改EMail時下載 Gravatars 圖片" digest_topics: "EMail 摘要中顯示的最大話題數量" digest_min_excerpt_length: "EMail 摘要中每篇文章最少顯示的字元數量" + default_external_links_in_new_tab: "以新分頁開啟所有外部連結,用戶可於個人偏好設定更改設定。" detect_custom_avatars: "是否檢測用會上傳了自訂的個人圖示" max_daily_gravatar_crawls: "一天內 Discourse 將自動檢查 Gravatar 自訂個人圖示的次數" public_user_custom_fields: "用戶可設定公開顯示的自定欄位白名單" allow_profile_backgrounds: "允許使用者上傳個人檔案背景圖片" suppress_uncategorized_badge: "不在\"無分類\"的話題列表內顯示徽章。" - enable_names: "允許用戶顯示全名,並禁止隱藏全名" autohighlight_all_code: "即使未顯示特定語言,仍為所有預編排程式套用程式碼顏色提示" embed_truncate: "截斷 embed 過的文章" embed_category: "embedded 話題的分類" @@ -634,7 +646,6 @@ zh_TW: errors: "%{errors}" not_available: "不可用,試試 %{suggestion}?" something_already_taken: "出了一些問題,可能此用戶名或電子郵箱已經被註冊。試試 忘記密碼 連結吧。" - omniauth_error: "抱歉,在給你的 %{strategy} 帳號授權時發生了錯誤。有可能你沒有批准授權申請?" omniauth_error_unknown: "在處理你的登入過程中發生了錯誤,請重試。" new_registrations_disabled: "現在不允許註冊新的帳戶" user: @@ -672,10 +683,6 @@ zh_TW: flags_reminder: please_review: "請再次確認。" post_number: "文章" - flag_reasons: - off_topic: "你的文章被標記為 **離題**:社群認為它不切合主題,也就是該討論話題的標題和首篇文章所述之內容。" - spam: "你的文章被標記為 **垃圾內容**:我們認為這是一則廣告,和討論話題無關只是促銷資訊。" - notify_moderators: "你的文章被投訴 **需要板主注意**:社群認為文章中某些部分需要板主介入干涉。" system_messages: post_hidden: subject_template: "因為社群投訴而被隱藏的文章" @@ -683,12 +690,22 @@ zh_TW: subject_template: "歡迎來到 %{site_name}!" welcome_invite: subject_template: "歡迎來到 %{site_name}!" + backup_failed: + subject_template: "備份失敗" + restore_failed: + subject_template: "復原失敗" bulk_invite_succeeded: subject_template: "批次用戶邀請已完成處理" bulk_invite_failed: subject_template: "批次使用者邀請處理已經完成,發生了些錯誤" + csv_export_succeeded: + subject_template: "資料匯出完成" + csv_export_failed: + subject_template: "資料匯出失敗" email_reject_trust_level: subject_template: "EMail 錯誤 -- 尚未達到信任等級" + email_reject_no_account: + subject_template: "EMail 錯誤 -- 未知的帳戶" email_reject_empty: subject_template: "EMail 錯誤 -- 沒有內文" email_reject_parsing: @@ -729,11 +746,15 @@ zh_TW: download_remote_images_disabled: subject_template: "停用下載遠端圖片" unsubscribe_link: "如果你希望取消這些郵件的訂閱,請瀏覽 [用戶偏好設定](%{user_preferences_url})。" + subject_re: "回覆:" + subject_pm: "[私訊]" user_notifications: previous_discussion: "之前的回覆" unsubscribe: title: "取消訂閱" description: "不再對這些郵件感興趣?沒問題!點擊下面按鈕來立即取消訂閱:" + reply_by_email: "要回應時,請回覆這封郵件,或在瀏覽器裡開啟 %{base_url}%{url} 網址。" + visit_link_to_respond: "要回應時,請在瀏覽器裡開啟 %{base_url}%{url} 網址。" posted_by: "由 %{username} 張貼於 %{post_date}" user_invited_to_private_message_pm: subject_template: "[%{site_name}] %{username} 邀請你進行私人對話 '%{topic_title}'" @@ -825,14 +846,13 @@ zh_TW: search_title: "搜尋該網頁" search_google: "Google" terms_of_service: + title: "服務條款" signup_form_message: '我已閱讀並接受 服務條款' deleted: '已刪除' upload: edit_reason: "下載外部圖片留做存檔" pasted_image_filename: "貼上的圖片" store_failure: "用戶 #%{user_id} 上傳 #%{upload_id} 儲存失敗" - attachments: - too_large: "抱歉,你試著上傳的檔案過大(檔案上傳上限: %{max_size_kb}%kb)" images: fetch_failure: "抱歉,獲取圖片時發生錯誤。" unknown_image_type: "抱歉,你上傳的檔似乎不是一張圖片。" @@ -855,5 +875,11 @@ zh_TW: color_schemes: base_theme_name: "基礎" guidelines: "指導" + privacy: "隱私政策" + csv_export: + boolean_yes: "是" + boolean_no: "否" guidelines_topic: title: "FAQ / 使用守則" + privacy_topic: + title: "隱私政策" diff --git a/config/nginx.sample.conf b/config/nginx.sample.conf index 434a3ec88b..5f8ceaac63 100644 --- a/config/nginx.sample.conf +++ b/config/nginx.sample.conf @@ -34,6 +34,18 @@ server { gzip_comp_level 5; gzip_types application/json text/css application/x-javascript application/javascript; + # Uncomment and configure this section for HTTPS support + # NOTE: Put your ssl cert in your main nginx config directory (/etc/nginx) + # + # rewrite ^/(.*) https://enter.your.web.hostname.here/$1 permanent; + # + # listen 443 ssl; + # ssl_certificate your-hostname-cert.pem; + # ssl_certificate_key your-hostname-cert.key; + # ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + # ssl_ciphers HIGH:!aNULL:!MD5; + # + server_name enter.your.web.hostname.here; server_tokens off; @@ -64,6 +76,11 @@ server { internal; } + # prevent direct download of backups + location ^~ /backups/ { + internal; + } + location / { root $public; add_header ETag ""; @@ -79,9 +96,21 @@ server { # asset pipeline enables this gzip_static on; add_header Cache-Control public; + # TODO I don't think this break is needed, it just breaks out of rewrite break; } + location ~ ^/plugins/ { + expires 1y; + add_header Cache-Control public; + } + + # cache emojis + location ~ /_?emoji/ { + expires 1y; + add_header Cache-Control public; + } + location ~ ^/uploads/ { # NOTE: it is really annoying that we can't just define headers diff --git a/config/routes.rb b/config/routes.rb index f33712d4bb..94d7d9ebd4 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -26,6 +26,7 @@ Discourse::Application.routes.draw do resources :about get "site" => "site#index" + get "site_customizations/:key" => "site_customizations#show" resources :forums get "srv/status" => "forums#status" @@ -33,6 +34,8 @@ Discourse::Application.routes.draw do namespace :admin, constraints: StaffConstraint.new do get "" => "admin#index" + get 'plugins' => 'plugins#index' + resources :site_settings, constraints: AdminConstraint.new do collection do get "category/:id" => "site_settings#index" @@ -45,13 +48,21 @@ Discourse::Application.routes.draw do collection do post "refresh_automatic_groups" => "groups#refresh_automatic_groups" end - get "users" + member do + put "members" => "groups#add_members" + delete "members" => "groups#remove_member" + end end + get "groups/:type" => "groups#show", constraints: AdminConstraint.new + get "groups/:type/:id" => "groups#show", constraints: AdminConstraint.new + resources :users, id: USERNAME_ROUTE_FORMAT do collection do get "list/:query" => "users#index" get "ip-info" => "users#ip_info" + delete "delete-others-with-same-ip" => "users#delete_other_accounts_with_same_ip" + get "total-others-with-same-ip" => "users#total_other_accounts_with_same_ip" put "approve-bulk" => "users#approve_bulk" delete "reject-bulk" => "users#reject_bulk" end @@ -83,6 +94,7 @@ Discourse::Application.routes.draw do post "users/sync_sso" => "users#sync_sso", constraints: AdminConstraint.new + post "users/invite_admin" => "users#invite_admin", constraints: AdminConstraint.new resources :impersonate, constraints: AdminConstraint.new @@ -99,7 +111,11 @@ Discourse::Application.routes.draw do scope "/logs" do resources :staff_action_logs, only: [:index] resources :screened_emails, only: [:index, :destroy] - resources :screened_ip_addresses, only: [:index, :create, :update, :destroy] + resources :screened_ip_addresses, only: [:index, :create, :update, :destroy] do + collection do + post "roll_up" + end + end resources :screened_urls, only: [:index] end @@ -118,6 +134,7 @@ Discourse::Application.routes.draw do resources :site_text, constraints: AdminConstraint.new resources :site_text_types, constraints: AdminConstraint.new resources :user_fields, constraints: AdminConstraint.new + resources :emojis, constraints: AdminConstraint.new end resources :color_schemes, constraints: AdminConstraint.new @@ -155,15 +172,6 @@ Discourse::Application.routes.draw do end end - resources :export_csv, constraints: AdminConstraint.new do - member do - get "download" => "export_csv#download", constraints: { id: /[^\/]+/ } - end - collection do - get "users" => "export_csv#export_user_list" - end - end - resources :badges, constraints: AdminConstraint.new do collection do get "types" => "badges#badge_types" @@ -173,6 +181,7 @@ Discourse::Application.routes.draw do end get "memory_stats"=> "diagnostics#memory_stats", constraints: AdminConstraint.new + get "dump_heap"=> "diagnostics#dump_heap", constraints: AdminConstraint.new end # admin namespace @@ -189,6 +198,7 @@ Discourse::Application.routes.draw do get "session/sso" => "session#sso" get "session/sso_login" => "session#sso_login" + get "session/sso_provider" => "session#sso_provider" get "session/current" => "session#current" get "session/csrf" => "session#csrf" get "composer-messages" => "composer_messages#index" @@ -234,14 +244,12 @@ Discourse::Application.routes.draw do put "users/:username/preferences/badge_title" => "users#badge_title", constraints: {username: USERNAME_ROUTE_FORMAT} get "users/:username/preferences/username" => "users#preferences", constraints: {username: USERNAME_ROUTE_FORMAT} put "users/:username/preferences/username" => "users#username", constraints: {username: USERNAME_ROUTE_FORMAT} - get "users/:username/avatar(/:size)" => "users#avatar", constraints: {username: USERNAME_ROUTE_FORMAT} # LEGACY ROUTE - post "users/:username/preferences/avatar" => "users#upload_avatar", constraints: {username: USERNAME_ROUTE_FORMAT} # LEGACY ROUTE post "users/:username/preferences/user_image" => "users#upload_user_image", constraints: {username: USERNAME_ROUTE_FORMAT} delete "users/:username/preferences/user_image" => "users#destroy_user_image", constraints: {username: USERNAME_ROUTE_FORMAT} put "users/:username/preferences/avatar/pick" => "users#pick_avatar", constraints: {username: USERNAME_ROUTE_FORMAT} get "users/:username/preferences/card-badge" => "users#card_badge", constraints: {username: USERNAME_ROUTE_FORMAT} put "users/:username/preferences/card-badge" => "users#update_card_badge", constraints: {username: USERNAME_ROUTE_FORMAT} - + get "users/:username/staff-info" => "users#staff_info", constraints: {username: USERNAME_ROUTE_FORMAT} get "users/:username/invited" => "users#invited", constraints: {username: USERNAME_ROUTE_FORMAT} post "users/action/send_activation_email" => "users#send_activation_email" @@ -263,6 +271,7 @@ Discourse::Application.routes.draw do get "uploads/:site/:sha" => "uploads#show", constraints: { site: /\w+/, sha: /[a-z0-9]{40}/} post "uploads" => "uploads#create" + get "posts" => "posts#latest" get "posts/by_number/:topic_id/:post_number" => "posts#by_number" get "posts/:id/reply-history" => "posts#reply_history" get "posts/:username/deleted" => "posts#deleted_posts", constraints: {username: USERNAME_ROUTE_FORMAT} @@ -272,6 +281,9 @@ Discourse::Application.routes.draw do get 'members' get 'posts' get 'counts' + + put "members" => "groups#add_members" + delete "members/:username" => "groups#remove_member" end # In case people try the wrong URL @@ -326,6 +338,7 @@ Discourse::Application.routes.draw do post "category/uploads" => "categories#upload" post "category/:category_id/move" => "categories#move" post "category/:category_id/notifications" => "categories#set_notifications" + put "category/:category_id/slug" => "categories#update_slug" get "c/:id/show" => "categories#show" get "c/:category.rss" => "list#category_feed", format: :rss @@ -412,6 +425,8 @@ Discourse::Application.routes.draw do post "t/:topic_id/merge-topic" => "topics#merge_topic", constraints: {topic_id: /\d+/} post "t/:topic_id/change-owner" => "topics#change_post_owners", constraints: {topic_id: /\d+/} delete "t/:topic_id/timings" => "topics#destroy_timings", constraints: {topic_id: /\d+/} + put "t/:topic_id/bookmark" => "topics#bookmark", constraints: {topic_id: /\d+/} + put "t/:topic_id/remove_bookmarks" => "topics#remove_bookmarks", constraints: {topic_id: /\d+/} post "t/:topic_id/notifications" => "topics#set_notifications" , constraints: {topic_id: /\d+/} @@ -433,6 +448,15 @@ Discourse::Application.routes.draw do get "invites/redeem/:token" => "invites#redeem_disposable_invite" delete "invites" => "invites#destroy" + resources :export_csv do + collection do + get "export_entity" => "export_csv#export_entity" + end + member do + get "" => "export_csv#show", constraints: { id: /[^\/]+/ } + end + end + get "onebox" => "onebox#show" get "error" => "forums#error" diff --git a/config/site_settings.yml b/config/site_settings.yml index 284615fe09..bf05e249b4 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -1,13 +1,14 @@ # Available options: # -# default - The default value of the setting. -# client - Set to true if the javascript should have access to this setting's value. -# refresh - Set to true if clients should refresh when the setting is changed. -# min - For a string setting, the minimum length. For an integer setting, the minimum value. -# max - For a string setting, the maximum length. For an integer setting, the maximum value. -# regex - A regex that the value must match. -# enum - The setting has a fixed set of allowed values, and only one can be chosen. -# Set to the class name that defines the set. +# default - The default value of the setting. +# client - Set to true if the javascript should have access to this setting's value. +# refresh - Set to true if clients should refresh when the setting is changed. +# min - For a string setting, the minimum length. For an integer setting, the minimum value. +# max - For a string setting, the maximum length. For an integer setting, the maximum value. +# regex - A regex that the value must match. +# validator - The name of the class that will be use to validate the value of the setting. +# enum - The setting has a fixed set of allowed values, and only one can be chosen. +# Set to the class name that defines the set. # type: email - Must be a valid email address. # type: username - Must match the username of an existing user. # type: list - A list of values, chosen from a set of valid values defined in the choices option. @@ -21,10 +22,11 @@ required: site_description: default: '' contact_email: + client: true default: '' type: email notification_email: - default: 'info@unconfigured.discourse.org' + default: 'noreply@unconfigured.discourse.org' type: email site_contact_username: default: '' @@ -51,6 +53,7 @@ basic: default: 'en' enum: 'LocaleSiteSetting' refresh: true + shadowed_by_global: true allow_user_locale: client: true default: false @@ -82,16 +85,16 @@ basic: client: true refresh: true type: list - default: 'latest|new|unread|starred|top|categories' + default: 'latest|new|unread|top|categories' choices: - latest - new - unread - - starred - top - categories - read - posted + - bookmarks post_menu: client: true type: list @@ -108,7 +111,7 @@ basic: post_menu_hidden_items: client: true type: list - default: 'edit|delete|admin' + default: 'bookmark|edit|delete|admin' choices: - like - edit @@ -131,6 +134,11 @@ basic: client: true type: list default: 'BF1E2E|F1592A|F7941D|9EB83B|3AB54A|12A89D|25AAE2|0E76BD|652D90|92278F|ED207B|8C6238|231F20|808281|B3B5B4|283890' + category_style: + client: true + default: 'bar' + enum: 'CategoryStyleSetting' + preview: 'Category' enable_mobile_theme: client: true default: true @@ -151,10 +159,6 @@ basic: fixed_category_positions: client: true default: false - topics_per_page: - default: 30 - min: 1 - client: true show_subcategory_list: default: false client: true @@ -196,10 +200,10 @@ login: default: false twitter_consumer_key: default: '' - regex: "^[a-zA-Z0-9_+-]*$" + regex: "^[a-zA-Z0-9_+-]+$" twitter_consumer_secret: default: '' - regex: "^[a-zA-Z0-9_+-]*$" + regex: "^[a-zA-Z0-9_+-]+$" enable_facebook_logins: client: true default: false @@ -208,19 +212,20 @@ login: regex: "^\\d+$" facebook_app_secret: default: '' - regex: "^[a-f0-9]*$" + regex: "^[a-f0-9]+$" enable_github_logins: client: true default: false github_client_id: default: '' - regex: "^[a-f0-9]*$" + regex: "^[a-f0-9]+$" github_client_secret: default: '' - regex: "^[a-f0-9]*$" + regex: "^[a-f0-9]+$" enable_sso: client: true default: false + enable_sso_provider: false sso_url: '' sso_secret: '' sso_overrides_email: false @@ -236,6 +241,7 @@ login: default: '' type: list forgot_password_strict: false + log_out_strict: true users: min_username_length: @@ -284,6 +290,10 @@ users: default: false email_token_valid_hours: 24 email_token_grace_period_hours: 0 + purge_unactivated_users_grace_period_days: 7 + public_user_custom_fields: + type: list + default: '' posting: min_post_length: @@ -388,19 +398,34 @@ posting: default: '' refresh: true type: list + enable_emoji: + default: true + client: true + emoji_set: + default: 'emoji_one' + client: true + enum: 'EmojiSetSiteSetting' email: - email_time_window_mins: 10 + email_time_window_mins: + default: 10 + client: true email_posts_context: 5 digest_min_excerpt_length: 100 digest_topics: 20 default_digest_email_frequency: default: 7 enum: 'DigestEmailSiteSetting' + suppress_digest_email_after_days: 365 + disable_digest_emails: + default: false + client: true email_custom_headers: 'Auto-Submitted: auto-generated' email_subject: '[%{site_name}] %{optional_pm}%{optional_cat}%{topic_title}' reply_by_email_enabled: false - reply_by_email_address: '' + reply_by_email_address: + default: '' + validator: "ReplyByEmailAddressValidator" pop3_polling_enabled: false pop3_polling_ssl: true pop3_polling_period_mins: 5 @@ -416,10 +441,11 @@ email: enum: 'TrustLevelSetting' email_prefix: '' email_site_title: '' - disable_emails: false + disable_emails: + default: false + client: true strip_images_from_short_emails: true short_email_length: 2800 - enable_email_names: true files: max_image_size_kb: @@ -467,7 +493,7 @@ files: enum: 'S3RegionSiteSetting' s3_upload_bucket: default: '' - regex: "^[^\\.]*$" + regex: "^[^A-Z._]+$" allow_profile_backgrounds: client: true default: true @@ -577,6 +603,11 @@ spam: min: 0 max: 3 max_new_accounts_per_registration_ip: 3 + min_ban_entries_for_roll_up: 5 + max_age_unmatched_emails: 365 + max_age_unmatched_ips: 365 + num_flaggers_to_close_topic: 5 + num_flags_to_close_topic: 12 rate_limits: unique_posts_mins: @@ -593,7 +624,7 @@ rate_limits: max_bookmarks_per_day: 20 max_flags_per_day: 20 max_edits_per_day: 30 - max_stars_per_day: 20 + max_invites_per_day: 10 max_topics_in_first_day: 5 max_replies_in_first_day: 10 @@ -640,10 +671,6 @@ developer: verbose_localization: default: false client: true - posts_chunksize: - client: true - default: 20 - min: 1 embedding: embeddable_host: '' @@ -786,10 +813,6 @@ uncategorized: notify_about_flags_after: 48 - public_user_custom_fields: - type: list - default: '' - enable_cdn_js_debugging: false show_create_topics_notice: @@ -801,11 +824,6 @@ uncategorized: default: true disable_edit_notifications: false - purge_inactive_users_grace_period_days: 7 - - company_full_name: 'My Unconfigured Forum Ltd.' - company_short_name: 'Unconfigured Forum' - company_domain: 'www.example.com' vacuum_db_days: 90 last_vacuum: diff --git a/db/fixtures/006_badges.rb b/db/fixtures/006_badges.rb index 08c61684d5..93d11a65f4 100644 --- a/db/fixtures/006_badges.rb +++ b/db/fixtures/006_badges.rb @@ -238,6 +238,18 @@ like_badges.each do |spec| end end +Badge.seed do |b| + b.id = Badge::OneYearAnniversary + b.default_name = "Anniversary" + b.default_icon = "fa-clock-o" + b.badge_type_id = BadgeType::Silver + b.query = Badge::Queries::OneYearAnniversary + b.default_badge_grouping_id = BadgeGrouping::Community + b.trigger = Badge::Trigger::None + b.auto_revoke = false + b.system = true +end + Badge.where("NOT system AND id < 100").each do |badge| new_id = [Badge.maximum(:id) + 1, 100].max old_id = badge.id diff --git a/db/fixtures/999_topics.rb b/db/fixtures/999_topics.rb index cf46ccc6e1..1ab472c300 100644 --- a/db/fixtures/999_topics.rb +++ b/db/fixtures/999_topics.rb @@ -27,9 +27,9 @@ unless Rails.env.test? end create_static_page_topic('tos_topic_id', 'tos_topic.title', "tos_topic.body", nil, staff, "terms of service", { - company_domain: SiteSetting.company_domain, - company_full_name: SiteSetting.company_full_name, - company_name: SiteSetting.company_short_name + company_domain: "company_domain", + company_full_name: "company_full_name", + company_name: "company_short_name" }) create_static_page_topic('guidelines_topic_id', 'guidelines_topic.title', "guidelines_topic.body", diff --git a/db/migrate/20130625170842_remove_access_password.rb b/db/migrate/20130625170842_remove_access_password.rb index dba45885c7..31c5a83d4a 100644 --- a/db/migrate/20130625170842_remove_access_password.rb +++ b/db/migrate/20130625170842_remove_access_password.rb @@ -5,7 +5,6 @@ class RemoveAccessPassword < ActiveRecord::Migration execute "DELETE FROM site_settings where name='access_password'" SiteSetting.login_required = true SiteSetting.must_approve_users = true - AdminDashboardData.report_access_password_removal rescue nil end end diff --git a/db/migrate/20141120035016_add_allowed_ips_to_api_keys.rb b/db/migrate/20141120035016_add_allowed_ips_to_api_keys.rb new file mode 100644 index 0000000000..846c889195 --- /dev/null +++ b/db/migrate/20141120035016_add_allowed_ips_to_api_keys.rb @@ -0,0 +1,7 @@ +class AddAllowedIpsToApiKeys < ActiveRecord::Migration + def change + change_table :api_keys do |t| + t.inet :allowed_ips, array: true + end + end +end diff --git a/db/migrate/20141120043401_add_hidden_to_api_keys.rb b/db/migrate/20141120043401_add_hidden_to_api_keys.rb new file mode 100644 index 0000000000..565baa0504 --- /dev/null +++ b/db/migrate/20141120043401_add_hidden_to_api_keys.rb @@ -0,0 +1,7 @@ +class AddHiddenToApiKeys < ActiveRecord::Migration + def change + change_table :api_keys do |t| + t.boolean :hidden, null: false, default: false + end + end +end diff --git a/db/migrate/20141211114517_fix_emoji_path.rb b/db/migrate/20141211114517_fix_emoji_path.rb new file mode 100644 index 0000000000..03c5cd9bdd --- /dev/null +++ b/db/migrate/20141211114517_fix_emoji_path.rb @@ -0,0 +1,19 @@ +class FixEmojiPath < ActiveRecord::Migration + BASE_URL = '/plugins/emoji/images/' + + def up + execute <<-SQL + UPDATE posts + SET cooked = REPLACE(cooked, '#{BASE_URL}', '#{BASE_URL}emoji_one/') + WHERE cooked LIKE '%#{BASE_URL}%' + SQL + end + + def down + execute <<-SQL + UPDATE posts + SET cooked = REPLACE(cooked, '#{BASE_URL}emoji_one/', '#{BASE_URL}') + WHERE cooked LIKE '%#{BASE_URL}emoji_one/%' + SQL + end +end diff --git a/db/migrate/20141216112341_resolve_duplicate_group_names.rb b/db/migrate/20141216112341_resolve_duplicate_group_names.rb new file mode 100644 index 0000000000..63a49e4b60 --- /dev/null +++ b/db/migrate/20141216112341_resolve_duplicate_group_names.rb @@ -0,0 +1,23 @@ +class ResolveDuplicateGroupNames < ActiveRecord::Migration + + def up + results = Group.exec_sql 'SELECT id FROM groups + WHERE name ILIKE + (SELECT lower(name) + FROM groups + GROUP BY lower(name) + HAVING count(*) > 1);' + + groups = Group.where id: results.map { |r| r['id'] } + groups.group_by { |g| g.name.downcase }.each do |key, value| + value.each_with_index do |dup, index| + dup.update! name: "#{dup.name[0..18]}_#{index+1}" if index > 0 + end + end + end + + def down + # does not reverse changes + end + +end diff --git a/db/migrate/20141222051622_remove_override_default_styles_from_site_customizations.rb b/db/migrate/20141222051622_remove_override_default_styles_from_site_customizations.rb new file mode 100644 index 0000000000..c70f570f86 --- /dev/null +++ b/db/migrate/20141222051622_remove_override_default_styles_from_site_customizations.rb @@ -0,0 +1,5 @@ +class RemoveOverrideDefaultStylesFromSiteCustomizations < ActiveRecord::Migration + def change + remove_column :site_customizations, :override_default_style + end +end diff --git a/db/migrate/20141222224220_fix_emoji_path_take2.rb b/db/migrate/20141222224220_fix_emoji_path_take2.rb new file mode 100644 index 0000000000..e0b167d065 --- /dev/null +++ b/db/migrate/20141222224220_fix_emoji_path_take2.rb @@ -0,0 +1,20 @@ +class FixEmojiPathTake2 < ActiveRecord::Migration + OLD_URL = '/plugins/emoji/images/' + NEW_URL = '/images/emoji/' + + def up + execute <<-SQL + UPDATE posts + SET cooked = REPLACE(cooked, '#{OLD_URL}', '#{NEW_URL}') + WHERE cooked LIKE '%#{OLD_URL}%' + SQL + end + + def down + execute <<-SQL + UPDATE posts + SET cooked = REPLACE(cooked, '#{NEW_URL}', '#{OLD_URL}') + WHERE cooked LIKE '%#{NEW_URL}%' + SQL + end +end diff --git a/db/migrate/20141222230707_amend_site_customization.rb b/db/migrate/20141222230707_amend_site_customization.rb new file mode 100644 index 0000000000..70b3067f6f --- /dev/null +++ b/db/migrate/20141222230707_amend_site_customization.rb @@ -0,0 +1,5 @@ +class AmendSiteCustomization < ActiveRecord::Migration + def change + remove_column :site_customizations, :position + end +end diff --git a/db/migrate/20141223145058_create_csv_export_logs.rb b/db/migrate/20141223145058_create_csv_export_logs.rb new file mode 100644 index 0000000000..83291db28b --- /dev/null +++ b/db/migrate/20141223145058_create_csv_export_logs.rb @@ -0,0 +1,9 @@ +class CreateCsvExportLogs < ActiveRecord::Migration + def change + create_table :csv_export_logs do |t| + t.string :export_type, null: false + t.integer :user_id, null: false + t.timestamps + end + end +end diff --git a/db/migrate/20141228151019_rename_csv_export_logs_to_user_exports.rb b/db/migrate/20141228151019_rename_csv_export_logs_to_user_exports.rb new file mode 100644 index 0000000000..12d15bb5e1 --- /dev/null +++ b/db/migrate/20141228151019_rename_csv_export_logs_to_user_exports.rb @@ -0,0 +1,5 @@ +class RenameCsvExportLogsToUserExports < ActiveRecord::Migration + def change + rename_table :csv_export_logs, :user_exports + end +end diff --git a/db/migrate/20150102113309_clean_up_user_history.rb b/db/migrate/20150102113309_clean_up_user_history.rb new file mode 100644 index 0000000000..8ddbe9be05 --- /dev/null +++ b/db/migrate/20150102113309_clean_up_user_history.rb @@ -0,0 +1,10 @@ +class CleanUpUserHistory < ActiveRecord::Migration + def up + # 'checked_for_custom_avatar' is not used anymore + # was removed in https://github.com/discourse/discourse/commit/6c1c8be79433f87bef9d768da7b8fa4ec9bb18d7 + UserHistory.where(action: UserHistory.actions[:checked_for_custom_avatar]).delete_all + end + + def down + end +end diff --git a/db/migrate/20150106215342_remove_stars.rb b/db/migrate/20150106215342_remove_stars.rb new file mode 100644 index 0000000000..7e5f6e44d9 --- /dev/null +++ b/db/migrate/20150106215342_remove_stars.rb @@ -0,0 +1,29 @@ +class RemoveStars < ActiveRecord::Migration + def up + r = execute < 0 + ttl = "'#{ttl.seconds.ago.strftime('%Y-%m-%d %H:%M:%S')}'" + else + ttl = "CURRENT_TIMESTAMP" + end + $redis.del(key) + key.gsub!('temporary_key:', '') + user_id ? "('#{key}', #{user_id}, #{ttl}, #{ttl})" : nil + end + temp_keys.compact! + if temp_keys.present? + execute "INSERT INTO digest_unsubscribe_keys (key, user_id, created_at, updated_at) VALUES #{temp_keys.join(', ')}" + end + end + rescue + # If anything goes wrong, continue with other migrations + end + + def down + drop_table :digest_unsubscribe_keys + end +end diff --git a/docs/ADMIN-QUICK-START-GUIDE.md b/docs/ADMIN-QUICK-START-GUIDE.md index b07a46c1d6..f235ba2654 100644 --- a/docs/ADMIN-QUICK-START-GUIDE.md +++ b/docs/ADMIN-QUICK-START-GUIDE.md @@ -23,15 +23,13 @@ To quickly give your Discourse a distinctive look, without having to edit or und You can also specify custom CSS and custom HTML headers/footers to further customize the look. One common request is a navigation header that takes you back to the parent site. Here is some example HTML to put in [**Customize, CSS/HTML**](/admin/customize/css_html) under "Header": ``` -
- - Home | - About | - News | - Products | - Blog | +
+ Home | + About | + News | + Products | + Blog | Forums -
``` @@ -39,10 +37,14 @@ You can also specify custom CSS and custom HTML headers/footers to further custo Staff members are official representatives of this community. There are two kinds of Staff: -1. **Admins**, who can do anything and configure anything on this site. -2. **Moderators**, who can edit all posts and users, but cannot add categories or change any site settings. +1. **Admins**, who can do anything and configure anything on this site. +2. **Moderators**, who can edit all posts and users, but cannot add categories or change any site settings. -You may want to grant other users staff abilities – to do so click the admin button :wrench: on their user page, then look for the grant buttons. +To add additional staff members: + +- have them sign up on the site (or send out an invitation to join via your user page) +- click the admin button :wrench: on their user page +- look for the Grant Admin and Grant Moderator buttons there ### Private or Public? @@ -69,7 +71,7 @@ If you want to get extra-fancy you can also [set up single-sign on](https://meta Email is required for new account signups and notifications. **Test your email to make sure it is configured correctly!** Visit [the admin email settings](/admin/email), then enter an email address in the "email address to test" field and click send test email. -- You got the test email? Great! **Read that email closely**, it has important email deliverability tips. +- You got the test email? Great! **Read that email closely**, it has important email deliverability tips. - You didn't get the test email? This means your users probably aren't getting any signup or notification emails either. Email deliverability can be hard. We strongly recommend using dedicated email services like [Mandrill](http://mandrill.com), [MailGun](http://www.mailgun.com/), or [MailJet](http://www.mailjet.com/), which offer generous free plans that work fine for most communities. @@ -106,13 +108,13 @@ We strongly recommend sticking with this homepage for small and medium communiti You can change the homepage to the Categories list by editing `top menu` in the [Basic Setup](/admin/site_settings/category/basic) site settings. Change it from the default of -`latest|new|unread|starred|top|categories` +`latest|new|unread|top|categories` to -`categories|latest|new|unread|starred|top` +`categories|latest|new|unread|top` -That is, move categories from the far right to the far left -- that leftmost top menu item is your default homepage. +That is, move categories from the far right to the far left -- that leftmost top menu item is your default homepage. ### Build Your Own FAQ @@ -128,7 +130,7 @@ However, if you want to set up a more detailed FAQ dealing with the specifics of 4. Paste that URL into the `faq url` setting in the admin site settings. This links your topic from the hamburger FAQ menu entry at the top right of every page. -Now you have a community FAQ for your site that is collaboratively editable, and linked from every page on the site. +Now you have a community FAQ for your site that is collaboratively editable, and linked from every page on the site. ### Categories @@ -157,7 +159,7 @@ Basic image uploads work fine out of the box stored locally, provided you have a If your discussion area is be open to the public, new visitors will arrive that are initially strangers to the community. Discourse has a [trust system](https://meta.discourse.org/t/what-do-user-trust-levels-do/4924/2) where users can, over time, earn the trust of the community and gain abilities to assist in governing their community. -Discourse is designed to offer safe defaults for public communities, even with no active moderation. +Discourse is designed to offer safe defaults for public communities, even with no active moderation. > **0 (new) → 1 (basic) → 2 (member) → 3 (regular) → 4 (leader)** @@ -185,7 +187,7 @@ Out of the box, Discourse defaults to [Creative Commons licensing](https://creat > Your users will always retain copyright on their posts, and will always grant the site owner enough rights to include their content on the site. > > Who is allowed to republish the content posted on this forum? -> +> > 1. Only the author > 2. Author and the owner of this forum > 3. Anybody\* @@ -196,7 +198,7 @@ Number 3 is the Discourse default – that's [Creative Commons BY-NC-SA 3.0] ### Building Your Community -Be patient! Building communities is hard. Before launching, be sure to: +Be patient; building communities is hard. Before launching, be sure to: 1. Define your community's purpose in a pinned or banner topic. 2. Seed the discussion with interesting topics. @@ -211,7 +213,7 @@ One way to get people to visit your site is to invite them via email. You can do - The Invite button at the bottom of the topic. - The Invite area on your profile page. - + The invite area on your profile page also includes advanced Staff methods of [sending bulk invites](https://meta.discourse.org/t/sending-bulk-user-invites/16468), and [inviting users into groups](https://meta.discourse.org/t/invite-individual-users-to-a-group/15544). ### Maintenance @@ -225,12 +227,13 @@ The invite area on your profile page also includes advanced Staff methods of [se - Some other things you might eventually want to set up: - [Automatic daily backups](https://meta.discourse.org/t/configure-automatic-backups-for-discourse/14855) - [HTTPS support](https://meta.discourse.org/t/allowing-ssl-for-your-discourse-docker-setup/13847) - - [Content Delivery Network support](https://meta.discourse.org/t/enable-a-cdn-for-your-discourse/14857) - - [Reply via Email](https://meta.discourse.org/t/set-up-reply-via-email-support/14003) + - [Content Delivery Network support](https://meta.discourse.org/t/enable-a-cdn-for-your-discourse/14857) + - [Reply via Email](https://meta.discourse.org/t/set-up-reply-via-email-support/14003) - [Import and Export your data](https://meta.discourse.org/t/move-your-discourse-instance-to-a-different-server/15721) - [Change the domain name](https://meta.discourse.org/t/how-do-i-change-the-domain-name/16098) - [Multiple Discourse instances on the same server](https://meta.discourse.org/t/multisite-configuration-with-docker/14084) - - [Import old content from vBulletin, PHPbb, Vanilla, Drupal, BBPress, etc?](https://github.com/discourse/discourse/tree/master/script/import_scripts)? + - [Import old content from vBulletin, PHPbb, Vanilla, Drupal, BBPress, etc](https://github.com/discourse/discourse/tree/master/script/import_scripts) + - Embed Discourse [in your WordPress install](https://github.com/discourse/wp-discourse), or [on your static HTML site](http://eviltrout.com/2014/01/22/embedding-discourse.html) ### Need more Help? diff --git a/docs/DEVELOPER-ADVANCED.md b/docs/DEVELOPER-ADVANCED.md index 7b5f3ff0ff..4d531a9de1 100644 --- a/docs/DEVELOPER-ADVANCED.md +++ b/docs/DEVELOPER-ADVANCED.md @@ -12,7 +12,7 @@ Note: If you are developing on a Mac, you will probably want to look at [these i 2. Install and configure Redis 2+. 1. Run `redis-server -v` to see if you already have it. 3. Install ImageMagick -4. Install libxml2, g++, and make. +4. Install libxml2, libpq-dev, g++, and make. 5. Install Ruby 2.1.3 and Bundler. 6. Clone the project and bundle. diff --git a/docs/DEVELOPMENT-OSX-NATIVE.md b/docs/DEVELOPMENT-OSX-NATIVE.md index 802c981965..1b2536c23b 100644 --- a/docs/DEVELOPMENT-OSX-NATIVE.md +++ b/docs/DEVELOPMENT-OSX-NATIVE.md @@ -46,7 +46,7 @@ RVM (below) can automatically install homebrew for you with the autolibs setting So, you will need to install Homebrew separately, based on the instructions at the website above, and then run the following from the command line: - brew tap homebrew/versions # roughly the same to adding a repo to apt/sources.list + brew tap homebrew/dupes # roughly the same to adding a repo to apt/sources.list brew install apple-gcc42 gcc-4.2 -v # Test that it's installed and available diff --git a/docs/INSTALL-cloud66.md b/docs/INSTALL-cloud66.md deleted file mode 100644 index da6322c259..0000000000 --- a/docs/INSTALL-cloud66.md +++ /dev/null @@ -1,17 +0,0 @@ -# Deploying on Cloud 66 - -![Logo](http://cdn.cloud66.com/images/easy-deploy.png) - - -Simply follow 7 steps on [building your stack](https://www.cloud66.com/help/first_stack), sign up for a Sendgrid account (for sending emails) and set -the environment variables below to have your own fully functioning Discourse installation up and running. - -Note: Setting environment variables is done during step five, before you click 'deploy': -![Environment variables](http://cdn.cloud66.com/images/environment_variables.png) - -1. SMTP_ADDRESS = your SMTP host -2. SMTP_PORT = the port on your SMTP host -3. SMTP_DOMAIN = the domain you will be sending emails from -4. SMTP_USERNAME = your SMTP username -5. SMTP_PASSWORD = your SMTP password -6. HOST_NAME = the domain hosting your site \ No newline at end of file diff --git a/docs/INSTALL-digital-ocean.md b/docs/INSTALL-digital-ocean.md index be7e008c83..8f728c032d 100644 --- a/docs/INSTALL-digital-ocean.md +++ b/docs/INSTALL-digital-ocean.md @@ -1,4 +1,4 @@ -**Set up Discourse on a cloud server in under 30 minutes** with zero knowledge of Ruby, Rails or Linux shell using our [Discourse Docker image][dd]. We prefer [Digital Ocean][do], although these steps may work on other cloud providers that also support Docker. Let's begin! +**Set up Discourse on a cloud server in under 30 minutes** with zero knowledge of Ruby, Rails or Linux shell using our [Discourse Docker image][dd]. This guide uses [Digital Ocean][do], these steps will work on other cloud providers and local installs that also support Docker. Let's begin! # Create New Digital Ocean Droplet @@ -7,7 +7,7 @@ - Enter your domain `discourse.example.com` as the Droplet name. - The default of **1GB** RAM works fine for small Discourse communities. We do recommend 2 GB RAM for medium communities. - + - The default of **Ubuntu 14.04 LTS x64** works fine. Always select the latest 64-bit [LTS distribution][lts]. Create your new Droplet. You will receive a mail from Digital Ocean with the root password to your Droplet. (However, if you know [how to use SSH keys](https://www.google.com/search?q=digitalocean+ssh+keys), you may not need a password to log in.) @@ -161,12 +161,14 @@ Do you want... - A firewall on your server? [Configure firewall](https://meta.discourse.org/t/configure-a-firewall-for-discourse/20584) +- To embed Discourse [in your WordPress install](https://github.com/discourse/wp-discourse), or [on your static HTML site](http://eviltrout.com/2014/01/22/embedding-discourse.html)? + If anything needs to be improved in this guide, feel free to ask on [meta.discourse.org][meta], or even better, submit a pull request. [dd]: https://github.com/discourse/discourse_docker [man]: https://mandrillapp.com [ssh]: https://help.github.com/articles/generating-ssh-keys - [meta]: https://meta.discourse.org/t/beginners-guide-to-deploy-discourse-on-digital-ocean-using-docker/12156 + [meta]: https://meta.discourse.org [do]: https://www.digitalocean.com/?refcode=5fa48ac82415 [lts]: https://wiki.ubuntu.com/LTS [jet]: http://www.mailjet.com/pricing diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 0b6ea04378..ca051f42bd 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -1,12 +1,12 @@ # How Do I Install Discourse? Simple 30 minute basic install: -[**Beginner Docker install guide for Digital Ocean**][do] +[**Beginner Docker install guide**][basic] Powerful, flexible, large or multiple server install: -[**Advanced Docker install guide**][docker] +[**Advanced Docker install guide**][advanced] -The only officially supported installs of Discourse are the [Docker](https://www.docker.io/) based beginner and advanced installs. We regret that we cannot support any other methods of installation. (Alternately, you can try the [unofficial Heroku install guide][heroku], the [unofficial Ubuntu install guide][ubuntu], the [BitNami Discourse Virtual Machine package][bitnami] or [Cloud66][cloud66].) +The only officially supported installs of Discourse are the [Docker](https://www.docker.io/) based beginner and advanced installs. We regret that we cannot support any other methods of installation. ### Why do you only officially support Docker? @@ -24,16 +24,10 @@ Hosting Rails applications is complicated. Even if you already have Postgres, Re - [Redis 2.6+](http://redis.io/download) - [Ruby 2.0+](http://www.ruby-lang.org/en/downloads/) (we recommend 2.0.0-p353 or higher) - - ## Security We take security very seriously at Discourse, and all our code is 100% open source and peer reviewed. Please read [our security guide](https://github.com/discourse/discourse/blob/master/docs/SECURITY.md) for an overview of security measures in Discourse. -[do]: https://github.com/discourse/discourse/blob/master/docs/INSTALL-digital-ocean.md -[docker]: https://github.com/discourse/discourse_docker -[bitnami]: http://bitnami.com/stack/discourse -[cloud66]: https://github.com/discourse/discourse/blob/master/docs/INSTALL-cloud66.md -[heroku]: https://github.com/discourse/discourse/blob/master/docs/install-HEROKU.md -[ubuntu]: https://github.com/discourse/discourse/blob/master/docs/INSTALL-ubuntu.md +[basic]: https://github.com/discourse/discourse/blob/master/docs/INSTALL-digital-ocean.md +[advanced]: https://github.com/discourse/discourse_docker [swap]: https://meta.discourse.org/t/create-a-swapfile-for-your-linux-server/13880 diff --git a/docs/install-HEROKU.md b/docs/install-HEROKU.md index 3d33c6ac9a..d7c5c8b42e 100644 --- a/docs/install-HEROKU.md +++ b/docs/install-HEROKU.md @@ -1,3 +1,8 @@ +# Warning: This Guide is Deprecated +We only support Docker based installs now. Please see [our **official install guide**](https://github.com/discourse/discourse/blob/master/docs/INSTALL.md) for supported install instructions. + +----- + # Basic Heroku deployment This guide takes you through the steps for deploying Discourse to the [Heroku](http://www.heroku.com/) cloud application platform. If you're unfamiliar with Heroku, [read this first](https://devcenter.heroku.com/articles/quickstart). The basic deployment of Discourse requires several services that will cost you money. In addition to the [750 free Dyno hours](https://devcenter.heroku.com/articles/usage-and-billing) provided by Heroku, the application requires one additional process to be running for the Sidekiq queue ($34 monthly), and a Redis database plan that supports a minimum of 2 databases (average $10 monthly). diff --git a/lib/admin_user_index_query.rb b/lib/admin_user_index_query.rb index cf20f2103c..052e8f0602 100644 --- a/lib/admin_user_index_query.rb +++ b/lib/admin_user_index_query.rb @@ -1,6 +1,7 @@ require_dependency 'trust_level' class AdminUserIndexQuery + def initialize(params = {}, klass = User, trust_levels = TrustLevel.levels) @params = params @query = initialize_query_with_order(klass) @@ -9,12 +10,26 @@ class AdminUserIndexQuery attr_reader :params, :trust_levels + def find_users(limit=100) + find_users_query.includes(:user_stat).limit(limit) + end + + def count_users + find_users_query.count + end + def initialize_query_with_order(klass) + order = [params[:order]] + if params[:query] == "active" - klass.order("COALESCE(last_seen_at, to_date('1970-01-01', 'YYYY-MM-DD')) DESC, username") + order << "COALESCE(last_seen_at, to_date('1970-01-01', 'YYYY-MM-DD')) DESC" else - klass.order("created_at DESC, username") + order << "users.created_at DESC" end + + order << "users.username" + + klass.order(order.reject(&:blank?).join(",")) end def filter_by_trust @@ -24,20 +39,36 @@ class AdminUserIndexQuery end end + def suspect_users + where_conds = [] + + # One signal: no reading yet the user has bio text + where_conds << "user_stats.posts_read_count <= 1 AND user_stats.topics_entered <= 1" + + @query.activated + .references(:user_stats) + .includes(:user_profile) + .where("COALESCE(user_profiles.bio_raw, '') != ''") + .where('users.created_at <= ?', 1.day.ago) + .where(where_conds.map {|c| "(#{c})"}.join(" OR ")) + end + def filter_by_query_classification case params[:query] - when 'admins' then @query.where(admin: true) + when 'staff' then @query.where("admin or moderator") + when 'admins' then @query.where(admin: true) when 'moderators' then @query.where(moderator: true) - when 'blocked' then @query.blocked - when 'suspended' then @query.suspended - when 'pending' then @query.not_suspended.where(approved: false) + when 'blocked' then @query.blocked + when 'suspended' then @query.suspended + when 'pending' then @query.not_suspended.where(approved: false) + when 'suspect' then suspect_users end end def filter_by_search if params[:filter].present? - if params[:filter] =~ Resolv::IPv4::Regex || params[:filter] =~ Resolv::IPv6::Regex - @query.where('ip_address = :ip OR registration_ip_address = :ip', ip: params[:filter]) + if ip = IPAddr.new(params[:filter]) rescue nil + @query.where('ip_address <<= :ip OR registration_ip_address <<= :ip', ip: ip.to_cidr_s) else @query.where('username_lower ILIKE :filter OR email ILIKE :filter', filter: "%#{params[:filter]}%") end @@ -70,15 +101,4 @@ class AdminUserIndexQuery @query end - def find_users - find_users_query.includes(:user_stat) - .includes(:single_sign_on_record) - .includes(:facebook_user_info) - .includes(:twitter_user_info) - .includes(:github_user_info) - .includes(:google_user_info) - .includes(:oauth2_user_info) - .includes(:user_open_ids) - .take(100) - end end diff --git a/lib/auth/default_current_user_provider.rb b/lib/auth/default_current_user_provider.rb index 94c7a8e914..1e5cec773a 100644 --- a/lib/auth/default_current_user_provider.rb +++ b/lib/auth/default_current_user_provider.rb @@ -82,6 +82,11 @@ class Auth::DefaultCurrentUserProvider end def log_off_user(session, cookies) + if SiteSetting.log_out_strict && (user = current_user) + user.auth_token = nil + user.save! + MessageBus.publish "/logout", user.id, user_ids: [user.id] + end cookies[TOKEN_COOKIE] = nil end @@ -107,12 +112,16 @@ class Auth::DefaultCurrentUserProvider api_key = ApiKey.where(key: api_key_value).includes(:user).first if api_key api_username = request["api_username"] + + if api_key.allowed_ips.present? && !api_key.allowed_ips.any?{|ip| ip.include?(request.ip)} + Rails.logger.warn("Unauthorized API access: #{api_username} ip address: #{request.ip}") + return nil + end + if api_key.user api_key.user if !api_username || (api_key.user.username_lower == api_username.downcase) elsif api_username User.find_by(username_lower: api_username.downcase) - else - nil end end end diff --git a/lib/auth/facebook_authenticator.rb b/lib/auth/facebook_authenticator.rb index 3b5ddf5036..b4c48ae674 100644 --- a/lib/auth/facebook_authenticator.rb +++ b/lib/auth/facebook_authenticator.rb @@ -12,7 +12,7 @@ class Auth::FacebookAuthenticator < Auth::Authenticator facebook_hash = session_info[:facebook] result.email = email = session_info[:email] - result.email_valid = true + result.email_valid = !email.blank? result.name = facebook_hash[:name] result.extra_data = facebook_hash diff --git a/lib/auth/result.rb b/lib/auth/result.rb index 3b0a36b5de..505541ea4c 100644 --- a/lib/auth/result.rb +++ b/lib/auth/result.rb @@ -19,12 +19,20 @@ class Auth::Result if requires_invite { requires_invite: true } elsif user - { - authenticated: !!authenticated, - awaiting_activation: !!awaiting_activation, - awaiting_approval: !!awaiting_approval, - not_allowed_from_ip_address: !!not_allowed_from_ip_address - } + if user.suspended? + { + suspended: true, + suspended_message: I18n.t( user.suspend_reason ? "login.suspended_with_reason" : "login.suspended", + {date: I18n.l(user.suspended_till, format: :date_only), reason: user.suspend_reason} ) + } + else + { + authenticated: !!authenticated, + awaiting_activation: !!awaiting_activation, + awaiting_approval: !!awaiting_approval, + not_allowed_from_ip_address: !!not_allowed_from_ip_address + } + end else { email: email, diff --git a/lib/avatar_upload_service.rb b/lib/avatar_upload_service.rb index 860b7a7d55..6cc4db35b1 100644 --- a/lib/avatar_upload_service.rb +++ b/lib/avatar_upload_service.rb @@ -14,9 +14,9 @@ class AvatarUploadService case source when :url tmp = FileHelper.download(file, SiteSetting.max_image_size_kb.kilobytes, "discourse-avatar") - [tmp, File.basename(URI.parse(file).path), File.size(tmp)] + [tmp, File.basename(URI.parse(file).path), tmp.size] when :image - [file.tempfile, file.original_filename, File.size(file.tempfile)] + [file.tempfile, file.original_filename, file.tempfile.size] end end diff --git a/lib/backup_restore/backuper.rb b/lib/backup_restore/backuper.rb index da51b90284..3c0ddaf7d8 100644 --- a/lib/backup_restore/backuper.rb +++ b/lib/backup_restore/backuper.rb @@ -283,6 +283,7 @@ module BackupRestore def clean_up log "Cleaning stuff up..." + remove_tar_leftovers remove_tmp_directory unpause_sidekiq disable_readonly_mode if Discourse.readonly_mode? @@ -290,6 +291,11 @@ module BackupRestore log "Finished!" end + def remove_tar_leftovers + log "Removing '.tar' leftovers..." + `rm -f #{@archive_directory}/*.tar` + end + def remove_tmp_directory log "Removing tmp '#{@tmp_directory}' directory..." FileUtils.rm_rf(@tmp_directory) if Dir[@tmp_directory].present? @@ -321,19 +327,20 @@ module BackupRestore end def log(message) + timestamp = Time.now.strftime("%Y-%m-%d %H:%M:%S") puts(message) rescue nil - publish_log(message) rescue nil - save_log(message) + publish_log(message, timestamp) rescue nil + save_log(message, timestamp) end - def publish_log(message) + def publish_log(message, timestamp) return unless @publish_to_message_bus - data = { timestamp: Time.now, operation: "backup", message: message } + data = { timestamp: timestamp, operation: "backup", message: message } MessageBus.publish(BackupRestore::LOGS_CHANNEL, data, user_ids: [@user_id]) end - def save_log(message) - @logs << "[#{Time.now}] #{message}" + def save_log(message, timestamp) + @logs << "[#{timestamp}] #{message}" end end diff --git a/lib/backup_restore/restorer.rb b/lib/backup_restore/restorer.rb index 8751aecdd2..7193398618 100644 --- a/lib/backup_restore/restorer.rb +++ b/lib/backup_restore/restorer.rb @@ -268,8 +268,8 @@ module BackupRestore end def extract_uploads - log "Extracting uploads..." if `tar --list --file #{@tar_filename} | grep 'uploads/'`.present? + log "Extracting uploads..." FileUtils.cd(File.join(Rails.root, "public")) do `tar --extract --keep-newer-files --file #{@tar_filename} uploads/` end @@ -339,19 +339,20 @@ module BackupRestore end def log(message) + timestamp = Time.now.strftime("%Y-%m-%d %H:%M:%S") puts(message) rescue nil - publish_log(message) rescue nil - save_log(message) + publish_log(message, timestamp) rescue nil + save_log(message, timestamp) end - def publish_log(message) + def publish_log(message, timestamp) return unless @publish_to_message_bus - data = { timestamp: Time.now, operation: "restore", message: message } + data = { timestamp: timestamp, operation: "restore", message: message } MessageBus.publish(BackupRestore::LOGS_CHANNEL, data, user_ids: [@user_id]) end - def save_log(message) - @logs << "[#{Time.now}] #{message}" + def save_log(message, timestamp) + @logs << "[#{timestamp}] #{message}" end end diff --git a/lib/cache.rb b/lib/cache.rb index 819bddfb72..00ca6365a5 100644 --- a/lib/cache.rb +++ b/lib/cache.rb @@ -1,9 +1,15 @@ -# Discourse specific cache supports expire by family missing from standard cache +# Discourse specific cache, enforces 1 day expiry class Cache < ActiveSupport::Cache::Store + # nothing is cached for longer than 1 day EVER + # there is no reason to have data older than this clogging redis + # it is dangerous cause if we rename keys we will be stuck with + # pointless data + MAX_CACHE_AGE = 1.day unless defined? MAX_CACHE_AGE + def initialize(opts = {}) - opts[:namespace] ||= "_CACHE_" + @namespace = opts[:namespace] || "_CACHE_" super(opts) end @@ -11,27 +17,18 @@ class Cache < ActiveSupport::Cache::Store $redis end - def delete_by_family(key) - k = family_key(key, options) - redis.smembers(k).each do |member| - redis.del(member) - end - redis.del(k) - end - def reconnect redis.reconnect end def clear - redis.keys.each do |k| - redis.del(k) if k =~ /^_CACHE_:/ + redis.keys("#{@namespace}:*").each do |k| + redis.del(k) end end def namespaced_key(key, opts=nil) - opts ||= options - super(key,opts) + "#{@namespace}:" << key end protected @@ -47,17 +44,8 @@ class Cache < ActiveSupport::Cache::Store def write_entry(key, entry, options) dumped = Marshal.dump(entry.value) - - if expiry = options[:expires_in] - redis.setex(key, expiry, dumped) - else - redis.set(key, dumped) - end - - if family = family_key(options[:family], options) - redis.sadd(family, key) - end - + expiry = options[:expires_in] || MAX_CACHE_AGE + redis.setex(key, expiry, dumped) true end @@ -65,13 +53,4 @@ class Cache < ActiveSupport::Cache::Store redis.del key end - private - - def family_key(name, options) - if name - key = namespaced_key(name, options) - key << "FAMILY:#{name}" - end - end - end diff --git a/lib/category_badge.rb b/lib/category_badge.rb new file mode 100644 index 0000000000..26ddb74788 --- /dev/null +++ b/lib/category_badge.rb @@ -0,0 +1,28 @@ +module CategoryBadge + + def self.html_for(category, opts=nil) + opts = opts || {} + + # If there is no category, bail + return "" if category.blank? + + # By default hide uncategorized + return "" if category.uncategorized? && !opts[:show_uncategorized] + + category_url = "#{Discourse.base_url}#{category.url}" + + result = "" + + result << " " + + unless category.parent_category_id.nil? + parent_category = Category.find_by(id: category.parent_category_id) + result << " " + end + + result << "#{category.name}" + + "#{result}" + end + +end diff --git a/lib/composer_messages_finder.rb b/lib/composer_messages_finder.rb index 8d2865ac25..e61d0729d8 100644 --- a/lib/composer_messages_finder.rb +++ b/lib/composer_messages_finder.rb @@ -26,9 +26,11 @@ class ComposerMessagesFinder if count < SiteSetting.educate_until_posts education_posts_text = I18n.t('education.until_posts', count: SiteSetting.educate_until_posts) - return {templateName: 'composer/education', - wait_for_typing: true, - body: PrettyText.cook(SiteText.text_for(education_key, education_posts_text: education_posts_text)) } + return { + templateName: 'composer/education', + wait_for_typing: true, + body: PrettyText.cook(SiteText.text_for(education_key, education_posts_text: education_posts_text)) + } end nil @@ -37,7 +39,11 @@ class ComposerMessagesFinder # New users have a limited number of replies in a topic def check_new_user_many_replies return unless replying? && @user.posted_too_much_in_topic?(@details[:topic_id]) - {templateName: 'composer/education', body: PrettyText.cook(I18n.t('education.too_many_replies', newuser_max_replies_per_topic: SiteSetting.newuser_max_replies_per_topic)) } + + { + templateName: 'composer/education', + body: PrettyText.cook(I18n.t('education.too_many_replies', newuser_max_replies_per_topic: SiteSetting.newuser_max_replies_per_topic)) + } end # Should a user be contacted to update their avatar? @@ -49,14 +55,14 @@ class ComposerMessagesFinder # We don't notify users who have avatars or who have been notified already. return if @user.uploaded_avatar_id || UserHistory.exists_for_user?(@user, :notified_about_avatar) - # Finally, we don't check users whose avatars haven't been examined - return unless UserHistory.exists_for_user?(@user, :checked_for_custom_avatar) - # If we got this far, log that we've nagged them about the avatar UserHistory.create!(action: UserHistory.actions[:notified_about_avatar], target_user_id: @user.id ) # Return the message - {templateName: 'composer/education', body: PrettyText.cook(I18n.t('education.avatar', profile_path: "/users/#{@user.username_lower}")) } + { + templateName: 'composer/education', + body: PrettyText.cook(I18n.t('education.avatar', profile_path: "/users/#{@user.username_lower}")) + } end # Is a user replying too much in succession? @@ -87,10 +93,12 @@ class ComposerMessagesFinder target_user_id: @user.id, topic_id: @details[:topic_id] ) - {templateName: 'composer/education', - wait_for_typing: true, - extraClass: 'urgent', - body: PrettyText.cook(I18n.t('education.sequential_replies')) } + { + templateName: 'composer/education', + wait_for_typing: true, + extraClass: 'urgent', + body: PrettyText.cook(I18n.t('education.sequential_replies')) + } end def check_dominating_topic @@ -102,6 +110,7 @@ class ComposerMessagesFinder !UserHistory.exists_for_user?(@user, :notified_about_dominating_topic, topic_id: @details[:topic_id]) topic = Topic.find_by(id: @details[:topic_id]) + return if topic.blank? || topic.user_id == @user.id || topic.posts_count < SiteSetting.summary_posts_required || @@ -117,11 +126,12 @@ class ComposerMessagesFinder target_user_id: @user.id, topic_id: @details[:topic_id]) - - {templateName: 'composer/education', - wait_for_typing: true, - extraClass: 'urgent', - body: PrettyText.cook(I18n.t('education.dominating_topic', percent: (ratio * 100).round)) } + { + templateName: 'composer/education', + wait_for_typing: true, + extraClass: 'urgent', + body: PrettyText.cook(I18n.t('education.dominating_topic', percent: (ratio * 100).round)) + } end def check_reviving_old_topic @@ -136,20 +146,22 @@ class ComposerMessagesFinder topic.last_posted_at.nil? || topic.last_posted_at > SiteSetting.warn_reviving_old_topic_age.days.ago - {templateName: 'composer/education', - wait_for_typing: false, - extraClass: 'urgent', - body: PrettyText.cook(I18n.t('education.reviving_old_topic', days: (Time.zone.now - topic.last_posted_at).round / 1.day)) } + { + templateName: 'composer/education', + wait_for_typing: false, + extraClass: 'urgent', + body: PrettyText.cook(I18n.t('education.reviving_old_topic', days: (Time.zone.now - topic.last_posted_at).round / 1.day)) + } end private def creating_topic? - return @details[:composerAction] == "createTopic" + @details[:composerAction] == "createTopic" end def replying? - return @details[:composerAction] == "reply" + @details[:composerAction] == "reply" end end diff --git a/lib/cooked_post_processor.rb b/lib/cooked_post_processor.rb index 57af1276d5..e58f17bd17 100644 --- a/lib/cooked_post_processor.rb +++ b/lib/cooked_post_processor.rb @@ -264,7 +264,7 @@ class CookedPostProcessor end def available_disk_space - 100 - `df -l . | tail -1 | tr -s ' ' | cut -d ' ' -f 5`.to_i + 100 - `df -P #{Rails.root}/public/uploads | tail -1 | tr -s ' ' | cut -d ' ' -f 5`.to_i end def dirty? diff --git a/lib/crawler_detection.rb b/lib/crawler_detection.rb index d82cf443f6..d4bdac727d 100644 --- a/lib/crawler_detection.rb +++ b/lib/crawler_detection.rb @@ -1,5 +1,7 @@ module CrawlerDetection + # added 'ia_archiver' based on https://meta.discourse.org/t/unable-to-archive-discourse-pages-with-the-internet-archive/21232 + # added 'Wayback Save Page' based on https://meta.discourse.org/t/unable-to-archive-discourse-with-the-internet-archive-save-page-now-button/22875 def self.crawler?(user_agent) - !/Googlebot|Mediapartners|AdsBot|curl|Twitterbot|facebookexternalhit|bingbot|Baiduspider|ia_archiver/.match(user_agent).nil? + !/Googlebot|Mediapartners|AdsBot|curl|Twitterbot|facebookexternalhit|bingbot|Baiduspider|ia_archiver|Wayback Save Page/.match(user_agent).nil? end end diff --git a/lib/current_user.rb b/lib/current_user.rb index 9765857419..dc81ca43ca 100644 --- a/lib/current_user.rb +++ b/lib/current_user.rb @@ -27,7 +27,7 @@ module CurrentUser end def current_user - c = current_user_provider.current_user + current_user_provider.current_user end private diff --git a/lib/discourse.rb b/lib/discourse.rb index 4f5f6528b9..1e2827306d 100644 --- a/lib/discourse.rb +++ b/lib/discourse.rb @@ -56,7 +56,7 @@ module Discourse class CSRF < Exception; end def self.filters - @filters ||= [:latest, :unread, :new, :starred, :read, :posted] + @filters ||= [:latest, :unread, :new, :read, :posted, :bookmarks] end def self.feed_filters @@ -84,8 +84,12 @@ module Discourse @plugins.each { |plugin| plugin.activate! } end + def self.disabled_plugin_names + plugins.select {|p| !p.enabled?}.map(&:name) + end + def self.plugins - @plugins + @plugins ||= [] end def self.assets_digest @@ -114,12 +118,10 @@ module Discourse def self.auth_providers providers = [] - if plugins - plugins.each do |p| - next unless p.auth_providers - p.auth_providers.each do |prov| - providers << prov - end + plugins.each do |p| + next unless p.auth_providers + p.auth_providers.each do |prov| + providers << prov end end providers @@ -168,13 +170,24 @@ module Discourse end def self.enable_readonly_mode - $redis.set readonly_mode_key, 1 + $redis.set(readonly_mode_key, 1) MessageBus.publish(readonly_channel, true) + keep_readonly_mode true end + def self.keep_readonly_mode + # extend the expiry by 1 minute every 30 seconds + Thread.new do + while readonly_mode? + $redis.expire(readonly_mode_key, 1.minute) + sleep 30.seconds + end + end + end + def self.disable_readonly_mode - $redis.del readonly_mode_key + $redis.del(readonly_mode_key) MessageBus.publish(readonly_channel, false) true end @@ -276,21 +289,32 @@ module Discourse nil end - def self.start_connection_reaper(interval=30, age=30) + def self.start_connection_reaper + return if GlobalSetting.connection_reaper_age < 1 || + GlobalSetting.connection_reaper_interval < 1 + # this helps keep connection counts in check Thread.new do while true - sleep interval - pools = [] - ObjectSpace.each_object(ActiveRecord::ConnectionAdapters::ConnectionPool){|pool| pools << pool} - - pools.each do |pool| - pool.drain(age.seconds) + begin + sleep GlobalSetting.connection_reaper_interval + reap_connections(GlobalSetting.connection_reaper_age) + rescue => e + Discourse.handle_exception(e, {message: "Error reaping connections"}) end end end end + def self.reap_connections(age) + pools = [] + ObjectSpace.each_object(ActiveRecord::ConnectionAdapters::ConnectionPool){|pool| pools << pool} + + pools.each do |pool| + pool.drain(age.seconds) + end + end + def self.sidekiq_redis_config { url: $redis.url, namespace: 'sidekiq' } end diff --git a/lib/discourse_plugin_registry.rb b/lib/discourse_plugin_registry.rb index d85eba6a52..af605b2770 100644 --- a/lib/discourse_plugin_registry.rb +++ b/lib/discourse_plugin_registry.rb @@ -4,23 +4,27 @@ class DiscoursePluginRegistry class << self - attr_accessor :javascripts - attr_accessor :server_side_javascripts - attr_accessor :admin_javascripts - attr_accessor :stylesheets - attr_accessor :mobile_stylesheets - attr_accessor :desktop_stylesheets - attr_accessor :sass_variables - attr_accessor :handlebars - attr_accessor :custom_html - attr_accessor :serialized_current_user_fields + attr_writer :javascripts + attr_writer :server_side_javascripts + attr_writer :admin_javascripts + attr_writer :stylesheets + attr_writer :mobile_stylesheets + attr_writer :desktop_stylesheets + attr_writer :sass_variables + attr_writer :handlebars + attr_writer :serialized_current_user_fields + attr_accessor :custom_html # Default accessor values def javascripts @javascripts ||= Set.new end + def asset_globs + @asset_globs ||= Set.new + end + def admin_javascripts @admin_javascripts ||= Set.new end @@ -68,6 +72,38 @@ class DiscoursePluginRegistry Archetype.register(name, options) end + def self.register_glob(root, extension) + self.asset_globs << [root, extension] + end + + def self.register_asset(asset, opts=nil) + if asset =~ /\.js$|\.js\.erb$|\.js\.es6$/ + if opts == :admin + self.admin_javascripts << asset + else + if opts == :server_side + self.server_side_javascripts << asset + end + self.javascripts << asset + end + elsif asset =~ /\.css$|\.scss$/ + if opts == :mobile + self.mobile_stylesheets << asset + elsif opts == :desktop + self.desktop_stylesheets << asset + elsif opts == :variables + self.sass_variables << asset + else + self.stylesheets << asset + end + + elsif asset =~ /\.hbs$/ + self.handlebars << asset + elsif asset =~ /\.js\.handlebars$/ + self.handlebars << asset + end + end + def javascripts self.class.javascripts end @@ -106,6 +142,18 @@ class DiscoursePluginRegistry self.handlebars = nil end + def self.reset! + javascripts.clear + admin_javascripts.clear + server_side_javascripts.clear + stylesheets.clear + mobile_stylesheets.clear + desktop_stylesheets.clear + sass_variables.clear + serialized_current_user_fields + asset_globs.clear + end + def self.setup(plugin_class) registry = DiscoursePluginRegistry.new plugin = plugin_class.new(registry) diff --git a/lib/discourse_redis.rb b/lib/discourse_redis.rb index fe5a695096..a7a17012bf 100644 --- a/lib/discourse_redis.rb +++ b/lib/discourse_redis.rb @@ -63,13 +63,17 @@ class DiscourseRedis @redis.del k end - def keys + def keys(pattern=nil) len = DiscourseRedis.namespace.length + 1 - @redis.keys("#{DiscourseRedis.namespace}:*").map{ + @redis.keys("#{DiscourseRedis.namespace}:#{pattern || '*'}").map{ |k| k[len..-1] } end + def delete_prefixed(prefix) + keys("#{prefix}*").each { |k| $redis.del(k) } + end + def flushdb keys.each{|k| del(k)} end diff --git a/lib/disk_space.rb b/lib/disk_space.rb new file mode 100644 index 0000000000..18f4d1a9fa --- /dev/null +++ b/lib/disk_space.rb @@ -0,0 +1,67 @@ +class DiskSpace + + extend ActionView::Helpers::NumberHelper + + def self.uploads_used_bytes + # used(uploads_path) + # temporary (on our internal setup its just too slow to iterate) + Upload.sum(:filesize).to_i + end + + def self.uploads_free_bytes + free(uploads_path) + end + + def self.backups_used_bytes + used(backups_path) + end + + def self.backups_free_bytes + free(backups_path) + end + + def self.backups_path + Backup.base_directory + end + + def self.uploads_path + "#{Rails.root}/public/uploads/#{RailsMultisite::ConnectionManagement.current_db}" + end + + def self.stats + { + uploads_used: number_to_human_size(uploads_used_bytes), + uploads_free: number_to_human_size(uploads_free_bytes), + backups_used: number_to_human_size(backups_used_bytes), + backups_free: number_to_human_size(backups_free_bytes) + } + end + + def self.cached_stats + stats = $redis.get('disk_space_stats') + updated_at = $redis.get('disk_space_stats_updated') + + unless updated_at && (Time.now.to_i - updated_at.to_i) < 30.minutes + Scheduler::Defer.later "updated stats" do + $redis.set('disk_space_stats_updated', Time.now.to_i) + $redis.set('disk_space_stats', self.stats.to_json) + end + end + + if stats + JSON.parse(stats) + end + + end + + protected + + def self.free(path) + `df -Pk #{path} | awk 'NR==2 {print $4 * 1024;}'`.to_i + end + + def self.used(path) + `du -s #{path}`.to_i * 1024 + end + +end diff --git a/lib/distributed_cache.rb b/lib/distributed_cache.rb index 267c070f00..2ea6f930df 100644 --- a/lib/distributed_cache.rb +++ b/lib/distributed_cache.rb @@ -1,7 +1,6 @@ # Like a hash, just does its best to stay in sync across the farm # On boot all instances are blank, but they populate as various processes # fill it up -# require 'weakref' @@ -31,9 +30,9 @@ class DistributedCache hash = current.hash(message.site_id) case payload["op"] - when "set" then hash[payload["key"]] = payload["value"] + when "set" then hash[payload["key"]] = payload["value"] when "delete" then hash.delete(payload["key"]) - when "clear" then hash.clear + when "clear" then hash.clear end rescue WeakRef::RefError @@ -64,7 +63,7 @@ class DistributedCache def self.publish(hash, message) message[:origin] = hash.object_id message[:hash_key] = hash.key - MessageBus.publish(channel_name, message, {user_ids: [-1]}) + MessageBus.publish(channel_name, message, { user_ids: [-1] }) end def self.set(hash, key, value) @@ -72,11 +71,11 @@ class DistributedCache end def self.delete(hash, key) - publish(hash, { op: :delete, key: key}) + publish(hash, { op: :delete, key: key }) end def self.clear(hash) - publish(hash, {op: :clear}) + publish(hash, { op: :clear }) end def self.register(hash) @@ -93,7 +92,6 @@ class DistributedCache @data = {} end - def []=(k,v) k = k.to_s if Symbol === k DistributedCache.set(self, k, v) @@ -116,7 +114,6 @@ class DistributedCache hash.clear end - def hash(db = nil) db ||= RailsMultisite::ConnectionManagement.current_db @data[db] ||= ThreadSafe::Hash.new diff --git a/lib/email.rb b/lib/email.rb index d0029b3d0c..06413f0f25 100644 --- a/lib/email.rb +++ b/lib/email.rb @@ -23,7 +23,7 @@ module Email def self.cleanup_alias(name) # TODO: I'm sure there are more, but I can't find a list - name ? name.gsub(/[:<>]/, '') : name + name ? name.gsub(/[:<>,]/, '') : name end end diff --git a/lib/email/message_builder.rb b/lib/email/message_builder.rb index cff0e2b640..5e1428d969 100644 --- a/lib/email/message_builder.rb +++ b/lib/email/message_builder.rb @@ -146,10 +146,7 @@ module Email end def private_reply? - SiteSetting.reply_by_email_enabled? && - reply_by_email_address.present? && - @opts[:allow_reply_by_email] && - @opts[:private_reply] + allow_reply_by_email? && @opts[:private_reply] end def from_value diff --git a/lib/email/receiver.rb b/lib/email/receiver.rb index 5d2dfcfa11..bf8f825b25 100644 --- a/lib/email/receiver.rb +++ b/lib/email/receiver.rb @@ -17,6 +17,7 @@ module Email class BadDestinationAddress < ProcessingError; end class TopicNotFoundError < ProcessingError; end class TopicClosedError < ProcessingError; end + class AutoGeneratedEmailError < ProcessingError; end class EmailLogNotFound < ProcessingError; end class InvalidPost < ProcessingError; end @@ -41,6 +42,7 @@ module Email end raise BadDestinationAddress if dest_info[:type] == :invalid + raise AutoGeneratedEmailError if message.header.to_s =~ /auto-generated/ || message.header.to_s =~ /auto-replied/ # TODO get to a state where we can remove this @message = message @@ -117,9 +119,9 @@ module Email if message.multipart? html = fix_charset message.html_part text = fix_charset message.text_part - # TODO picking text if available may be better - # in case of email reply from MS Outlook client, prefer text - if (text && !html) || (text && (message.header.to_s =~ /X-MS-Has-Attach/ || message.header.to_s =~ /Microsoft Outlook/)) + + # prefer plain text + if text return text end elsif message.content_type =~ /text\/html/ @@ -145,7 +147,7 @@ module Email return nil if object.nil? if object.charset - object.body.decoded.force_encoding(object.charset).encode("UTF-8").to_s + object.body.decoded.force_encoding(object.charset.gsub(/utf8/i, "UTF-8")).encode("UTF-8").to_s else object.body.to_s end @@ -223,7 +225,7 @@ module Email # read attachment File.open(tmp.path, "w+b") { |f| f.write attachment.body.decoded } # create the upload for the user - upload = Upload.create_for(user.id, tmp, attachment.filename, File.size(tmp)) + upload = Upload.create_for(user.id, tmp, attachment.filename, tmp.size) if upload && upload.errors.empty? # TODO: should use the same code as the client to insert attachments raw << "\n#{attachment_markdown(upload)}\n" diff --git a/lib/email/sender.rb b/lib/email/sender.rb index 8fcbd1c5f1..d1720aedd9 100644 --- a/lib/email/sender.rb +++ b/lib/email/sender.rb @@ -49,6 +49,13 @@ module Email @message.parts[0].body = @message.parts[0].body.to_s.gsub(/\[\/?email-indent\]/, '') + # Fix relative (ie upload) HTML links in markdown which do not work well in plain text emails. + # These are the links we add when a user uploads a file or image. + # Ideally we would parse general markdown into plain text, but that is almost an intractable problem. + url_prefix = Discourse.base_url + @message.parts[0].body = @message.parts[0].body.to_s.gsub(/([^<]*)<\/a>/, '[\2]('+url_prefix+'\1)') + @message.parts[0].body = @message.parts[0].body.to_s.gsub(/]*)>/, '![]('+url_prefix+'\1)') + @message.text_part.content_type = 'text/plain; charset=UTF-8' # Set up the email log @@ -63,10 +70,16 @@ module Email post_id = header_value('X-Discourse-Post-Id') reply_key = header_value('X-Discourse-Reply-Key') + # always set a default Message ID from the host + uuid = SecureRandom.uuid + @message.header['Message-ID'] = "<#{uuid}@#{host}>" + if topic_id.present? email_log.topic_id = topic_id topic_identifier = "" + post_identifier = "" + @message.header['Message-ID'] = post_identifier @message.header['In-Reply-To'] = topic_identifier @message.header['References'] = topic_identifier @@ -104,9 +117,9 @@ module Email email_log.reply_key = reply_key if reply_key.present? # Remove headers we don't need anymore - @message.header['X-Discourse-Topic-Id'] = nil - @message.header['X-Discourse-Post-Id'] = nil - @message.header['X-Discourse-Reply-Key'] = nil + @message.header['X-Discourse-Topic-Id'] = nil if topic_id.present? + @message.header['X-Discourse-Post-Id'] = nil if post_id.present? + @message.header['X-Discourse-Reply-Key'] = nil if reply_key.present? # Suppress images from short emails if SiteSetting.strip_images_from_short_emails && @message.html_part.body.to_s.bytesize <= SiteSetting.short_email_length && @message.html_part.body =~ /]+>/ diff --git a/lib/email/styles.rb b/lib/email/styles.rb index b3cbeac05a..bc5b2e483b 100644 --- a/lib/email/styles.rb +++ b/lib/email/styles.rb @@ -136,7 +136,7 @@ module Email style('pre', 'word-wrap: break-word; max-width: 694px;') style('code', 'background-color: #f1f1ff; padding: 2px 5px;') style('pre code', 'display: block; background-color: #f1f1ff; padding: 5px;') - style('.featured-topic a', 'text-decoration: none; font-weight: bold; color: #006699; margin-right: 5px') + style('.featured-topic a', 'text-decoration: none; font-weight: bold; color: #006699; line-height:1.5em;') onebox_styles plugin_styles @@ -163,7 +163,7 @@ module Email img.remove end - if img['src'] =~ /plugins\/emoji/ + if img['src'] =~ /images\/emoji/ img.replace img['title'] end end diff --git a/lib/emoji/db.json b/lib/emoji/db.json new file mode 100644 index 0000000000..536a025cfc --- /dev/null +++ b/lib/emoji/db.json @@ -0,0 +1,8122 @@ +[ + { + "emoji": "😄" + , "description": "smiling face with open mouth and smiling eyes" + , "aliases": [ + "smile" + ] + , "tags": [ + "happy" + , "joy" + , "pleased" + ] + } +, { + "emoji": "😃" + , "description": "smiling face with open mouth" + , "aliases": [ + "smiley" + ] + , "tags": [ + "happy" + , "joy" + , "haha" + ] + } +, { + "emoji": "😀" + , "description": "grinning face" + , "aliases": [ + "grinning" + ] + , "tags": [ + "smile" + , "happy" + ] + } +, { + "emoji": "😊" + , "description": "smiling face with smiling eyes" + , "aliases": [ + "blush" + ] + , "tags": [ + "proud" + ] + } +, { + "emoji": "☺️" + , "description": "white smiling face" + , "aliases": [ + "relaxed" + ] + , "tags": [ + "blush" + , "pleased" + ] + } +, { + "emoji": "😉" + , "description": "winking face" + , "aliases": [ + "wink" + ] + , "tags": [ + "flirt" + ] + } +, { + "emoji": "😍" + , "description": "smiling face with heart-shaped eyes" + , "aliases": [ + "heart_eyes" + ] + , "tags": [ + "love" + , "crush" + ] + } +, { + "emoji": "😘" + , "description": "face throwing a kiss" + , "aliases": [ + "kissing_heart" + ] + , "tags": [ + "flirt" + ] + } +, { + "emoji": "😚" + , "description": "kissing face with closed eyes" + , "aliases": [ + "kissing_closed_eyes" + ] + , "tags": [ + ] + } +, { + "emoji": "😗" + , "description": "kissing face" + , "aliases": [ + "kissing" + ] + , "tags": [ + ] + } +, { + "emoji": "😙" + , "description": "kissing face with smiling eyes" + , "aliases": [ + "kissing_smiling_eyes" + ] + , "tags": [ + ] + } +, { + "emoji": "😜" + , "description": "face with stuck-out tongue and winking eye" + , "aliases": [ + "stuck_out_tongue_winking_eye" + ] + , "tags": [ + "prank" + , "silly" + ] + } +, { + "emoji": "😝" + , "description": "face with stuck-out tongue and tightly-closed eyes" + , "aliases": [ + "stuck_out_tongue_closed_eyes" + ] + , "tags": [ + "prank" + ] + } +, { + "emoji": "😛" + , "description": "face with stuck-out tongue" + , "aliases": [ + "stuck_out_tongue" + ] + , "tags": [ + ] + } +, { + "emoji": "😳" + , "description": "flushed face" + , "aliases": [ + "flushed" + ] + , "tags": [ + ] + } +, { + "emoji": "😁" + , "description": "grinning face with smiling eyes" + , "aliases": [ + "grin" + ] + , "tags": [ + ] + } +, { + "emoji": "😔" + , "description": "pensive face" + , "aliases": [ + "pensive" + ] + , "tags": [ + ] + } +, { + "emoji": "😌" + , "description": "relieved face" + , "aliases": [ + "relieved" + ] + , "tags": [ + "whew" + ] + } +, { + "emoji": "😒" + , "description": "unamused face" + , "aliases": [ + "unamused" + ] + , "tags": [ + "meh" + ] + } +, { + "emoji": "😞" + , "description": "disappointed face" + , "aliases": [ + "disappointed" + ] + , "tags": [ + "sad" + ] + } +, { + "emoji": "😣" + , "description": "persevering face" + , "aliases": [ + "persevere" + ] + , "tags": [ + "struggling" + ] + } +, { + "emoji": "😢" + , "description": "crying face" + , "aliases": [ + "cry" + ] + , "tags": [ + "sad" + , "tear" + ] + } +, { + "emoji": "😂" + , "description": "face with tears of joy" + , "aliases": [ + "joy" + ] + , "tags": [ + "tears" + ] + } +, { + "emoji": "😭" + , "description": "loudly crying face" + , "aliases": [ + "sob" + ] + , "tags": [ + "sad" + , "cry" + , "bawling" + ] + } +, { + "emoji": "😪" + , "description": "sleepy face" + , "aliases": [ + "sleepy" + ] + , "tags": [ + "tired" + ] + } +, { + "emoji": "😥" + , "description": "disappointed but relieved face" + , "aliases": [ + "disappointed_relieved" + ] + , "tags": [ + "phew" + , "sweat" + , "nervous" + ] + } +, { + "emoji": "😰" + , "description": "face with open mouth and cold sweat" + , "aliases": [ + "cold_sweat" + ] + , "tags": [ + "nervous" + ] + } +, { + "emoji": "😅" + , "description": "smiling face with open mouth and cold sweat" + , "aliases": [ + "sweat_smile" + ] + , "tags": [ + "hot" + ] + } +, { + "emoji": "😓" + , "description": "face with cold sweat" + , "aliases": [ + "sweat" + ] + , "tags": [ + ] + } +, { + "emoji": "😩" + , "description": "weary face" + , "aliases": [ + "weary" + ] + , "tags": [ + "tired" + ] + } +, { + "emoji": "😫" + , "description": "tired face" + , "aliases": [ + "tired_face" + ] + , "tags": [ + "upset" + , "whine" + ] + } +, { + "emoji": "😨" + , "description": "fearful face" + , "aliases": [ + "fearful" + ] + , "tags": [ + "scared" + , "shocked" + , "oops" + ] + } +, { + "emoji": "😱" + , "description": "face screaming in fear" + , "aliases": [ + "scream" + ] + , "tags": [ + "horror" + , "shocked" + ] + } +, { + "emoji": "😠" + , "description": "angry face" + , "aliases": [ + "angry" + ] + , "tags": [ + "mad" + , "annoyed" + ] + } +, { + "emoji": "😡" + , "description": "pouting face" + , "aliases": [ + "rage" + ] + , "tags": [ + "angry" + ] + } +, { + "emoji": "😤" + , "description": "face with look of triumph" + , "aliases": [ + "triumph" + ] + , "tags": [ + "smug" + ] + } +, { + "emoji": "😖" + , "description": "confounded face" + , "aliases": [ + "confounded" + ] + , "tags": [ + ] + } +, { + "emoji": "😆" + , "description": "smiling face with open mouth and tightly-closed eyes" + , "aliases": [ + "laughing" + , "satisfied" + ] + , "tags": [ + "happy" + , "haha" + ] + } +, { + "emoji": "😋" + , "description": "face savouring delicious food" + , "aliases": [ + "yum" + ] + , "tags": [ + "tongue" + , "lick" + ] + } +, { + "emoji": "😷" + , "description": "face with medical mask" + , "aliases": [ + "mask" + ] + , "tags": [ + "sick" + , "ill" + ] + } +, { + "emoji": "😎" + , "description": "smiling face with sunglasses" + , "aliases": [ + "sunglasses" + ] + , "tags": [ + "cool" + ] + } +, { + "emoji": "😴" + , "description": "sleeping face" + , "aliases": [ + "sleeping" + ] + , "tags": [ + "zzz" + ] + } +, { + "emoji": "😵" + , "description": "dizzy face" + , "aliases": [ + "dizzy_face" + ] + , "tags": [ + ] + } +, { + "emoji": "😲" + , "description": "astonished face" + , "aliases": [ + "astonished" + ] + , "tags": [ + "amazed" + , "gasp" + ] + } +, { + "emoji": "😟" + , "description": "worried face" + , "aliases": [ + "worried" + ] + , "tags": [ + "nervous" + ] + } +, { + "emoji": "😦" + , "description": "frowning face with open mouth" + , "aliases": [ + "frowning" + ] + , "tags": [ + ] + } +, { + "emoji": "😧" + , "description": "anguished face" + , "aliases": [ + "anguished" + ] + , "tags": [ + "stunned" + ] + } +, { + "emoji": "😈" + , "description": "smiling face with horns" + , "aliases": [ + "smiling_imp" + ] + , "tags": [ + "devil" + , "evil" + , "horns" + ] + } +, { + "emoji": "👿" + , "description": "imp" + , "aliases": [ + "imp" + ] + , "tags": [ + "angry" + , "devil" + , "evil" + , "horns" + ] + } +, { + "emoji": "😮" + , "description": "face with open mouth" + , "aliases": [ + "open_mouth" + ] + , "tags": [ + "surprise" + , "impressed" + , "wow" + ] + } +, { + "emoji": "😬" + , "description": "grimacing face" + , "aliases": [ + "grimacing" + ] + , "tags": [ + ] + } +, { + "emoji": "😐" + , "description": "neutral face" + , "aliases": [ + "neutral_face" + ] + , "tags": [ + "meh" + ] + } +, { + "emoji": "😕" + , "description": "confused face" + , "aliases": [ + "confused" + ] + , "tags": [ + ] + } +, { + "emoji": "😯" + , "description": "hushed face" + , "aliases": [ + "hushed" + ] + , "tags": [ + "silence" + , "speechless" + ] + } +, { + "emoji": "😶" + , "description": "face without mouth" + , "aliases": [ + "no_mouth" + ] + , "tags": [ + "mute" + , "silence" + ] + } +, { + "emoji": "😇" + , "description": "smiling face with halo" + , "aliases": [ + "innocent" + ] + , "tags": [ + "angel" + ] + } +, { + "emoji": "😏" + , "description": "smirking face" + , "aliases": [ + "smirk" + ] + , "tags": [ + "smug" + ] + } +, { + "emoji": "😑" + , "description": "expressionless face" + , "aliases": [ + "expressionless" + ] + , "tags": [ + ] + } +, { + "emoji": "👲" + , "description": "man with gua pi mao" + , "aliases": [ + "man_with_gua_pi_mao" + ] + , "tags": [ + ] + } +, { + "emoji": "👳" + , "description": "man with turban" + , "aliases": [ + "man_with_turban" + ] + , "tags": [ + ] + } +, { + "emoji": "👮" + , "description": "police officer" + , "aliases": [ + "cop" + ] + , "tags": [ + "police" + , "law" + ] + } +, { + "emoji": "👷" + , "description": "construction worker" + , "aliases": [ + "construction_worker" + ] + , "tags": [ + "helmet" + ] + } +, { + "emoji": "💂" + , "description": "guardsman" + , "aliases": [ + "guardsman" + ] + , "tags": [ + ] + } +, { + "emoji": "👶" + , "description": "baby" + , "aliases": [ + "baby" + ] + , "tags": [ + "child" + , "newborn" + ] + } +, { + "emoji": "👦" + , "description": "boy" + , "aliases": [ + "boy" + ] + , "tags": [ + "child" + ] + } +, { + "emoji": "👧" + , "description": "girl" + , "aliases": [ + "girl" + ] + , "tags": [ + "child" + ] + } +, { + "emoji": "👨" + , "description": "man" + , "aliases": [ + "man" + ] + , "tags": [ + "mustache" + , "father" + , "dad" + ] + } +, { + "emoji": "👩" + , "description": "woman" + , "aliases": [ + "woman" + ] + , "tags": [ + "girls" + ] + } +, { + "emoji": "👴" + , "description": "older man" + , "aliases": [ + "older_man" + ] + , "tags": [ + ] + } +, { + "emoji": "👵" + , "description": "older woman" + , "aliases": [ + "older_woman" + ] + , "tags": [ + ] + } +, { + "emoji": "👱" + , "description": "person with blond hair" + , "aliases": [ + "person_with_blond_hair" + ] + , "tags": [ + "boy" + ] + } +, { + "emoji": "👼" + , "description": "baby angel" + , "aliases": [ + "angel" + ] + , "tags": [ + ] + } +, { + "emoji": "👸" + , "description": "princess" + , "aliases": [ + "princess" + ] + , "tags": [ + "blonde" + , "crown" + , "royal" + ] + } +, { + "emoji": "😺" + , "description": "smiling cat face with open mouth" + , "aliases": [ + "smiley_cat" + ] + , "tags": [ + ] + } +, { + "emoji": "😸" + , "description": "grinning cat face with smiling eyes" + , "aliases": [ + "smile_cat" + ] + , "tags": [ + ] + } +, { + "emoji": "😻" + , "description": "smiling cat face with heart-shaped eyes" + , "aliases": [ + "heart_eyes_cat" + ] + , "tags": [ + ] + } +, { + "emoji": "😽" + , "description": "kissing cat face with closed eyes" + , "aliases": [ + "kissing_cat" + ] + , "tags": [ + ] + } +, { + "emoji": "😼" + , "description": "cat face with wry smile" + , "aliases": [ + "smirk_cat" + ] + , "tags": [ + ] + } +, { + "emoji": "🙀" + , "description": "weary cat face" + , "aliases": [ + "scream_cat" + ] + , "tags": [ + "horror" + ] + } +, { + "emoji": "😿" + , "description": "crying cat face" + , "aliases": [ + "crying_cat_face" + ] + , "tags": [ + "sad" + , "tear" + ] + } +, { + "emoji": "😹" + , "description": "cat face with tears of joy" + , "aliases": [ + "joy_cat" + ] + , "tags": [ + ] + } +, { + "emoji": "😾" + , "description": "pouting cat face" + , "aliases": [ + "pouting_cat" + ] + , "tags": [ + ] + } +, { + "emoji": "👹" + , "description": "japanese ogre" + , "aliases": [ + "japanese_ogre" + ] + , "tags": [ + "monster" + ] + } +, { + "emoji": "👺" + , "description": "japanese goblin" + , "aliases": [ + "japanese_goblin" + ] + , "tags": [ + ] + } +, { + "emoji": "🙈" + , "description": "see-no-evil monkey" + , "aliases": [ + "see_no_evil" + ] + , "tags": [ + "monkey" + , "blind" + , "ignore" + ] + } +, { + "emoji": "🙉" + , "description": "hear-no-evil monkey" + , "aliases": [ + "hear_no_evil" + ] + , "tags": [ + "monkey" + , "deaf" + ] + } +, { + "emoji": "🙊" + , "description": "speak-no-evil monkey" + , "aliases": [ + "speak_no_evil" + ] + , "tags": [ + "monkey" + , "mute" + , "hush" + ] + } +, { + "emoji": "💀" + , "description": "skull" + , "aliases": [ + "skull" + ] + , "tags": [ + "dead" + , "danger" + , "poison" + ] + } +, { + "emoji": "👽" + , "description": "extraterrestrial alien" + , "aliases": [ + "alien" + ] + , "tags": [ + "ufo" + ] + } +, { + "emoji": "💩" + , "description": "pile of poo" + , "aliases": [ + "hankey" + , "poop" + , "shit" + ] + , "tags": [ + "crap" + ] + } +, { + "emoji": "🔥" + , "description": "fire" + , "aliases": [ + "fire" + ] + , "tags": [ + "burn" + ] + } +, { + "emoji": "✨" + , "description": "sparkles" + , "aliases": [ + "sparkles" + ] + , "tags": [ + "shiny" + ] + } +, { + "emoji": "🌟" + , "description": "glowing star" + , "aliases": [ + "star2" + ] + , "tags": [ + ] + } +, { + "emoji": "💫" + , "description": "dizzy symbol" + , "aliases": [ + "dizzy" + ] + , "tags": [ + "star" + ] + } +, { + "emoji": "💥" + , "description": "collision symbol" + , "aliases": [ + "boom" + , "collision" + ] + , "tags": [ + "explode" + ] + } +, { + "emoji": "💢" + , "description": "anger symbol" + , "aliases": [ + "anger" + ] + , "tags": [ + "angry" + ] + } +, { + "emoji": "💦" + , "description": "splashing sweat symbol" + , "aliases": [ + "sweat_drops" + ] + , "tags": [ + "water" + , "workout" + ] + } +, { + "emoji": "💧" + , "description": "droplet" + , "aliases": [ + "droplet" + ] + , "tags": [ + "water" + ] + } +, { + "emoji": "💤" + , "description": "sleeping symbol" + , "aliases": [ + "zzz" + ] + , "tags": [ + "sleeping" + ] + } +, { + "emoji": "💨" + , "description": "dash symbol" + , "aliases": [ + "dash" + ] + , "tags": [ + "wind" + , "blow" + , "fast" + ] + } +, { + "emoji": "👂" + , "description": "ear" + , "aliases": [ + "ear" + ] + , "tags": [ + "hear" + , "sound" + , "listen" + ] + } +, { + "emoji": "👀" + , "description": "eyes" + , "aliases": [ + "eyes" + ] + , "tags": [ + "look" + , "see" + , "watch" + ] + } +, { + "emoji": "👃" + , "description": "nose" + , "aliases": [ + "nose" + ] + , "tags": [ + "smell" + ] + } +, { + "emoji": "👅" + , "description": "tongue" + , "aliases": [ + "tongue" + ] + , "tags": [ + "taste" + ] + } +, { + "emoji": "👄" + , "description": "mouth" + , "aliases": [ + "lips" + ] + , "tags": [ + "kiss" + ] + } +, { + "emoji": "👍" + , "description": "thumbs up sign" + , "aliases": [ + "+1" + , "thumbsup" + ] + , "tags": [ + "approve" + , "ok" + ] + } +, { + "emoji": "👎" + , "description": "thumbs down sign" + , "aliases": [ + "-1" + , "thumbsdown" + ] + , "tags": [ + "disapprove" + , "bury" + ] + } +, { + "emoji": "👌" + , "description": "ok hand sign" + , "aliases": [ + "ok_hand" + ] + , "tags": [ + ] + } +, { + "emoji": "👊" + , "description": "fisted hand sign" + , "aliases": [ + "facepunch" + , "punch" + ] + , "tags": [ + "attack" + ] + } +, { + "emoji": "✊" + , "description": "raised fist" + , "aliases": [ + "fist" + ] + , "tags": [ + "power" + ] + } +, { + "emoji": "✌️" + , "description": "victory hand" + , "aliases": [ + "v" + ] + , "tags": [ + "victory" + , "peace" + ] + } +, { + "emoji": "👋" + , "description": "waving hand sign" + , "aliases": [ + "wave" + ] + , "tags": [ + "goodbye" + ] + } +, { + "emoji": "✋" + , "description": "raised hand" + , "aliases": [ + "hand" + , "raised_hand" + ] + , "tags": [ + "highfive" + , "stop" + ] + } +, { + "emoji": "👐" + , "description": "open hands sign" + , "aliases": [ + "open_hands" + ] + , "tags": [ + ] + } +, { + "emoji": "👆" + , "description": "white up pointing backhand index" + , "aliases": [ + "point_up_2" + ] + , "tags": [ + ] + } +, { + "emoji": "👇" + , "description": "white down pointing backhand index" + , "aliases": [ + "point_down" + ] + , "tags": [ + ] + } +, { + "emoji": "👉" + , "description": "white right pointing backhand index" + , "aliases": [ + "point_right" + ] + , "tags": [ + ] + } +, { + "emoji": "👈" + , "description": "white left pointing backhand index" + , "aliases": [ + "point_left" + ] + , "tags": [ + ] + } +, { + "emoji": "🙌" + , "description": "person raising both hands in celebration" + , "aliases": [ + "raised_hands" + ] + , "tags": [ + "hooray" + ] + } +, { + "emoji": "🙏" + , "description": "person with folded hands" + , "aliases": [ + "pray" + ] + , "tags": [ + "please" + , "hope" + , "wish" + ] + } +, { + "emoji": "☝️" + , "description": "white up pointing index" + , "aliases": [ + "point_up" + ] + , "tags": [ + ] + } +, { + "emoji": "👏" + , "description": "clapping hands sign" + , "aliases": [ + "clap" + ] + , "tags": [ + "praise" + , "applause" + ] + } +, { + "emoji": "💪" + , "description": "flexed biceps" + , "aliases": [ + "muscle" + ] + , "tags": [ + "flex" + , "bicep" + , "strong" + , "workout" + ] + } +, { + "emoji": "🚶" + , "description": "pedestrian" + , "aliases": [ + "walking" + ] + , "tags": [ + ] + } +, { + "emoji": "🏃" + , "description": "runner" + , "aliases": [ + "runner" + , "running" + ] + , "tags": [ + "exercise" + , "workout" + , "marathon" + ] + } +, { + "emoji": "💃" + , "description": "dancer" + , "aliases": [ + "dancer" + ] + , "tags": [ + "dress" + ] + } +, { + "emoji": "👫" + , "description": "man and woman holding hands" + , "aliases": [ + "couple" + ] + , "tags": [ + "date" + ] + } +, { + "emoji": "👪" + , "description": "family" + , "aliases": [ + "family" + ] + , "tags": [ + "home" + , "parents" + , "child" + ] + } +, { + "emoji": "👬" + , "description": "two men holding hands" + , "aliases": [ + "two_men_holding_hands" + ] + , "tags": [ + "couple" + , "date" + ] + } +, { + "emoji": "👭" + , "description": "two women holding hands" + , "aliases": [ + "two_women_holding_hands" + ] + , "tags": [ + "couple" + , "date" + ] + } +, { + "emoji": "💏" + , "description": "kiss" + , "aliases": [ + "couplekiss" + ] + , "tags": [ + ] + } +, { + "emoji": "💑" + , "description": "couple with heart" + , "aliases": [ + "couple_with_heart" + ] + , "tags": [ + ] + } +, { + "emoji": "👯" + , "description": "woman with bunny ears" + , "aliases": [ + "dancers" + ] + , "tags": [ + "bunny" + ] + } +, { + "emoji": "🙆" + , "description": "face with ok gesture" + , "aliases": [ + "ok_woman" + ] + , "tags": [ + ] + } +, { + "emoji": "🙅" + , "description": "face with no good gesture" + , "aliases": [ + "no_good" + ] + , "tags": [ + "stop" + , "halt" + ] + } +, { + "emoji": "💁" + , "description": "information desk person" + , "aliases": [ + "information_desk_person" + ] + , "tags": [ + ] + } +, { + "emoji": "🙋" + , "description": "happy person raising one hand" + , "aliases": [ + "raising_hand" + ] + , "tags": [ + ] + } +, { + "emoji": "💆" + , "description": "face massage" + , "aliases": [ + "massage" + ] + , "tags": [ + "spa" + ] + } +, { + "emoji": "💇" + , "description": "haircut" + , "aliases": [ + "haircut" + ] + , "tags": [ + "beauty" + ] + } +, { + "emoji": "💅" + , "description": "nail polish" + , "aliases": [ + "nail_care" + ] + , "tags": [ + "beauty" + , "manicure" + ] + } +, { + "emoji": "👰" + , "description": "bride with veil" + , "aliases": [ + "bride_with_veil" + ] + , "tags": [ + "marriage" + , "wedding" + ] + } +, { + "emoji": "🙎" + , "description": "person with pouting face" + , "aliases": [ + "person_with_pouting_face" + ] + , "tags": [ + ] + } +, { + "emoji": "🙍" + , "description": "person frowning" + , "aliases": [ + "person_frowning" + ] + , "tags": [ + "sad" + ] + } +, { + "emoji": "🙇" + , "description": "person bowing deeply" + , "aliases": [ + "bow" + ] + , "tags": [ + "respect" + , "thanks" + ] + } +, { + "emoji": "🎩" + , "description": "top hat" + , "aliases": [ + "tophat" + ] + , "tags": [ + "hat" + , "classy" + ] + } +, { + "emoji": "👑" + , "description": "crown" + , "aliases": [ + "crown" + ] + , "tags": [ + "king" + , "queen" + , "royal" + ] + } +, { + "emoji": "👒" + , "description": "womans hat" + , "aliases": [ + "womans_hat" + ] + , "tags": [ + ] + } +, { + "emoji": "👟" + , "description": "athletic shoe" + , "aliases": [ + "athletic_shoe" + ] + , "tags": [ + "sneaker" + , "sport" + , "running" + ] + } +, { + "emoji": "👞" + , "description": "mans shoe" + , "aliases": [ + "mans_shoe" + , "shoe" + ] + , "tags": [ + ] + } +, { + "emoji": "👡" + , "description": "womans sandal" + , "aliases": [ + "sandal" + ] + , "tags": [ + "shoe" + ] + } +, { + "emoji": "👠" + , "description": "high-heeled shoe" + , "aliases": [ + "high_heel" + ] + , "tags": [ + "shoe" + ] + } +, { + "emoji": "👢" + , "description": "womans boots" + , "aliases": [ + "boot" + ] + , "tags": [ + ] + } +, { + "emoji": "👕" + , "description": "t-shirt" + , "aliases": [ + "shirt" + , "tshirt" + ] + , "tags": [ + ] + } +, { + "emoji": "👔" + , "description": "necktie" + , "aliases": [ + "necktie" + ] + , "tags": [ + "shirt" + , "formal" + ] + } +, { + "emoji": "👚" + , "description": "womans clothes" + , "aliases": [ + "womans_clothes" + ] + , "tags": [ + ] + } +, { + "emoji": "👗" + , "description": "dress" + , "aliases": [ + "dress" + ] + , "tags": [ + ] + } +, { + "emoji": "🎽" + , "description": "running shirt with sash" + , "aliases": [ + "running_shirt_with_sash" + ] + , "tags": [ + "marathon" + ] + } +, { + "emoji": "👖" + , "description": "jeans" + , "aliases": [ + "jeans" + ] + , "tags": [ + "pants" + ] + } +, { + "emoji": "👘" + , "description": "kimono" + , "aliases": [ + "kimono" + ] + , "tags": [ + ] + } +, { + "emoji": "👙" + , "description": "bikini" + , "aliases": [ + "bikini" + ] + , "tags": [ + "beach" + ] + } +, { + "emoji": "💼" + , "description": "briefcase" + , "aliases": [ + "briefcase" + ] + , "tags": [ + "business" + ] + } +, { + "emoji": "👜" + , "description": "handbag" + , "aliases": [ + "handbag" + ] + , "tags": [ + "bag" + ] + } +, { + "emoji": "👝" + , "description": "pouch" + , "aliases": [ + "pouch" + ] + , "tags": [ + "bag" + ] + } +, { + "emoji": "👛" + , "description": "purse" + , "aliases": [ + "purse" + ] + , "tags": [ + ] + } +, { + "emoji": "👓" + , "description": "eyeglasses" + , "aliases": [ + "eyeglasses" + ] + , "tags": [ + "glasses" + ] + } +, { + "emoji": "🎀" + , "description": "ribbon" + , "aliases": [ + "ribbon" + ] + , "tags": [ + ] + } +, { + "emoji": "🌂" + , "description": "closed umbrella" + , "aliases": [ + "closed_umbrella" + ] + , "tags": [ + "weather" + , "rain" + ] + } +, { + "emoji": "💄" + , "description": "lipstick" + , "aliases": [ + "lipstick" + ] + , "tags": [ + "makeup" + ] + } +, { + "emoji": "💛" + , "description": "yellow heart" + , "aliases": [ + "yellow_heart" + ] + , "tags": [ + ] + } +, { + "emoji": "💙" + , "description": "blue heart" + , "aliases": [ + "blue_heart" + ] + , "tags": [ + ] + } +, { + "emoji": "💜" + , "description": "purple heart" + , "aliases": [ + "purple_heart" + ] + , "tags": [ + ] + } +, { + "emoji": "💚" + , "description": "green heart" + , "aliases": [ + "green_heart" + ] + , "tags": [ + ] + } +, { + "emoji": "❤️" + , "description": "heavy black heart" + , "aliases": [ + "heart" + ] + , "tags": [ + "love" + ] + } +, { + "emoji": "💔" + , "description": "broken heart" + , "aliases": [ + "broken_heart" + ] + , "tags": [ + ] + } +, { + "emoji": "💗" + , "description": "growing heart" + , "aliases": [ + "heartpulse" + ] + , "tags": [ + ] + } +, { + "emoji": "💓" + , "description": "beating heart" + , "aliases": [ + "heartbeat" + ] + , "tags": [ + ] + } +, { + "emoji": "💕" + , "description": "two hearts" + , "aliases": [ + "two_hearts" + ] + , "tags": [ + ] + } +, { + "emoji": "💖" + , "description": "sparkling heart" + , "aliases": [ + "sparkling_heart" + ] + , "tags": [ + ] + } +, { + "emoji": "💞" + , "description": "revolving hearts" + , "aliases": [ + "revolving_hearts" + ] + , "tags": [ + ] + } +, { + "emoji": "💘" + , "description": "heart with arrow" + , "aliases": [ + "cupid" + ] + , "tags": [ + "love" + , "heart" + ] + } +, { + "emoji": "💌" + , "description": "love letter" + , "aliases": [ + "love_letter" + ] + , "tags": [ + "email" + , "envelope" + ] + } +, { + "emoji": "💋" + , "description": "kiss mark" + , "aliases": [ + "kiss" + ] + , "tags": [ + "lipstick" + ] + } +, { + "emoji": "💍" + , "description": "ring" + , "aliases": [ + "ring" + ] + , "tags": [ + "wedding" + , "marriage" + , "engaged" + ] + } +, { + "emoji": "💎" + , "description": "gem stone" + , "aliases": [ + "gem" + ] + , "tags": [ + "diamond" + ] + } +, { + "emoji": "👤" + , "description": "bust in silhouette" + , "aliases": [ + "bust_in_silhouette" + ] + , "tags": [ + "user" + ] + } +, { + "emoji": "👥" + , "description": "busts in silhouette" + , "aliases": [ + "busts_in_silhouette" + ] + , "tags": [ + "users" + , "group" + , "team" + ] + } +, { + "emoji": "💬" + , "description": "speech balloon" + , "aliases": [ + "speech_balloon" + ] + , "tags": [ + "comment" + ] + } +, { + "emoji": "👣" + , "description": "footprints" + , "aliases": [ + "footprints" + ] + , "tags": [ + "feet" + , "tracks" + ] + } +, { + "emoji": "💭" + , "description": "thought balloon" + , "aliases": [ + "thought_balloon" + ] + , "tags": [ + "thinking" + ] + } +, { + "emoji": "🐶" + , "description": "dog face" + , "aliases": [ + "dog" + ] + , "tags": [ + "pet" + ] + } +, { + "emoji": "🐺" + , "description": "wolf face" + , "aliases": [ + "wolf" + ] + , "tags": [ + ] + } +, { + "emoji": "🐱" + , "description": "cat face" + , "aliases": [ + "cat" + ] + , "tags": [ + "pet" + ] + } +, { + "emoji": "🐭" + , "description": "mouse face" + , "aliases": [ + "mouse" + ] + , "tags": [ + ] + } +, { + "emoji": "🐹" + , "description": "hamster face" + , "aliases": [ + "hamster" + ] + , "tags": [ + "pet" + ] + } +, { + "emoji": "🐰" + , "description": "rabbit face" + , "aliases": [ + "rabbit" + ] + , "tags": [ + "bunny" + ] + } +, { + "emoji": "🐸" + , "description": "frog face" + , "aliases": [ + "frog" + ] + , "tags": [ + ] + } +, { + "emoji": "🐯" + , "description": "tiger face" + , "aliases": [ + "tiger" + ] + , "tags": [ + ] + } +, { + "emoji": "🐨" + , "description": "koala" + , "aliases": [ + "koala" + ] + , "tags": [ + ] + } +, { + "emoji": "🐻" + , "description": "bear face" + , "aliases": [ + "bear" + ] + , "tags": [ + ] + } +, { + "emoji": "🐷" + , "description": "pig face" + , "aliases": [ + "pig" + ] + , "tags": [ + ] + } +, { + "emoji": "🐽" + , "description": "pig nose" + , "aliases": [ + "pig_nose" + ] + , "tags": [ + ] + } +, { + "emoji": "🐮" + , "description": "cow face" + , "aliases": [ + "cow" + ] + , "tags": [ + ] + } +, { + "emoji": "🐗" + , "description": "boar" + , "aliases": [ + "boar" + ] + , "tags": [ + ] + } +, { + "emoji": "🐵" + , "description": "monkey face" + , "aliases": [ + "monkey_face" + ] + , "tags": [ + ] + } +, { + "emoji": "🐒" + , "description": "monkey" + , "aliases": [ + "monkey" + ] + , "tags": [ + ] + } +, { + "emoji": "🐴" + , "description": "horse face" + , "aliases": [ + "horse" + ] + , "tags": [ + ] + } +, { + "emoji": "🐑" + , "description": "sheep" + , "aliases": [ + "sheep" + ] + , "tags": [ + ] + } +, { + "emoji": "🐘" + , "description": "elephant" + , "aliases": [ + "elephant" + ] + , "tags": [ + ] + } +, { + "emoji": "🐼" + , "description": "panda face" + , "aliases": [ + "panda_face" + ] + , "tags": [ + ] + } +, { + "emoji": "🐧" + , "description": "penguin" + , "aliases": [ + "penguin" + ] + , "tags": [ + ] + } +, { + "emoji": "🐦" + , "description": "bird" + , "aliases": [ + "bird" + ] + , "tags": [ + ] + } +, { + "emoji": "🐤" + , "description": "baby chick" + , "aliases": [ + "baby_chick" + ] + , "tags": [ + ] + } +, { + "emoji": "🐥" + , "description": "front-facing baby chick" + , "aliases": [ + "hatched_chick" + ] + , "tags": [ + ] + } +, { + "emoji": "🐣" + , "description": "hatching chick" + , "aliases": [ + "hatching_chick" + ] + , "tags": [ + ] + } +, { + "emoji": "🐔" + , "description": "chicken" + , "aliases": [ + "chicken" + ] + , "tags": [ + ] + } +, { + "emoji": "🐍" + , "description": "snake" + , "aliases": [ + "snake" + ] + , "tags": [ + ] + } +, { + "emoji": "🐢" + , "description": "turtle" + , "aliases": [ + "turtle" + ] + , "tags": [ + "slow" + ] + } +, { + "emoji": "🐛" + , "description": "bug" + , "aliases": [ + "bug" + ] + , "tags": [ + ] + } +, { + "emoji": "🐝" + , "description": "honeybee" + , "aliases": [ + "bee" + , "honeybee" + ] + , "tags": [ + ] + } +, { + "emoji": "🐜" + , "description": "ant" + , "aliases": [ + "ant" + ] + , "tags": [ + ] + } +, { + "emoji": "🐞" + , "description": "lady beetle" + , "aliases": [ + "beetle" + ] + , "tags": [ + "bug" + ] + } +, { + "emoji": "🐌" + , "description": "snail" + , "aliases": [ + "snail" + ] + , "tags": [ + "slow" + ] + } +, { + "emoji": "🐙" + , "description": "octopus" + , "aliases": [ + "octopus" + ] + , "tags": [ + ] + } +, { + "emoji": "🐚" + , "description": "spiral shell" + , "aliases": [ + "shell" + ] + , "tags": [ + "sea" + , "beach" + ] + } +, { + "emoji": "🐠" + , "description": "tropical fish" + , "aliases": [ + "tropical_fish" + ] + , "tags": [ + ] + } +, { + "emoji": "🐟" + , "description": "fish" + , "aliases": [ + "fish" + ] + , "tags": [ + ] + } +, { + "emoji": "🐬" + , "description": "dolphin" + , "aliases": [ + "dolphin" + , "flipper" + ] + , "tags": [ + ] + } +, { + "emoji": "🐳" + , "description": "spouting whale" + , "aliases": [ + "whale" + ] + , "tags": [ + "sea" + ] + } +, { + "emoji": "🐋" + , "description": "whale" + , "aliases": [ + "whale2" + ] + , "tags": [ + ] + } +, { + "emoji": "🐄" + , "description": "cow" + , "aliases": [ + "cow2" + ] + , "tags": [ + ] + } +, { + "emoji": "🐏" + , "description": "ram" + , "aliases": [ + "ram" + ] + , "tags": [ + ] + } +, { + "emoji": "🐀" + , "description": "rat" + , "aliases": [ + "rat" + ] + , "tags": [ + ] + } +, { + "emoji": "🐃" + , "description": "water buffalo" + , "aliases": [ + "water_buffalo" + ] + , "tags": [ + ] + } +, { + "emoji": "🐅" + , "description": "tiger" + , "aliases": [ + "tiger2" + ] + , "tags": [ + ] + } +, { + "emoji": "🐇" + , "description": "rabbit" + , "aliases": [ + "rabbit2" + ] + , "tags": [ + ] + } +, { + "emoji": "🐉" + , "description": "dragon" + , "aliases": [ + "dragon" + ] + , "tags": [ + ] + } +, { + "emoji": "🐎" + , "description": "horse" + , "aliases": [ + "racehorse" + ] + , "tags": [ + "speed" + ] + } +, { + "emoji": "🐐" + , "description": "goat" + , "aliases": [ + "goat" + ] + , "tags": [ + ] + } +, { + "emoji": "🐓" + , "description": "rooster" + , "aliases": [ + "rooster" + ] + , "tags": [ + ] + } +, { + "emoji": "🐕" + , "description": "dog" + , "aliases": [ + "dog2" + ] + , "tags": [ + ] + } +, { + "emoji": "🐖" + , "description": "pig" + , "aliases": [ + "pig2" + ] + , "tags": [ + ] + } +, { + "emoji": "🐁" + , "description": "mouse" + , "aliases": [ + "mouse2" + ] + , "tags": [ + ] + } +, { + "emoji": "🐂" + , "description": "ox" + , "aliases": [ + "ox" + ] + , "tags": [ + ] + } +, { + "emoji": "🐲" + , "description": "dragon face" + , "aliases": [ + "dragon_face" + ] + , "tags": [ + ] + } +, { + "emoji": "🐡" + , "description": "blowfish" + , "aliases": [ + "blowfish" + ] + , "tags": [ + ] + } +, { + "emoji": "🐊" + , "description": "crocodile" + , "aliases": [ + "crocodile" + ] + , "tags": [ + ] + } +, { + "emoji": "🐫" + , "description": "bactrian camel" + , "aliases": [ + "camel" + ] + , "tags": [ + ] + } +, { + "emoji": "🐪" + , "description": "dromedary camel" + , "aliases": [ + "dromedary_camel" + ] + , "tags": [ + "desert" + ] + } +, { + "emoji": "🐆" + , "description": "leopard" + , "aliases": [ + "leopard" + ] + , "tags": [ + ] + } +, { + "emoji": "🐈" + , "description": "cat" + , "aliases": [ + "cat2" + ] + , "tags": [ + ] + } +, { + "emoji": "🐩" + , "description": "poodle" + , "aliases": [ + "poodle" + ] + , "tags": [ + "dog" + ] + } +, { + "emoji": "🐾" + , "description": "paw prints" + , "aliases": [ + "feet" + , "paw_prints" + ] + , "tags": [ + ] + } +, { + "emoji": "💐" + , "description": "bouquet" + , "aliases": [ + "bouquet" + ] + , "tags": [ + "flowers" + ] + } +, { + "emoji": "🌸" + , "description": "cherry blossom" + , "aliases": [ + "cherry_blossom" + ] + , "tags": [ + "flower" + , "spring" + ] + } +, { + "emoji": "🌷" + , "description": "tulip" + , "aliases": [ + "tulip" + ] + , "tags": [ + "flower" + ] + } +, { + "emoji": "🍀" + , "description": "four leaf clover" + , "aliases": [ + "four_leaf_clover" + ] + , "tags": [ + "luck" + ] + } +, { + "emoji": "🌹" + , "description": "rose" + , "aliases": [ + "rose" + ] + , "tags": [ + "flower" + ] + } +, { + "emoji": "🌻" + , "description": "sunflower" + , "aliases": [ + "sunflower" + ] + , "tags": [ + ] + } +, { + "emoji": "🌺" + , "description": "hibiscus" + , "aliases": [ + "hibiscus" + ] + , "tags": [ + ] + } +, { + "emoji": "🍁" + , "description": "maple leaf" + , "aliases": [ + "maple_leaf" + ] + , "tags": [ + "canada" + ] + } +, { + "emoji": "🍃" + , "description": "leaf fluttering in wind" + , "aliases": [ + "leaves" + ] + , "tags": [ + "leaf" + ] + } +, { + "emoji": "🍂" + , "description": "fallen leaf" + , "aliases": [ + "fallen_leaf" + ] + , "tags": [ + "autumn" + ] + } +, { + "emoji": "🌿" + , "description": "herb" + , "aliases": [ + "herb" + ] + , "tags": [ + ] + } +, { + "emoji": "🌾" + , "description": "ear of rice" + , "aliases": [ + "ear_of_rice" + ] + , "tags": [ + ] + } +, { + "emoji": "🍄" + , "description": "mushroom" + , "aliases": [ + "mushroom" + ] + , "tags": [ + ] + } +, { + "emoji": "🌵" + , "description": "cactus" + , "aliases": [ + "cactus" + ] + , "tags": [ + ] + } +, { + "emoji": "🌴" + , "description": "palm tree" + , "aliases": [ + "palm_tree" + ] + , "tags": [ + ] + } +, { + "emoji": "🌲" + , "description": "evergreen tree" + , "aliases": [ + "evergreen_tree" + ] + , "tags": [ + "wood" + ] + } +, { + "emoji": "🌳" + , "description": "deciduous tree" + , "aliases": [ + "deciduous_tree" + ] + , "tags": [ + "wood" + ] + } +, { + "emoji": "🌰" + , "description": "chestnut" + , "aliases": [ + "chestnut" + ] + , "tags": [ + ] + } +, { + "emoji": "🌱" + , "description": "seedling" + , "aliases": [ + "seedling" + ] + , "tags": [ + "plant" + ] + } +, { + "emoji": "🌼" + , "description": "blossom" + , "aliases": [ + "blossom" + ] + , "tags": [ + ] + } +, { + "emoji": "🌐" + , "description": "globe with meridians" + , "aliases": [ + "globe_with_meridians" + ] + , "tags": [ + "world" + , "global" + , "international" + ] + } +, { + "emoji": "🌞" + , "description": "sun with face" + , "aliases": [ + "sun_with_face" + ] + , "tags": [ + "summer" + ] + } +, { + "emoji": "🌝" + , "description": "full moon with face" + , "aliases": [ + "full_moon_with_face" + ] + , "tags": [ + ] + } +, { + "emoji": "🌚" + , "description": "new moon with face" + , "aliases": [ + "new_moon_with_face" + ] + , "tags": [ + ] + } +, { + "emoji": "🌑" + , "description": "new moon symbol" + , "aliases": [ + "new_moon" + ] + , "tags": [ + ] + } +, { + "emoji": "🌒" + , "description": "waxing crescent moon symbol" + , "aliases": [ + "waxing_crescent_moon" + ] + , "tags": [ + ] + } +, { + "emoji": "🌓" + , "description": "first quarter moon symbol" + , "aliases": [ + "first_quarter_moon" + ] + , "tags": [ + ] + } +, { + "emoji": "🌔" + , "description": "waxing gibbous moon symbol" + , "aliases": [ + "moon" + , "waxing_gibbous_moon" + ] + , "tags": [ + ] + } +, { + "emoji": "🌕" + , "description": "full moon symbol" + , "aliases": [ + "full_moon" + ] + , "tags": [ + ] + } +, { + "emoji": "🌖" + , "description": "waning gibbous moon symbol" + , "aliases": [ + "waning_gibbous_moon" + ] + , "tags": [ + ] + } +, { + "emoji": "🌗" + , "description": "last quarter moon symbol" + , "aliases": [ + "last_quarter_moon" + ] + , "tags": [ + ] + } +, { + "emoji": "🌘" + , "description": "waning crescent moon symbol" + , "aliases": [ + "waning_crescent_moon" + ] + , "tags": [ + ] + } +, { + "emoji": "🌜" + , "description": "last quarter moon with face" + , "aliases": [ + "last_quarter_moon_with_face" + ] + , "tags": [ + ] + } +, { + "emoji": "🌛" + , "description": "first quarter moon with face" + , "aliases": [ + "first_quarter_moon_with_face" + ] + , "tags": [ + ] + } +, { + "emoji": "🌙" + , "description": "crescent moon" + , "aliases": [ + "crescent_moon" + ] + , "tags": [ + "night" + ] + } +, { + "emoji": "🌍" + , "description": "earth globe europe-africa" + , "aliases": [ + "earth_africa" + ] + , "tags": [ + "globe" + , "world" + , "international" + ] + } +, { + "emoji": "🌎" + , "description": "earth globe americas" + , "aliases": [ + "earth_americas" + ] + , "tags": [ + "globe" + , "world" + , "international" + ] + } +, { + "emoji": "🌏" + , "description": "earth globe asia-australia" + , "aliases": [ + "earth_asia" + ] + , "tags": [ + "globe" + , "world" + , "international" + ] + } +, { + "emoji": "🌋" + , "description": "volcano" + , "aliases": [ + "volcano" + ] + , "tags": [ + ] + } +, { + "emoji": "🌌" + , "description": "milky way" + , "aliases": [ + "milky_way" + ] + , "tags": [ + ] + } +, { + "emoji": "🌠" + , "description": "shooting star" + , "aliases": [ + "stars" + ] + , "tags": [ + ] + } +, { + "emoji": "⭐" + , "description": "white medium star" + , "aliases": [ + "star" + ] + , "tags": [ + ] + } +, { + "emoji": "☀️" + , "description": "black sun with rays" + , "aliases": [ + "sunny" + ] + , "tags": [ + "weather" + ] + } +, { + "emoji": "⛅" + , "description": "sun behind cloud" + , "aliases": [ + "partly_sunny" + ] + , "tags": [ + "weather" + , "cloud" + ] + } +, { + "emoji": "☁️" + , "description": "cloud" + , "aliases": [ + "cloud" + ] + , "tags": [ + ] + } +, { + "emoji": "⚡" + , "description": "high voltage sign" + , "aliases": [ + "zap" + ] + , "tags": [ + "lightning" + , "thunder" + ] + } +, { + "emoji": "☔" + , "description": "umbrella with rain drops" + , "aliases": [ + "umbrella" + ] + , "tags": [ + "rain" + , "weather" + ] + } +, { + "emoji": "❄️" + , "description": "snowflake" + , "aliases": [ + "snowflake" + ] + , "tags": [ + "winter" + , "cold" + , "weather" + ] + } +, { + "emoji": "⛄" + , "description": "snowman without snow" + , "aliases": [ + "snowman" + ] + , "tags": [ + "winter" + , "christmas" + ] + } +, { + "emoji": "🌀" + , "description": "cyclone" + , "aliases": [ + "cyclone" + ] + , "tags": [ + "swirl" + ] + } +, { + "emoji": "🌁" + , "description": "foggy" + , "aliases": [ + "foggy" + ] + , "tags": [ + "karl" + ] + } +, { + "emoji": "🌈" + , "description": "rainbow" + , "aliases": [ + "rainbow" + ] + , "tags": [ + "pride" + ] + } +, { + "emoji": "🌊" + , "description": "water wave" + , "aliases": [ + "ocean" + ] + , "tags": [ + "sea" + ] + } +, { + "emoji": "🎍" + , "description": "pine decoration" + , "aliases": [ + "bamboo" + ] + , "tags": [ + ] + } +, { + "emoji": "💝" + , "description": "heart with ribbon" + , "aliases": [ + "gift_heart" + ] + , "tags": [ + "chocolates" + ] + } +, { + "emoji": "🎎" + , "description": "japanese dolls" + , "aliases": [ + "dolls" + ] + , "tags": [ + ] + } +, { + "emoji": "🎒" + , "description": "school satchel" + , "aliases": [ + "school_satchel" + ] + , "tags": [ + ] + } +, { + "emoji": "🎓" + , "description": "graduation cap" + , "aliases": [ + "mortar_board" + ] + , "tags": [ + "education" + , "college" + , "university" + , "graduation" + ] + } +, { + "emoji": "🎏" + , "description": "carp streamer" + , "aliases": [ + "flags" + ] + , "tags": [ + ] + } +, { + "emoji": "🎆" + , "description": "fireworks" + , "aliases": [ + "fireworks" + ] + , "tags": [ + "festival" + , "celebration" + ] + } +, { + "emoji": "🎇" + , "description": "firework sparkler" + , "aliases": [ + "sparkler" + ] + , "tags": [ + ] + } +, { + "emoji": "🎐" + , "description": "wind chime" + , "aliases": [ + "wind_chime" + ] + , "tags": [ + ] + } +, { + "emoji": "🎑" + , "description": "moon viewing ceremony" + , "aliases": [ + "rice_scene" + ] + , "tags": [ + ] + } +, { + "emoji": "🎃" + , "description": "jack-o-lantern" + , "aliases": [ + "jack_o_lantern" + ] + , "tags": [ + "halloween" + ] + } +, { + "emoji": "👻" + , "description": "ghost" + , "aliases": [ + "ghost" + ] + , "tags": [ + "halloween" + ] + } +, { + "emoji": "🎅" + , "description": "father christmas" + , "aliases": [ + "santa" + ] + , "tags": [ + "christmas" + ] + } +, { + "emoji": "🎄" + , "description": "christmas tree" + , "aliases": [ + "christmas_tree" + ] + , "tags": [ + ] + } +, { + "emoji": "🎁" + , "description": "wrapped present" + , "aliases": [ + "gift" + ] + , "tags": [ + "present" + , "birthday" + , "christmas" + ] + } +, { + "emoji": "🎋" + , "description": "tanabata tree" + , "aliases": [ + "tanabata_tree" + ] + , "tags": [ + ] + } +, { + "emoji": "🎉" + , "description": "party popper" + , "aliases": [ + "tada" + ] + , "tags": [ + "party" + ] + } +, { + "emoji": "🎊" + , "description": "confetti ball" + , "aliases": [ + "confetti_ball" + ] + , "tags": [ + ] + } +, { + "emoji": "🎈" + , "description": "balloon" + , "aliases": [ + "balloon" + ] + , "tags": [ + "party" + , "birthday" + ] + } +, { + "emoji": "🎌" + , "description": "crossed flags" + , "aliases": [ + "crossed_flags" + ] + , "tags": [ + ] + } +, { + "emoji": "🔮" + , "description": "crystal ball" + , "aliases": [ + "crystal_ball" + ] + , "tags": [ + "fortune" + ] + } +, { + "emoji": "🎥" + , "description": "movie camera" + , "aliases": [ + "movie_camera" + ] + , "tags": [ + "film" + , "video" + ] + } +, { + "emoji": "📷" + , "description": "camera" + , "aliases": [ + "camera" + ] + , "tags": [ + "photo" + ] + } +, { + "emoji": "📹" + , "description": "video camera" + , "aliases": [ + "video_camera" + ] + , "tags": [ + ] + } +, { + "emoji": "📼" + , "description": "videocassette" + , "aliases": [ + "vhs" + ] + , "tags": [ + ] + } +, { + "emoji": "💿" + , "description": "optical disc" + , "aliases": [ + "cd" + ] + , "tags": [ + ] + } +, { + "emoji": "📀" + , "description": "dvd" + , "aliases": [ + "dvd" + ] + , "tags": [ + ] + } +, { + "emoji": "💽" + , "description": "minidisc" + , "aliases": [ + "minidisc" + ] + , "tags": [ + ] + } +, { + "emoji": "💾" + , "description": "floppy disk" + , "aliases": [ + "floppy_disk" + ] + , "tags": [ + "save" + ] + } +, { + "emoji": "💻" + , "description": "personal computer" + , "aliases": [ + "computer" + ] + , "tags": [ + "desktop" + , "screen" + ] + } +, { + "emoji": "📱" + , "description": "mobile phone" + , "aliases": [ + "iphone" + ] + , "tags": [ + "smartphone" + , "mobile" + ] + } +, { + "emoji": "☎️" + , "description": "black telephone" + , "aliases": [ + "phone" + , "telephone" + ] + , "tags": [ + ] + } +, { + "emoji": "📞" + , "description": "telephone receiver" + , "aliases": [ + "telephone_receiver" + ] + , "tags": [ + "phone" + , "call" + ] + } +, { + "emoji": "📟" + , "description": "pager" + , "aliases": [ + "pager" + ] + , "tags": [ + ] + } +, { + "emoji": "📠" + , "description": "fax machine" + , "aliases": [ + "fax" + ] + , "tags": [ + ] + } +, { + "emoji": "📡" + , "description": "satellite antenna" + , "aliases": [ + "satellite" + ] + , "tags": [ + "signal" + ] + } +, { + "emoji": "📺" + , "description": "television" + , "aliases": [ + "tv" + ] + , "tags": [ + ] + } +, { + "emoji": "📻" + , "description": "radio" + , "aliases": [ + "radio" + ] + , "tags": [ + "podcast" + ] + } +, { + "emoji": "🔊" + , "description": "speaker with three sound waves" + , "aliases": [ + "loud_sound" + ] + , "tags": [ + "volume" + ] + } +, { + "emoji": "🔉" + , "description": "speaker with one sound wave" + , "aliases": [ + "sound" + ] + , "tags": [ + "volume" + ] + } +, { + "emoji": "🔈" + , "description": "speaker" + , "aliases": [ + "speaker" + ] + , "tags": [ + ] + } +, { + "emoji": "🔇" + , "description": "speaker with cancellation stroke" + , "aliases": [ + "mute" + ] + , "tags": [ + "sound" + , "volume" + ] + } +, { + "emoji": "🔔" + , "description": "bell" + , "aliases": [ + "bell" + ] + , "tags": [ + "sound" + , "notification" + ] + } +, { + "emoji": "🔕" + , "description": "bell with cancellation stroke" + , "aliases": [ + "no_bell" + ] + , "tags": [ + "volume" + , "off" + ] + } +, { + "emoji": "📢" + , "description": "public address loudspeaker" + , "aliases": [ + "loudspeaker" + ] + , "tags": [ + "announcement" + ] + } +, { + "emoji": "📣" + , "description": "cheering megaphone" + , "aliases": [ + "mega" + ] + , "tags": [ + ] + } +, { + "emoji": "⏳" + , "description": "hourglass with flowing sand" + , "aliases": [ + "hourglass_flowing_sand" + ] + , "tags": [ + "time" + ] + } +, { + "emoji": "⌛" + , "description": "hourglass" + , "aliases": [ + "hourglass" + ] + , "tags": [ + "time" + ] + } +, { + "emoji": "⏰" + , "description": "alarm clock" + , "aliases": [ + "alarm_clock" + ] + , "tags": [ + "morning" + ] + } +, { + "emoji": "⌚" + , "description": "watch" + , "aliases": [ + "watch" + ] + , "tags": [ + "time" + ] + } +, { + "emoji": "🔓" + , "description": "open lock" + , "aliases": [ + "unlock" + ] + , "tags": [ + "security" + ] + } +, { + "emoji": "🔒" + , "description": "lock" + , "aliases": [ + "lock" + ] + , "tags": [ + "security" + , "private" + ] + } +, { + "emoji": "🔏" + , "description": "lock with ink pen" + , "aliases": [ + "lock_with_ink_pen" + ] + , "tags": [ + ] + } +, { + "emoji": "🔐" + , "description": "closed lock with key" + , "aliases": [ + "closed_lock_with_key" + ] + , "tags": [ + "security" + ] + } +, { + "emoji": "🔑" + , "description": "key" + , "aliases": [ + "key" + ] + , "tags": [ + "lock" + , "password" + ] + } +, { + "emoji": "🔎" + , "description": "right-pointing magnifying glass" + , "aliases": [ + "mag_right" + ] + , "tags": [ + ] + } +, { + "emoji": "💡" + , "description": "electric light bulb" + , "aliases": [ + "bulb" + ] + , "tags": [ + "idea" + , "light" + ] + } +, { + "emoji": "🔦" + , "description": "electric torch" + , "aliases": [ + "flashlight" + ] + , "tags": [ + ] + } +, { + "emoji": "🔆" + , "description": "high brightness symbol" + , "aliases": [ + "high_brightness" + ] + , "tags": [ + ] + } +, { + "emoji": "🔅" + , "description": "low brightness symbol" + , "aliases": [ + "low_brightness" + ] + , "tags": [ + ] + } +, { + "emoji": "🔌" + , "description": "electric plug" + , "aliases": [ + "electric_plug" + ] + , "tags": [ + ] + } +, { + "emoji": "🔋" + , "description": "battery" + , "aliases": [ + "battery" + ] + , "tags": [ + "power" + ] + } +, { + "emoji": "🔍" + , "description": "left-pointing magnifying glass" + , "aliases": [ + "mag" + ] + , "tags": [ + "search" + , "zoom" + ] + } +, { + "emoji": "🛁" + , "description": "bathtub" + , "aliases": [ + "bathtub" + ] + , "tags": [ + ] + } +, { + "emoji": "🛀" + , "description": "bath" + , "aliases": [ + "bath" + ] + , "tags": [ + "shower" + ] + } +, { + "emoji": "🚿" + , "description": "shower" + , "aliases": [ + "shower" + ] + , "tags": [ + "bath" + ] + } +, { + "emoji": "🚽" + , "description": "toilet" + , "aliases": [ + "toilet" + ] + , "tags": [ + "wc" + ] + } +, { + "emoji": "🔧" + , "description": "wrench" + , "aliases": [ + "wrench" + ] + , "tags": [ + "tool" + ] + } +, { + "emoji": "🔩" + , "description": "nut and bolt" + , "aliases": [ + "nut_and_bolt" + ] + , "tags": [ + ] + } +, { + "emoji": "🔨" + , "description": "hammer" + , "aliases": [ + "hammer" + ] + , "tags": [ + "tool" + ] + } +, { + "emoji": "🚪" + , "description": "door" + , "aliases": [ + "door" + ] + , "tags": [ + ] + } +, { + "emoji": "🚬" + , "description": "smoking symbol" + , "aliases": [ + "smoking" + ] + , "tags": [ + "cigarette" + ] + } +, { + "emoji": "💣" + , "description": "bomb" + , "aliases": [ + "bomb" + ] + , "tags": [ + "boom" + ] + } +, { + "emoji": "🔫" + , "description": "pistol" + , "aliases": [ + "gun" + ] + , "tags": [ + "shoot" + , "weapon" + ] + } +, { + "emoji": "🔪" + , "description": "hocho" + , "aliases": [ + "hocho" + , "knife" + ] + , "tags": [ + "cut" + , "chop" + ] + } +, { + "emoji": "💊" + , "description": "pill" + , "aliases": [ + "pill" + ] + , "tags": [ + "health" + , "medicine" + ] + } +, { + "emoji": "💉" + , "description": "syringe" + , "aliases": [ + "syringe" + ] + , "tags": [ + "health" + , "hospital" + , "needle" + ] + } +, { + "emoji": "💰" + , "description": "money bag" + , "aliases": [ + "moneybag" + ] + , "tags": [ + "dollar" + , "cream" + ] + } +, { + "emoji": "💴" + , "description": "banknote with yen sign" + , "aliases": [ + "yen" + ] + , "tags": [ + ] + } +, { + "emoji": "💵" + , "description": "banknote with dollar sign" + , "aliases": [ + "dollar" + ] + , "tags": [ + "money" + ] + } +, { + "emoji": "💷" + , "description": "banknote with pound sign" + , "aliases": [ + "pound" + ] + , "tags": [ + ] + } +, { + "emoji": "💶" + , "description": "banknote with euro sign" + , "aliases": [ + "euro" + ] + , "tags": [ + ] + } +, { + "emoji": "💳" + , "description": "credit card" + , "aliases": [ + "credit_card" + ] + , "tags": [ + "subscription" + ] + } +, { + "emoji": "💸" + , "description": "money with wings" + , "aliases": [ + "money_with_wings" + ] + , "tags": [ + "dollar" + ] + } +, { + "emoji": "📲" + , "description": "mobile phone with rightwards arrow at left" + , "aliases": [ + "calling" + ] + , "tags": [ + "call" + , "incoming" + ] + } +, { + "emoji": "📧" + , "description": "e-mail symbol" + , "aliases": [ + "e-mail" + ] + , "tags": [ + ] + } +, { + "emoji": "📥" + , "description": "inbox tray" + , "aliases": [ + "inbox_tray" + ] + , "tags": [ + ] + } +, { + "emoji": "📤" + , "description": "outbox tray" + , "aliases": [ + "outbox_tray" + ] + , "tags": [ + ] + } +, { + "emoji": "✉️" + , "description": "envelope" + , "aliases": [ + "email" + , "envelope" + ] + , "tags": [ + "letter" + ] + } +, { + "emoji": "📩" + , "description": "envelope with downwards arrow above" + , "aliases": [ + "envelope_with_arrow" + ] + , "tags": [ + ] + } +, { + "emoji": "📨" + , "description": "incoming envelope" + , "aliases": [ + "incoming_envelope" + ] + , "tags": [ + ] + } +, { + "emoji": "📯" + , "description": "postal horn" + , "aliases": [ + "postal_horn" + ] + , "tags": [ + ] + } +, { + "emoji": "📫" + , "description": "closed mailbox with raised flag" + , "aliases": [ + "mailbox" + ] + , "tags": [ + ] + } +, { + "emoji": "📪" + , "description": "closed mailbox with lowered flag" + , "aliases": [ + "mailbox_closed" + ] + , "tags": [ + ] + } +, { + "emoji": "📬" + , "description": "open mailbox with raised flag" + , "aliases": [ + "mailbox_with_mail" + ] + , "tags": [ + ] + } +, { + "emoji": "📭" + , "description": "open mailbox with lowered flag" + , "aliases": [ + "mailbox_with_no_mail" + ] + , "tags": [ + ] + } +, { + "emoji": "📮" + , "description": "postbox" + , "aliases": [ + "postbox" + ] + , "tags": [ + ] + } +, { + "emoji": "📦" + , "description": "package" + , "aliases": [ + "package" + ] + , "tags": [ + "shipping" + ] + } +, { + "emoji": "📝" + , "description": "memo" + , "aliases": [ + "memo" + , "pencil" + ] + , "tags": [ + "document" + , "note" + ] + } +, { + "emoji": "📄" + , "description": "page facing up" + , "aliases": [ + "page_facing_up" + ] + , "tags": [ + "document" + ] + } +, { + "emoji": "📃" + , "description": "page with curl" + , "aliases": [ + "page_with_curl" + ] + , "tags": [ + ] + } +, { + "emoji": "📑" + , "description": "bookmark tabs" + , "aliases": [ + "bookmark_tabs" + ] + , "tags": [ + ] + } +, { + "emoji": "📊" + , "description": "bar chart" + , "aliases": [ + "bar_chart" + ] + , "tags": [ + "stats" + , "metrics" + ] + } +, { + "emoji": "📈" + , "description": "chart with upwards trend" + , "aliases": [ + "chart_with_upwards_trend" + ] + , "tags": [ + "graph" + , "metrics" + ] + } +, { + "emoji": "📉" + , "description": "chart with downwards trend" + , "aliases": [ + "chart_with_downwards_trend" + ] + , "tags": [ + "graph" + , "metrics" + ] + } +, { + "emoji": "📜" + , "description": "scroll" + , "aliases": [ + "scroll" + ] + , "tags": [ + "document" + ] + } +, { + "emoji": "📋" + , "description": "clipboard" + , "aliases": [ + "clipboard" + ] + , "tags": [ + ] + } +, { + "emoji": "📅" + , "description": "calendar" + , "aliases": [ + "date" + ] + , "tags": [ + "calendar" + , "schedule" + ] + } +, { + "emoji": "📆" + , "description": "tear-off calendar" + , "aliases": [ + "calendar" + ] + , "tags": [ + "schedule" + ] + } +, { + "emoji": "📇" + , "description": "card index" + , "aliases": [ + "card_index" + ] + , "tags": [ + ] + } +, { + "emoji": "📁" + , "description": "file folder" + , "aliases": [ + "file_folder" + ] + , "tags": [ + "directory" + ] + } +, { + "emoji": "📂" + , "description": "open file folder" + , "aliases": [ + "open_file_folder" + ] + , "tags": [ + ] + } +, { + "emoji": "✂️" + , "description": "black scissors" + , "aliases": [ + "scissors" + ] + , "tags": [ + "cut" + ] + } +, { + "emoji": "📌" + , "description": "pushpin" + , "aliases": [ + "pushpin" + ] + , "tags": [ + "location" + ] + } +, { + "emoji": "📎" + , "description": "paperclip" + , "aliases": [ + "paperclip" + ] + , "tags": [ + ] + } +, { + "emoji": "✒️" + , "description": "black nib" + , "aliases": [ + "black_nib" + ] + , "tags": [ + ] + } +, { + "emoji": "✏️" + , "description": "pencil" + , "aliases": [ + "pencil2" + ] + , "tags": [ + ] + } +, { + "emoji": "📏" + , "description": "straight ruler" + , "aliases": [ + "straight_ruler" + ] + , "tags": [ + ] + } +, { + "emoji": "📐" + , "description": "triangular ruler" + , "aliases": [ + "triangular_ruler" + ] + , "tags": [ + ] + } +, { + "emoji": "📕" + , "description": "closed book" + , "aliases": [ + "closed_book" + ] + , "tags": [ + ] + } +, { + "emoji": "📗" + , "description": "green book" + , "aliases": [ + "green_book" + ] + , "tags": [ + ] + } +, { + "emoji": "📘" + , "description": "blue book" + , "aliases": [ + "blue_book" + ] + , "tags": [ + ] + } +, { + "emoji": "📙" + , "description": "orange book" + , "aliases": [ + "orange_book" + ] + , "tags": [ + ] + } +, { + "emoji": "📓" + , "description": "notebook" + , "aliases": [ + "notebook" + ] + , "tags": [ + ] + } +, { + "emoji": "📔" + , "description": "notebook with decorative cover" + , "aliases": [ + "notebook_with_decorative_cover" + ] + , "tags": [ + ] + } +, { + "emoji": "📒" + , "description": "ledger" + , "aliases": [ + "ledger" + ] + , "tags": [ + ] + } +, { + "emoji": "📚" + , "description": "books" + , "aliases": [ + "books" + ] + , "tags": [ + "library" + ] + } +, { + "emoji": "📖" + , "description": "open book" + , "aliases": [ + "book" + , "open_book" + ] + , "tags": [ + ] + } +, { + "emoji": "🔖" + , "description": "bookmark" + , "aliases": [ + "bookmark" + ] + , "tags": [ + ] + } +, { + "emoji": "📛" + , "description": "name badge" + , "aliases": [ + "name_badge" + ] + , "tags": [ + ] + } +, { + "emoji": "🔬" + , "description": "microscope" + , "aliases": [ + "microscope" + ] + , "tags": [ + "science" + , "laboratory" + , "investigate" + ] + } +, { + "emoji": "🔭" + , "description": "telescope" + , "aliases": [ + "telescope" + ] + , "tags": [ + ] + } +, { + "emoji": "📰" + , "description": "newspaper" + , "aliases": [ + "newspaper" + ] + , "tags": [ + "press" + ] + } +, { + "emoji": "🎨" + , "description": "artist palette" + , "aliases": [ + "art" + ] + , "tags": [ + "design" + , "paint" + ] + } +, { + "emoji": "🎬" + , "description": "clapper board" + , "aliases": [ + "clapper" + ] + , "tags": [ + "film" + ] + } +, { + "emoji": "🎤" + , "description": "microphone" + , "aliases": [ + "microphone" + ] + , "tags": [ + "sing" + ] + } +, { + "emoji": "🎧" + , "description": "headphone" + , "aliases": [ + "headphones" + ] + , "tags": [ + "music" + , "earphones" + ] + } +, { + "emoji": "🎼" + , "description": "musical score" + , "aliases": [ + "musical_score" + ] + , "tags": [ + ] + } +, { + "emoji": "🎵" + , "description": "musical note" + , "aliases": [ + "musical_note" + ] + , "tags": [ + ] + } +, { + "emoji": "🎶" + , "description": "multiple musical notes" + , "aliases": [ + "notes" + ] + , "tags": [ + "music" + ] + } +, { + "emoji": "🎹" + , "description": "musical keyboard" + , "aliases": [ + "musical_keyboard" + ] + , "tags": [ + "piano" + ] + } +, { + "emoji": "🎻" + , "description": "violin" + , "aliases": [ + "violin" + ] + , "tags": [ + ] + } +, { + "emoji": "🎺" + , "description": "trumpet" + , "aliases": [ + "trumpet" + ] + , "tags": [ + ] + } +, { + "emoji": "🎷" + , "description": "saxophone" + , "aliases": [ + "saxophone" + ] + , "tags": [ + ] + } +, { + "emoji": "🎸" + , "description": "guitar" + , "aliases": [ + "guitar" + ] + , "tags": [ + "rock" + ] + } +, { + "emoji": "👾" + , "description": "alien monster" + , "aliases": [ + "space_invader" + ] + , "tags": [ + "game" + , "retro" + ] + } +, { + "emoji": "🎮" + , "description": "video game" + , "aliases": [ + "video_game" + ] + , "tags": [ + "play" + , "controller" + , "console" + ] + } +, { + "emoji": "🃏" + , "description": "playing card black joker" + , "aliases": [ + "black_joker" + ] + , "tags": [ + ] + } +, { + "emoji": "🎴" + , "description": "flower playing cards" + , "aliases": [ + "flower_playing_cards" + ] + , "tags": [ + ] + } +, { + "emoji": "🀄" + , "description": "mahjong tile red dragon" + , "aliases": [ + "mahjong" + ] + , "tags": [ + ] + } +, { + "emoji": "🎲" + , "description": "game die" + , "aliases": [ + "game_die" + ] + , "tags": [ + "dice" + , "gambling" + ] + } +, { + "emoji": "🎯" + , "description": "direct hit" + , "aliases": [ + "dart" + ] + , "tags": [ + "target" + ] + } +, { + "emoji": "🏈" + , "description": "american football" + , "aliases": [ + "football" + ] + , "tags": [ + "sports" + ] + } +, { + "emoji": "🏀" + , "description": "basketball and hoop" + , "aliases": [ + "basketball" + ] + , "tags": [ + "sports" + ] + } +, { + "emoji": "⚽" + , "description": "soccer ball" + , "aliases": [ + "soccer" + ] + , "tags": [ + "sports" + ] + } +, { + "emoji": "⚾️" + , "description": "baseball" + , "aliases": [ + "baseball" + ] + , "tags": [ + "sports" + ] + } +, { + "emoji": "🎾" + , "description": "tennis racquet and ball" + , "aliases": [ + "tennis" + ] + , "tags": [ + "sports" + ] + } +, { + "emoji": "🎱" + , "description": "billiards" + , "aliases": [ + "8ball" + ] + , "tags": [ + "pool" + , "billiards" + ] + } +, { + "emoji": "🏉" + , "description": "rugby football" + , "aliases": [ + "rugby_football" + ] + , "tags": [ + ] + } +, { + "emoji": "🎳" + , "description": "bowling" + , "aliases": [ + "bowling" + ] + , "tags": [ + ] + } +, { + "emoji": "⛳" + , "description": "flag in hole" + , "aliases": [ + "golf" + ] + , "tags": [ + ] + } +, { + "emoji": "🚵" + , "description": "mountain bicyclist" + , "aliases": [ + "mountain_bicyclist" + ] + , "tags": [ + ] + } +, { + "emoji": "🚴" + , "description": "bicyclist" + , "aliases": [ + "bicyclist" + ] + , "tags": [ + ] + } +, { + "emoji": "🏁" + , "description": "chequered flag" + , "aliases": [ + "checkered_flag" + ] + , "tags": [ + "milestone" + , "finish" + ] + } +, { + "emoji": "🏇" + , "description": "horse racing" + , "aliases": [ + "horse_racing" + ] + , "tags": [ + ] + } +, { + "emoji": "🏆" + , "description": "trophy" + , "aliases": [ + "trophy" + ] + , "tags": [ + "award" + , "contest" + , "winner" + ] + } +, { + "emoji": "🎿" + , "description": "ski and ski boot" + , "aliases": [ + "ski" + ] + , "tags": [ + ] + } +, { + "emoji": "🏂" + , "description": "snowboarder" + , "aliases": [ + "snowboarder" + ] + , "tags": [ + ] + } +, { + "emoji": "🏊" + , "description": "swimmer" + , "aliases": [ + "swimmer" + ] + , "tags": [ + ] + } +, { + "emoji": "🏄" + , "description": "surfer" + , "aliases": [ + "surfer" + ] + , "tags": [ + ] + } +, { + "emoji": "🎣" + , "description": "fishing pole and fish" + , "aliases": [ + "fishing_pole_and_fish" + ] + , "tags": [ + ] + } +, { + "emoji": "☕" + , "description": "hot beverage" + , "aliases": [ + "coffee" + ] + , "tags": [ + "cafe" + , "espresso" + ] + } +, { + "emoji": "🍵" + , "description": "teacup without handle" + , "aliases": [ + "tea" + ] + , "tags": [ + "green" + , "breakfast" + ] + } +, { + "emoji": "🍶" + , "description": "sake bottle and cup" + , "aliases": [ + "sake" + ] + , "tags": [ + ] + } +, { + "emoji": "🍼" + , "description": "baby bottle" + , "aliases": [ + "baby_bottle" + ] + , "tags": [ + "milk" + ] + } +, { + "emoji": "🍺" + , "description": "beer mug" + , "aliases": [ + "beer" + ] + , "tags": [ + "drink" + ] + } +, { + "emoji": "🍻" + , "description": "clinking beer mugs" + , "aliases": [ + "beers" + ] + , "tags": [ + "drinks" + ] + } +, { + "emoji": "🍸" + , "description": "cocktail glass" + , "aliases": [ + "cocktail" + ] + , "tags": [ + "drink" + ] + } +, { + "emoji": "🍹" + , "description": "tropical drink" + , "aliases": [ + "tropical_drink" + ] + , "tags": [ + "summer" + , "vacation" + ] + } +, { + "emoji": "🍷" + , "description": "wine glass" + , "aliases": [ + "wine_glass" + ] + , "tags": [ + ] + } +, { + "emoji": "🍴" + , "description": "fork and knife" + , "aliases": [ + "fork_and_knife" + ] + , "tags": [ + "cutlery" + ] + } +, { + "emoji": "🍕" + , "description": "slice of pizza" + , "aliases": [ + "pizza" + ] + , "tags": [ + ] + } +, { + "emoji": "🍔" + , "description": "hamburger" + , "aliases": [ + "hamburger" + ] + , "tags": [ + "burger" + ] + } +, { + "emoji": "🍟" + , "description": "french fries" + , "aliases": [ + "fries" + ] + , "tags": [ + ] + } +, { + "emoji": "🍗" + , "description": "poultry leg" + , "aliases": [ + "poultry_leg" + ] + , "tags": [ + "meat" + , "chicken" + ] + } +, { + "emoji": "🍖" + , "description": "meat on bone" + , "aliases": [ + "meat_on_bone" + ] + , "tags": [ + ] + } +, { + "emoji": "🍝" + , "description": "spaghetti" + , "aliases": [ + "spaghetti" + ] + , "tags": [ + "pasta" + ] + } +, { + "emoji": "🍛" + , "description": "curry and rice" + , "aliases": [ + "curry" + ] + , "tags": [ + ] + } +, { + "emoji": "🍤" + , "description": "fried shrimp" + , "aliases": [ + "fried_shrimp" + ] + , "tags": [ + "tempura" + ] + } +, { + "emoji": "🍱" + , "description": "bento box" + , "aliases": [ + "bento" + ] + , "tags": [ + ] + } +, { + "emoji": "🍣" + , "description": "sushi" + , "aliases": [ + "sushi" + ] + , "tags": [ + ] + } +, { + "emoji": "🍥" + , "description": "fish cake with swirl design" + , "aliases": [ + "fish_cake" + ] + , "tags": [ + ] + } +, { + "emoji": "🍙" + , "description": "rice ball" + , "aliases": [ + "rice_ball" + ] + , "tags": [ + ] + } +, { + "emoji": "🍘" + , "description": "rice cracker" + , "aliases": [ + "rice_cracker" + ] + , "tags": [ + ] + } +, { + "emoji": "🍚" + , "description": "cooked rice" + , "aliases": [ + "rice" + ] + , "tags": [ + ] + } +, { + "emoji": "🍜" + , "description": "steaming bowl" + , "aliases": [ + "ramen" + ] + , "tags": [ + "noodle" + ] + } +, { + "emoji": "🍲" + , "description": "pot of food" + , "aliases": [ + "stew" + ] + , "tags": [ + ] + } +, { + "emoji": "🍢" + , "description": "oden" + , "aliases": [ + "oden" + ] + , "tags": [ + ] + } +, { + "emoji": "🍡" + , "description": "dango" + , "aliases": [ + "dango" + ] + , "tags": [ + ] + } +, { + "emoji": "🍳" + , "description": "cooking" + , "aliases": [ + "egg" + ] + , "tags": [ + "breakfast" + ] + } +, { + "emoji": "🍞" + , "description": "bread" + , "aliases": [ + "bread" + ] + , "tags": [ + "toast" + ] + } +, { + "emoji": "🍩" + , "description": "doughnut" + , "aliases": [ + "doughnut" + ] + , "tags": [ + ] + } +, { + "emoji": "🍮" + , "description": "custard" + , "aliases": [ + "custard" + ] + , "tags": [ + ] + } +, { + "emoji": "🍦" + , "description": "soft ice cream" + , "aliases": [ + "icecream" + ] + , "tags": [ + ] + } +, { + "emoji": "🍨" + , "description": "ice cream" + , "aliases": [ + "ice_cream" + ] + , "tags": [ + ] + } +, { + "emoji": "🍧" + , "description": "shaved ice" + , "aliases": [ + "shaved_ice" + ] + , "tags": [ + ] + } +, { + "emoji": "🎂" + , "description": "birthday cake" + , "aliases": [ + "birthday" + ] + , "tags": [ + "party" + ] + } +, { + "emoji": "🍰" + , "description": "shortcake" + , "aliases": [ + "cake" + ] + , "tags": [ + "dessert" + ] + } +, { + "emoji": "🍪" + , "description": "cookie" + , "aliases": [ + "cookie" + ] + , "tags": [ + ] + } +, { + "emoji": "🍫" + , "description": "chocolate bar" + , "aliases": [ + "chocolate_bar" + ] + , "tags": [ + ] + } +, { + "emoji": "🍬" + , "description": "candy" + , "aliases": [ + "candy" + ] + , "tags": [ + "sweet" + ] + } +, { + "emoji": "🍭" + , "description": "lollipop" + , "aliases": [ + "lollipop" + ] + , "tags": [ + ] + } +, { + "emoji": "🍯" + , "description": "honey pot" + , "aliases": [ + "honey_pot" + ] + , "tags": [ + ] + } +, { + "emoji": "🍎" + , "description": "red apple" + , "aliases": [ + "apple" + ] + , "tags": [ + ] + } +, { + "emoji": "🍏" + , "description": "green apple" + , "aliases": [ + "green_apple" + ] + , "tags": [ + "fruit" + ] + } +, { + "emoji": "🍊" + , "description": "tangerine" + , "aliases": [ + "tangerine" + ] + , "tags": [ + ] + } +, { + "emoji": "🍋" + , "description": "lemon" + , "aliases": [ + "lemon" + ] + , "tags": [ + ] + } +, { + "emoji": "🍒" + , "description": "cherries" + , "aliases": [ + "cherries" + ] + , "tags": [ + "fruit" + ] + } +, { + "emoji": "🍇" + , "description": "grapes" + , "aliases": [ + "grapes" + ] + , "tags": [ + ] + } +, { + "emoji": "🍉" + , "description": "watermelon" + , "aliases": [ + "watermelon" + ] + , "tags": [ + ] + } +, { + "emoji": "🍓" + , "description": "strawberry" + , "aliases": [ + "strawberry" + ] + , "tags": [ + "fruit" + ] + } +, { + "emoji": "🍑" + , "description": "peach" + , "aliases": [ + "peach" + ] + , "tags": [ + ] + } +, { + "emoji": "🍈" + , "description": "melon" + , "aliases": [ + "melon" + ] + , "tags": [ + ] + } +, { + "emoji": "🍌" + , "description": "banana" + , "aliases": [ + "banana" + ] + , "tags": [ + "fruit" + ] + } +, { + "emoji": "🍐" + , "description": "pear" + , "aliases": [ + "pear" + ] + , "tags": [ + ] + } +, { + "emoji": "🍍" + , "description": "pineapple" + , "aliases": [ + "pineapple" + ] + , "tags": [ + ] + } +, { + "emoji": "🍠" + , "description": "roasted sweet potato" + , "aliases": [ + "sweet_potato" + ] + , "tags": [ + ] + } +, { + "emoji": "🍆" + , "description": "aubergine" + , "aliases": [ + "eggplant" + ] + , "tags": [ + "aubergine" + ] + } +, { + "emoji": "🍅" + , "description": "tomato" + , "aliases": [ + "tomato" + ] + , "tags": [ + ] + } +, { + "emoji": "🌽" + , "description": "ear of maize" + , "aliases": [ + "corn" + ] + , "tags": [ + ] + } +, { + "emoji": "🏠" + , "description": "house building" + , "aliases": [ + "house" + ] + , "tags": [ + ] + } +, { + "emoji": "🏡" + , "description": "house with garden" + , "aliases": [ + "house_with_garden" + ] + , "tags": [ + ] + } +, { + "emoji": "🏫" + , "description": "school" + , "aliases": [ + "school" + ] + , "tags": [ + ] + } +, { + "emoji": "🏢" + , "description": "office building" + , "aliases": [ + "office" + ] + , "tags": [ + ] + } +, { + "emoji": "🏣" + , "description": "japanese post office" + , "aliases": [ + "post_office" + ] + , "tags": [ + ] + } +, { + "emoji": "🏥" + , "description": "hospital" + , "aliases": [ + "hospital" + ] + , "tags": [ + ] + } +, { + "emoji": "🏦" + , "description": "bank" + , "aliases": [ + "bank" + ] + , "tags": [ + ] + } +, { + "emoji": "🏪" + , "description": "convenience store" + , "aliases": [ + "convenience_store" + ] + , "tags": [ + ] + } +, { + "emoji": "🏩" + , "description": "love hotel" + , "aliases": [ + "love_hotel" + ] + , "tags": [ + ] + } +, { + "emoji": "🏨" + , "description": "hotel" + , "aliases": [ + "hotel" + ] + , "tags": [ + ] + } +, { + "emoji": "💒" + , "description": "wedding" + , "aliases": [ + "wedding" + ] + , "tags": [ + "marriage" + ] + } +, { + "emoji": "⛪" + , "description": "church" + , "aliases": [ + "church" + ] + , "tags": [ + ] + } +, { + "emoji": "🏬" + , "description": "department store" + , "aliases": [ + "department_store" + ] + , "tags": [ + ] + } +, { + "emoji": "🏤" + , "description": "european post office" + , "aliases": [ + "european_post_office" + ] + , "tags": [ + ] + } +, { + "emoji": "🌇" + , "description": "sunset over buildings" + , "aliases": [ + "city_sunrise" + ] + , "tags": [ + ] + } +, { + "emoji": "🌆" + , "description": "cityscape at dusk" + , "aliases": [ + "city_sunset" + ] + , "tags": [ + ] + } +, { + "emoji": "🏯" + , "description": "japanese castle" + , "aliases": [ + "japanese_castle" + ] + , "tags": [ + ] + } +, { + "emoji": "🏰" + , "description": "european castle" + , "aliases": [ + "european_castle" + ] + , "tags": [ + ] + } +, { + "emoji": "⛺" + , "description": "tent" + , "aliases": [ + "tent" + ] + , "tags": [ + "camping" + ] + } +, { + "emoji": "🏭" + , "description": "factory" + , "aliases": [ + "factory" + ] + , "tags": [ + ] + } +, { + "emoji": "🗼" + , "description": "tokyo tower" + , "aliases": [ + "tokyo_tower" + ] + , "tags": [ + ] + } +, { + "emoji": "🗾" + , "description": "silhouette of japan" + , "aliases": [ + "japan" + ] + , "tags": [ + ] + } +, { + "emoji": "🗻" + , "description": "mount fuji" + , "aliases": [ + "mount_fuji" + ] + , "tags": [ + ] + } +, { + "emoji": "🌄" + , "description": "sunrise over mountains" + , "aliases": [ + "sunrise_over_mountains" + ] + , "tags": [ + ] + } +, { + "emoji": "🌅" + , "description": "sunrise" + , "aliases": [ + "sunrise" + ] + , "tags": [ + ] + } +, { + "emoji": "🌃" + , "description": "night with stars" + , "aliases": [ + "night_with_stars" + ] + , "tags": [ + ] + } +, { + "emoji": "🗽" + , "description": "statue of liberty" + , "aliases": [ + "statue_of_liberty" + ] + , "tags": [ + ] + } +, { + "emoji": "🌉" + , "description": "bridge at night" + , "aliases": [ + "bridge_at_night" + ] + , "tags": [ + ] + } +, { + "emoji": "🎠" + , "description": "carousel horse" + , "aliases": [ + "carousel_horse" + ] + , "tags": [ + ] + } +, { + "emoji": "🎡" + , "description": "ferris wheel" + , "aliases": [ + "ferris_wheel" + ] + , "tags": [ + ] + } +, { + "emoji": "⛲" + , "description": "fountain" + , "aliases": [ + "fountain" + ] + , "tags": [ + ] + } +, { + "emoji": "🎢" + , "description": "roller coaster" + , "aliases": [ + "roller_coaster" + ] + , "tags": [ + ] + } +, { + "emoji": "🚢" + , "description": "ship" + , "aliases": [ + "ship" + ] + , "tags": [ + ] + } +, { + "emoji": "⛵" + , "description": "sailboat" + , "aliases": [ + "boat" + , "sailboat" + ] + , "tags": [ + ] + } +, { + "emoji": "🚤" + , "description": "speedboat" + , "aliases": [ + "speedboat" + ] + , "tags": [ + "ship" + ] + } +, { + "emoji": "🚣" + , "description": "rowboat" + , "aliases": [ + "rowboat" + ] + , "tags": [ + ] + } +, { + "emoji": "⚓" + , "description": "anchor" + , "aliases": [ + "anchor" + ] + , "tags": [ + "ship" + ] + } +, { + "emoji": "🚀" + , "description": "rocket" + , "aliases": [ + "rocket" + ] + , "tags": [ + "ship" + , "launch" + ] + } +, { + "emoji": "✈️" + , "description": "airplane" + , "aliases": [ + "airplane" + ] + , "tags": [ + "flight" + ] + } +, { + "emoji": "💺" + , "description": "seat" + , "aliases": [ + "seat" + ] + , "tags": [ + ] + } +, { + "emoji": "🚁" + , "description": "helicopter" + , "aliases": [ + "helicopter" + ] + , "tags": [ + ] + } +, { + "emoji": "🚂" + , "description": "steam locomotive" + , "aliases": [ + "steam_locomotive" + ] + , "tags": [ + "train" + ] + } +, { + "emoji": "🚊" + , "description": "tram" + , "aliases": [ + "tram" + ] + , "tags": [ + ] + } +, { + "emoji": "🚉" + , "description": "station" + , "aliases": [ + "station" + ] + , "tags": [ + ] + } +, { + "emoji": "🚞" + , "description": "mountain railway" + , "aliases": [ + "mountain_railway" + ] + , "tags": [ + ] + } +, { + "emoji": "🚆" + , "description": "train" + , "aliases": [ + "train2" + ] + , "tags": [ + ] + } +, { + "emoji": "🚄" + , "description": "high-speed train" + , "aliases": [ + "bullettrain_side" + ] + , "tags": [ + "train" + ] + } +, { + "emoji": "🚅" + , "description": "high-speed train with bullet nose" + , "aliases": [ + "bullettrain_front" + ] + , "tags": [ + "train" + ] + } +, { + "emoji": "🚈" + , "description": "light rail" + , "aliases": [ + "light_rail" + ] + , "tags": [ + ] + } +, { + "emoji": "🚇" + , "description": "metro" + , "aliases": [ + "metro" + ] + , "tags": [ + ] + } +, { + "emoji": "🚝" + , "description": "monorail" + , "aliases": [ + "monorail" + ] + , "tags": [ + ] + } +, { + "emoji": "🚋" + , "description": "tram car" + , "aliases": [ + "train" + ] + , "tags": [ + ] + } +, { + "emoji": "🚃" + , "description": "railway car" + , "aliases": [ + "railway_car" + ] + , "tags": [ + ] + } +, { + "emoji": "🚎" + , "description": "trolleybus" + , "aliases": [ + "trolleybus" + ] + , "tags": [ + ] + } +, { + "emoji": "🚌" + , "description": "bus" + , "aliases": [ + "bus" + ] + , "tags": [ + ] + } +, { + "emoji": "🚍" + , "description": "oncoming bus" + , "aliases": [ + "oncoming_bus" + ] + , "tags": [ + ] + } +, { + "emoji": "🚙" + , "description": "recreational vehicle" + , "aliases": [ + "blue_car" + ] + , "tags": [ + ] + } +, { + "emoji": "🚘" + , "description": "oncoming automobile" + , "aliases": [ + "oncoming_automobile" + ] + , "tags": [ + ] + } +, { + "emoji": "🚗" + , "description": "automobile" + , "aliases": [ + "car" + , "red_car" + ] + , "tags": [ + ] + } +, { + "emoji": "🚕" + , "description": "taxi" + , "aliases": [ + "taxi" + ] + , "tags": [ + ] + } +, { + "emoji": "🚖" + , "description": "oncoming taxi" + , "aliases": [ + "oncoming_taxi" + ] + , "tags": [ + ] + } +, { + "emoji": "🚛" + , "description": "articulated lorry" + , "aliases": [ + "articulated_lorry" + ] + , "tags": [ + ] + } +, { + "emoji": "🚚" + , "description": "delivery truck" + , "aliases": [ + "truck" + ] + , "tags": [ + ] + } +, { + "emoji": "🚨" + , "description": "police cars revolving light" + , "aliases": [ + "rotating_light" + ] + , "tags": [ + "911" + , "emergency" + ] + } +, { + "emoji": "🚓" + , "description": "police car" + , "aliases": [ + "police_car" + ] + , "tags": [ + ] + } +, { + "emoji": "🚔" + , "description": "oncoming police car" + , "aliases": [ + "oncoming_police_car" + ] + , "tags": [ + ] + } +, { + "emoji": "🚒" + , "description": "fire engine" + , "aliases": [ + "fire_engine" + ] + , "tags": [ + ] + } +, { + "emoji": "🚑" + , "description": "ambulance" + , "aliases": [ + "ambulance" + ] + , "tags": [ + ] + } +, { + "emoji": "🚐" + , "description": "minibus" + , "aliases": [ + "minibus" + ] + , "tags": [ + ] + } +, { + "emoji": "🚲" + , "description": "bicycle" + , "aliases": [ + "bike" + ] + , "tags": [ + "bicycle" + ] + } +, { + "emoji": "🚡" + , "description": "aerial tramway" + , "aliases": [ + "aerial_tramway" + ] + , "tags": [ + ] + } +, { + "emoji": "🚟" + , "description": "suspension railway" + , "aliases": [ + "suspension_railway" + ] + , "tags": [ + ] + } +, { + "emoji": "🚠" + , "description": "mountain cableway" + , "aliases": [ + "mountain_cableway" + ] + , "tags": [ + ] + } +, { + "emoji": "🚜" + , "description": "tractor" + , "aliases": [ + "tractor" + ] + , "tags": [ + ] + } +, { + "emoji": "💈" + , "description": "barber pole" + , "aliases": [ + "barber" + ] + , "tags": [ + ] + } +, { + "emoji": "🚏" + , "description": "bus stop" + , "aliases": [ + "busstop" + ] + , "tags": [ + ] + } +, { + "emoji": "🎫" + , "description": "ticket" + , "aliases": [ + "ticket" + ] + , "tags": [ + ] + } +, { + "emoji": "🚦" + , "description": "vertical traffic light" + , "aliases": [ + "vertical_traffic_light" + ] + , "tags": [ + "semaphore" + ] + } +, { + "emoji": "🚥" + , "description": "horizontal traffic light" + , "aliases": [ + "traffic_light" + ] + , "tags": [ + ] + } +, { + "emoji": "⚠️" + , "description": "warning sign" + , "aliases": [ + "warning" + ] + , "tags": [ + "wip" + ] + } +, { + "emoji": "🚧" + , "description": "construction sign" + , "aliases": [ + "construction" + ] + , "tags": [ + "wip" + ] + } +, { + "emoji": "🔰" + , "description": "japanese symbol for beginner" + , "aliases": [ + "beginner" + ] + , "tags": [ + ] + } +, { + "emoji": "⛽" + , "description": "fuel pump" + , "aliases": [ + "fuelpump" + ] + , "tags": [ + ] + } +, { + "emoji": "🏮" + , "description": "izakaya lantern" + , "aliases": [ + "izakaya_lantern" + , "lantern" + ] + , "tags": [ + ] + } +, { + "emoji": "🎰" + , "description": "slot machine" + , "aliases": [ + "slot_machine" + ] + , "tags": [ + ] + } +, { + "emoji": "♨️" + , "description": "hot springs" + , "aliases": [ + "hotsprings" + ] + , "tags": [ + ] + } +, { + "emoji": "🗿" + , "description": "moyai" + , "aliases": [ + "moyai" + ] + , "tags": [ + "stone" + ] + } +, { + "emoji": "🎪" + , "description": "circus tent" + , "aliases": [ + "circus_tent" + ] + , "tags": [ + ] + } +, { + "emoji": "🎭" + , "description": "performing arts" + , "aliases": [ + "performing_arts" + ] + , "tags": [ + "theater" + , "drama" + ] + } +, { + "emoji": "📍" + , "description": "round pushpin" + , "aliases": [ + "round_pushpin" + ] + , "tags": [ + "location" + ] + } +, { + "emoji": "🚩" + , "description": "triangular flag on post" + , "aliases": [ + "triangular_flag_on_post" + ] + , "tags": [ + ] + } +, { + "emoji": "🇯🇵" + , "description": "regional indicator symbol letter j + regional indicator symbol letter p" + , "aliases": [ + "jp" + ] + , "tags": [ + "japan" + ] + } +, { + "emoji": "🇰🇷" + , "description": "regional indicator symbol letter k + regional indicator symbol letter r" + , "aliases": [ + "kr" + ] + , "tags": [ + "korea" + ] + } +, { + "emoji": "🇩🇪" + , "description": "regional indicator symbol letter d + regional indicator symbol letter e" + , "aliases": [ + "de" + ] + , "tags": [ + "flag" + , "germany" + ] + } +, { + "emoji": "🇨🇳" + , "description": "regional indicator symbol letter c + regional indicator symbol letter n" + , "aliases": [ + "cn" + ] + , "tags": [ + "china" + ] + } +, { + "emoji": "🇺🇸" + , "description": "regional indicator symbol letter u + regional indicator symbol letter s" + , "aliases": [ + "us" + ] + , "tags": [ + "flag" + , "united" + , "america" + ] + } +, { + "emoji": "🇫🇷" + , "description": "regional indicator symbol letter f + regional indicator symbol letter r" + , "aliases": [ + "fr" + ] + , "tags": [ + "france" + , "french" + ] + } +, { + "emoji": "🇪🇸" + , "description": "regional indicator symbol letter e + regional indicator symbol letter s" + , "aliases": [ + "es" + ] + , "tags": [ + "spain" + ] + } +, { + "emoji": "🇮🇹" + , "description": "regional indicator symbol letter i + regional indicator symbol letter t" + , "aliases": [ + "it" + ] + , "tags": [ + "italy" + ] + } +, { + "emoji": "🇷🇺" + , "description": "regional indicator symbol letter r + regional indicator symbol letter u" + , "aliases": [ + "ru" + ] + , "tags": [ + "russia" + ] + } +, { + "emoji": "🇬🇧" + , "description": "regional indicator symbol letter g + regional indicator symbol letter b" + , "aliases": [ + "gb" + , "uk" + ] + , "tags": [ + "flag" + , "british" + ] + } +, { + "emoji": "1️⃣" + , "description": "digit one + combining enclosing keycap" + , "aliases": [ + "one" + ] + , "tags": [ + ] + } +, { + "emoji": "2️⃣" + , "description": "digit two + combining enclosing keycap" + , "aliases": [ + "two" + ] + , "tags": [ + ] + } +, { + "emoji": "3️⃣" + , "description": "digit three + combining enclosing keycap" + , "aliases": [ + "three" + ] + , "tags": [ + ] + } +, { + "emoji": "4️⃣" + , "description": "digit four + combining enclosing keycap" + , "aliases": [ + "four" + ] + , "tags": [ + ] + } +, { + "emoji": "5️⃣" + , "description": "digit five + combining enclosing keycap" + , "aliases": [ + "five" + ] + , "tags": [ + ] + } +, { + "emoji": "6️⃣" + , "description": "digit six + combining enclosing keycap" + , "aliases": [ + "six" + ] + , "tags": [ + ] + } +, { + "emoji": "7️⃣" + , "description": "digit seven + combining enclosing keycap" + , "aliases": [ + "seven" + ] + , "tags": [ + ] + } +, { + "emoji": "8️⃣" + , "description": "digit eight + combining enclosing keycap" + , "aliases": [ + "eight" + ] + , "tags": [ + ] + } +, { + "emoji": "9️⃣" + , "description": "digit nine + combining enclosing keycap" + , "aliases": [ + "nine" + ] + , "tags": [ + ] + } +, { + "emoji": "0️⃣" + , "description": "digit zero + combining enclosing keycap" + , "aliases": [ + "zero" + ] + , "tags": [ + ] + } +, { + "emoji": "🔟" + , "description": "keycap ten" + , "aliases": [ + "keycap_ten" + ] + , "tags": [ + ] + } +, { + "emoji": "🔢" + , "description": "input symbol for numbers" + , "aliases": [ + "1234" + ] + , "tags": [ + "numbers" + ] + } +, { + "emoji": "#️⃣" + , "description": "number sign + combining enclosing keycap" + , "aliases": [ + "hash" + ] + , "tags": [ + "number" + ] + } +, { + "emoji": "🔣" + , "description": "input symbol for symbols" + , "aliases": [ + "symbols" + ] + , "tags": [ + ] + } +, { + "emoji": "⬆️" + , "description": "upwards black arrow" + , "aliases": [ + "arrow_up" + ] + , "tags": [ + ] + } +, { + "emoji": "⬇️" + , "description": "downwards black arrow" + , "aliases": [ + "arrow_down" + ] + , "tags": [ + ] + } +, { + "emoji": "⬅️" + , "description": "leftwards black arrow" + , "aliases": [ + "arrow_left" + ] + , "tags": [ + ] + } +, { + "emoji": "➡️" + , "description": "black rightwards arrow" + , "aliases": [ + "arrow_right" + ] + , "tags": [ + ] + } +, { + "emoji": "🔠" + , "description": "input symbol for latin capital letters" + , "aliases": [ + "capital_abcd" + ] + , "tags": [ + "letters" + ] + } +, { + "emoji": "🔡" + , "description": "input symbol for latin small letters" + , "aliases": [ + "abcd" + ] + , "tags": [ + ] + } +, { + "emoji": "🔤" + , "description": "input symbol for latin letters" + , "aliases": [ + "abc" + ] + , "tags": [ + "alphabet" + ] + } +, { + "emoji": "↗️" + , "description": "north east arrow" + , "aliases": [ + "arrow_upper_right" + ] + , "tags": [ + ] + } +, { + "emoji": "↖️" + , "description": "north west arrow" + , "aliases": [ + "arrow_upper_left" + ] + , "tags": [ + ] + } +, { + "emoji": "↘️" + , "description": "south east arrow" + , "aliases": [ + "arrow_lower_right" + ] + , "tags": [ + ] + } +, { + "emoji": "↙️" + , "description": "south west arrow" + , "aliases": [ + "arrow_lower_left" + ] + , "tags": [ + ] + } +, { + "emoji": "↔️" + , "description": "left right arrow" + , "aliases": [ + "left_right_arrow" + ] + , "tags": [ + ] + } +, { + "emoji": "↕️" + , "description": "up down arrow" + , "aliases": [ + "arrow_up_down" + ] + , "tags": [ + ] + } +, { + "emoji": "🔄" + , "description": "anticlockwise downwards and upwards open circle arrows" + , "aliases": [ + "arrows_counterclockwise" + ] + , "tags": [ + "sync" + ] + } +, { + "emoji": "◀️" + , "description": "black left-pointing triangle" + , "aliases": [ + "arrow_backward" + ] + , "tags": [ + ] + } +, { + "emoji": "▶️" + , "description": "black right-pointing triangle" + , "aliases": [ + "arrow_forward" + ] + , "tags": [ + ] + } +, { + "emoji": "🔼" + , "description": "up-pointing small red triangle" + , "aliases": [ + "arrow_up_small" + ] + , "tags": [ + ] + } +, { + "emoji": "🔽" + , "description": "down-pointing small red triangle" + , "aliases": [ + "arrow_down_small" + ] + , "tags": [ + ] + } +, { + "emoji": "↩️" + , "description": "leftwards arrow with hook" + , "aliases": [ + "leftwards_arrow_with_hook" + ] + , "tags": [ + "return" + ] + } +, { + "emoji": "↪️" + , "description": "rightwards arrow with hook" + , "aliases": [ + "arrow_right_hook" + ] + , "tags": [ + ] + } +, { + "emoji": "ℹ️" + , "description": "information source" + , "aliases": [ + "information_source" + ] + , "tags": [ + ] + } +, { + "emoji": "⏪" + , "description": "black left-pointing double triangle" + , "aliases": [ + "rewind" + ] + , "tags": [ + ] + } +, { + "emoji": "⏩" + , "description": "black right-pointing double triangle" + , "aliases": [ + "fast_forward" + ] + , "tags": [ + ] + } +, { + "emoji": "⏫" + , "description": "black up-pointing double triangle" + , "aliases": [ + "arrow_double_up" + ] + , "tags": [ + ] + } +, { + "emoji": "⏬" + , "description": "black down-pointing double triangle" + , "aliases": [ + "arrow_double_down" + ] + , "tags": [ + ] + } +, { + "emoji": "⤵️" + , "description": "arrow pointing rightwards then curving downwards" + , "aliases": [ + "arrow_heading_down" + ] + , "tags": [ + ] + } +, { + "emoji": "⤴️" + , "description": "arrow pointing rightwards then curving upwards" + , "aliases": [ + "arrow_heading_up" + ] + , "tags": [ + ] + } +, { + "emoji": "🆗" + , "description": "squared ok" + , "aliases": [ + "ok" + ] + , "tags": [ + "yes" + ] + } +, { + "emoji": "🔀" + , "description": "twisted rightwards arrows" + , "aliases": [ + "twisted_rightwards_arrows" + ] + , "tags": [ + "shuffle" + ] + } +, { + "emoji": "🔁" + , "description": "clockwise rightwards and leftwards open circle arrows" + , "aliases": [ + "repeat" + ] + , "tags": [ + "loop" + ] + } +, { + "emoji": "🔂" + , "description": "clockwise rightwards and leftwards open circle arrows with circled one overlay" + , "aliases": [ + "repeat_one" + ] + , "tags": [ + ] + } +, { + "emoji": "🆕" + , "description": "squared new" + , "aliases": [ + "new" + ] + , "tags": [ + "fresh" + ] + } +, { + "emoji": "🆙" + , "description": "squared up with exclamation mark" + , "aliases": [ + "up" + ] + , "tags": [ + ] + } +, { + "emoji": "🆒" + , "description": "squared cool" + , "aliases": [ + "cool" + ] + , "tags": [ + ] + } +, { + "emoji": "🆓" + , "description": "squared free" + , "aliases": [ + "free" + ] + , "tags": [ + ] + } +, { + "emoji": "🆖" + , "description": "squared ng" + , "aliases": [ + "ng" + ] + , "tags": [ + ] + } +, { + "emoji": "📶" + , "description": "antenna with bars" + , "aliases": [ + "signal_strength" + ] + , "tags": [ + "wifi" + ] + } +, { + "emoji": "🎦" + , "description": "cinema" + , "aliases": [ + "cinema" + ] + , "tags": [ + "film" + , "movie" + ] + } +, { + "emoji": "🈁" + , "description": "squared katakana koko" + , "aliases": [ + "koko" + ] + , "tags": [ + ] + } +, { + "emoji": "🈯" + , "description": "squared cjk unified ideograph-6307" + , "aliases": [ + "u6307" + ] + , "tags": [ + ] + } +, { + "emoji": "🈳" + , "description": "squared cjk unified ideograph-7a7a" + , "aliases": [ + "u7a7a" + ] + , "tags": [ + ] + } +, { + "emoji": "🈵" + , "description": "squared cjk unified ideograph-6e80" + , "aliases": [ + "u6e80" + ] + , "tags": [ + ] + } +, { + "emoji": "🈴" + , "description": "squared cjk unified ideograph-5408" + , "aliases": [ + "u5408" + ] + , "tags": [ + ] + } +, { + "emoji": "🈲" + , "description": "squared cjk unified ideograph-7981" + , "aliases": [ + "u7981" + ] + , "tags": [ + ] + } +, { + "emoji": "🉐" + , "description": "circled ideograph advantage" + , "aliases": [ + "ideograph_advantage" + ] + , "tags": [ + ] + } +, { + "emoji": "🈹" + , "description": "squared cjk unified ideograph-5272" + , "aliases": [ + "u5272" + ] + , "tags": [ + ] + } +, { + "emoji": "🈺" + , "description": "squared cjk unified ideograph-55b6" + , "aliases": [ + "u55b6" + ] + , "tags": [ + ] + } +, { + "emoji": "🈶" + , "description": "squared cjk unified ideograph-6709" + , "aliases": [ + "u6709" + ] + , "tags": [ + ] + } +, { + "emoji": "🈚" + , "description": "squared cjk unified ideograph-7121" + , "aliases": [ + "u7121" + ] + , "tags": [ + ] + } +, { + "emoji": "🚻" + , "description": "restroom" + , "aliases": [ + "restroom" + ] + , "tags": [ + "toilet" + ] + } +, { + "emoji": "🚹" + , "description": "mens symbol" + , "aliases": [ + "mens" + ] + , "tags": [ + ] + } +, { + "emoji": "🚺" + , "description": "womens symbol" + , "aliases": [ + "womens" + ] + , "tags": [ + ] + } +, { + "emoji": "🚼" + , "description": "baby symbol" + , "aliases": [ + "baby_symbol" + ] + , "tags": [ + ] + } +, { + "emoji": "🚾" + , "description": "water closet" + , "aliases": [ + "wc" + ] + , "tags": [ + "toilet" + , "restroom" + ] + } +, { + "emoji": "🚰" + , "description": "potable water symbol" + , "aliases": [ + "potable_water" + ] + , "tags": [ + ] + } +, { + "emoji": "🚮" + , "description": "put litter in its place symbol" + , "aliases": [ + "put_litter_in_its_place" + ] + , "tags": [ + ] + } +, { + "emoji": "🅿️" + , "description": "negative squared latin capital letter p" + , "aliases": [ + "parking" + ] + , "tags": [ + ] + } +, { + "emoji": "♿" + , "description": "wheelchair symbol" + , "aliases": [ + "wheelchair" + ] + , "tags": [ + "accessibility" + ] + } +, { + "emoji": "🚭" + , "description": "no smoking symbol" + , "aliases": [ + "no_smoking" + ] + , "tags": [ + ] + } +, { + "emoji": "🈷️" + , "description": "squared cjk unified ideograph-6708" + , "aliases": [ + "u6708" + ] + , "tags": [ + ] + } +, { + "emoji": "🈸" + , "description": "squared cjk unified ideograph-7533" + , "aliases": [ + "u7533" + ] + , "tags": [ + ] + } +, { + "emoji": "🈂️" + , "description": "squared katakana sa" + , "aliases": [ + "sa" + ] + , "tags": [ + ] + } +, { + "emoji": "Ⓜ️" + , "description": "circled latin capital letter m" + , "aliases": [ + "m" + ] + , "tags": [ + ] + } +, { + "emoji": "🛂" + , "description": "passport control" + , "aliases": [ + "passport_control" + ] + , "tags": [ + ] + } +, { + "emoji": "🛄" + , "description": "baggage claim" + , "aliases": [ + "baggage_claim" + ] + , "tags": [ + "airport" + ] + } +, { + "emoji": "🛅" + , "description": "left luggage" + , "aliases": [ + "left_luggage" + ] + , "tags": [ + ] + } +, { + "emoji": "🛃" + , "description": "customs" + , "aliases": [ + "customs" + ] + , "tags": [ + ] + } +, { + "emoji": "🉑" + , "description": "circled ideograph accept" + , "aliases": [ + "accept" + ] + , "tags": [ + ] + } +, { + "emoji": "㊙️" + , "description": "circled ideograph secret" + , "aliases": [ + "secret" + ] + , "tags": [ + ] + } +, { + "emoji": "㊗️" + , "description": "circled ideograph congratulation" + , "aliases": [ + "congratulations" + ] + , "tags": [ + ] + } +, { + "emoji": "🆑" + , "description": "squared cl" + , "aliases": [ + "cl" + ] + , "tags": [ + ] + } +, { + "emoji": "🆘" + , "description": "squared sos" + , "aliases": [ + "sos" + ] + , "tags": [ + "help" + , "emergency" + ] + } +, { + "emoji": "🆔" + , "description": "squared id" + , "aliases": [ + "id" + ] + , "tags": [ + ] + } +, { + "emoji": "🚫" + , "description": "no entry sign" + , "aliases": [ + "no_entry_sign" + ] + , "tags": [ + "block" + , "forbidden" + ] + } +, { + "emoji": "🔞" + , "description": "no one under eighteen symbol" + , "aliases": [ + "underage" + ] + , "tags": [ + ] + } +, { + "emoji": "📵" + , "description": "no mobile phones" + , "aliases": [ + "no_mobile_phones" + ] + , "tags": [ + ] + } +, { + "emoji": "🚯" + , "description": "do not litter symbol" + , "aliases": [ + "do_not_litter" + ] + , "tags": [ + ] + } +, { + "emoji": "🚱" + , "description": "non-potable water symbol" + , "aliases": [ + "non-potable_water" + ] + , "tags": [ + ] + } +, { + "emoji": "🚳" + , "description": "no bicycles" + , "aliases": [ + "no_bicycles" + ] + , "tags": [ + ] + } +, { + "emoji": "🚷" + , "description": "no pedestrians" + , "aliases": [ + "no_pedestrians" + ] + , "tags": [ + ] + } +, { + "emoji": "🚸" + , "description": "children crossing" + , "aliases": [ + "children_crossing" + ] + , "tags": [ + ] + } +, { + "emoji": "⛔" + , "description": "no entry" + , "aliases": [ + "no_entry" + ] + , "tags": [ + "limit" + ] + } +, { + "emoji": "✳️" + , "description": "eight spoked asterisk" + , "aliases": [ + "eight_spoked_asterisk" + ] + , "tags": [ + ] + } +, { + "emoji": "❇️" + , "description": "sparkle" + , "aliases": [ + "sparkle" + ] + , "tags": [ + ] + } +, { + "emoji": "❎" + , "description": "negative squared cross mark" + , "aliases": [ + "negative_squared_cross_mark" + ] + , "tags": [ + ] + } +, { + "emoji": "✅" + , "description": "white heavy check mark" + , "aliases": [ + "white_check_mark" + ] + , "tags": [ + ] + } +, { + "emoji": "✴️" + , "description": "eight pointed black star" + , "aliases": [ + "eight_pointed_black_star" + ] + , "tags": [ + ] + } +, { + "emoji": "💟" + , "description": "heart decoration" + , "aliases": [ + "heart_decoration" + ] + , "tags": [ + ] + } +, { + "emoji": "🆚" + , "description": "squared vs" + , "aliases": [ + "vs" + ] + , "tags": [ + ] + } +, { + "emoji": "📳" + , "description": "vibration mode" + , "aliases": [ + "vibration_mode" + ] + , "tags": [ + ] + } +, { + "emoji": "📴" + , "description": "mobile phone off" + , "aliases": [ + "mobile_phone_off" + ] + , "tags": [ + "mute" + , "off" + ] + } +, { + "emoji": "🅰️" + , "description": "negative squared latin capital letter a" + , "aliases": [ + "a" + ] + , "tags": [ + ] + } +, { + "emoji": "🅱️" + , "description": "negative squared latin capital letter b" + , "aliases": [ + "b" + ] + , "tags": [ + ] + } +, { + "emoji": "🆎" + , "description": "negative squared ab" + , "aliases": [ + "ab" + ] + , "tags": [ + ] + } +, { + "emoji": "🅾️" + , "description": "negative squared latin capital letter o" + , "aliases": [ + "o2" + ] + , "tags": [ + ] + } +, { + "emoji": "💠" + , "description": "diamond shape with a dot inside" + , "aliases": [ + "diamond_shape_with_a_dot_inside" + ] + , "tags": [ + ] + } +, { + "emoji": "➿" + , "description": "double curly loop" + , "aliases": [ + "loop" + ] + , "tags": [ + ] + } +, { + "emoji": "♻️" + , "description": "black universal recycling symbol" + , "aliases": [ + "recycle" + ] + , "tags": [ + "environment" + , "green" + ] + } +, { + "emoji": "♈" + , "description": "aries" + , "aliases": [ + "aries" + ] + , "tags": [ + ] + } +, { + "emoji": "♉" + , "description": "taurus" + , "aliases": [ + "taurus" + ] + , "tags": [ + ] + } +, { + "emoji": "♊" + , "description": "gemini" + , "aliases": [ + "gemini" + ] + , "tags": [ + ] + } +, { + "emoji": "♋" + , "description": "cancer" + , "aliases": [ + "cancer" + ] + , "tags": [ + ] + } +, { + "emoji": "♌" + , "description": "leo" + , "aliases": [ + "leo" + ] + , "tags": [ + ] + } +, { + "emoji": "♍" + , "description": "virgo" + , "aliases": [ + "virgo" + ] + , "tags": [ + ] + } +, { + "emoji": "♎" + , "description": "libra" + , "aliases": [ + "libra" + ] + , "tags": [ + ] + } +, { + "emoji": "♏" + , "description": "scorpius" + , "aliases": [ + "scorpius" + ] + , "tags": [ + ] + } +, { + "emoji": "♐" + , "description": "sagittarius" + , "aliases": [ + "sagittarius" + ] + , "tags": [ + ] + } +, { + "emoji": "♑" + , "description": "capricorn" + , "aliases": [ + "capricorn" + ] + , "tags": [ + ] + } +, { + "emoji": "♒" + , "description": "aquarius" + , "aliases": [ + "aquarius" + ] + , "tags": [ + ] + } +, { + "emoji": "♓" + , "description": "pisces" + , "aliases": [ + "pisces" + ] + , "tags": [ + ] + } +, { + "emoji": "⛎" + , "description": "ophiuchus" + , "aliases": [ + "ophiuchus" + ] + , "tags": [ + ] + } +, { + "emoji": "🔯" + , "description": "six pointed star with middle dot" + , "aliases": [ + "six_pointed_star" + ] + , "tags": [ + ] + } +, { + "emoji": "🏧" + , "description": "automated teller machine" + , "aliases": [ + "atm" + ] + , "tags": [ + ] + } +, { + "emoji": "💹" + , "description": "chart with upwards trend and yen sign" + , "aliases": [ + "chart" + ] + , "tags": [ + ] + } +, { + "emoji": "💲" + , "description": "heavy dollar sign" + , "aliases": [ + "heavy_dollar_sign" + ] + , "tags": [ + ] + } +, { + "emoji": "💱" + , "description": "currency exchange" + , "aliases": [ + "currency_exchange" + ] + , "tags": [ + ] + } +, { + "emoji": "©️" + , "description": "copyright sign" + , "aliases": [ + "copyright" + ] + , "tags": [ + ] + } +, { + "emoji": "®️" + , "description": "registered sign" + , "aliases": [ + "registered" + ] + , "tags": [ + ] + } +, { + "emoji": "™️" + , "description": "trade mark sign" + , "aliases": [ + "tm" + ] + , "tags": [ + "trademark" + ] + } +, { + "emoji": "❌" + , "description": "cross mark" + , "aliases": [ + "x" + ] + , "tags": [ + ] + } +, { + "emoji": "‼️" + , "description": "double exclamation mark" + , "aliases": [ + "bangbang" + ] + , "tags": [ + ] + } +, { + "emoji": "⁉️" + , "description": "exclamation question mark" + , "aliases": [ + "interrobang" + ] + , "tags": [ + ] + } +, { + "emoji": "❗" + , "description": "heavy exclamation mark symbol" + , "aliases": [ + "exclamation" + , "heavy_exclamation_mark" + ] + , "tags": [ + "bang" + ] + } +, { + "emoji": "❓" + , "description": "black question mark ornament" + , "aliases": [ + "question" + ] + , "tags": [ + "confused" + ] + } +, { + "emoji": "❕" + , "description": "white exclamation mark ornament" + , "aliases": [ + "grey_exclamation" + ] + , "tags": [ + ] + } +, { + "emoji": "❔" + , "description": "white question mark ornament" + , "aliases": [ + "grey_question" + ] + , "tags": [ + ] + } +, { + "emoji": "⭕" + , "description": "heavy large circle" + , "aliases": [ + "o" + ] + , "tags": [ + ] + } +, { + "emoji": "🔝" + , "description": "top with upwards arrow above" + , "aliases": [ + "top" + ] + , "tags": [ + ] + } +, { + "emoji": "🔚" + , "description": "end with leftwards arrow above" + , "aliases": [ + "end" + ] + , "tags": [ + ] + } +, { + "emoji": "🔙" + , "description": "back with leftwards arrow above" + , "aliases": [ + "back" + ] + , "tags": [ + ] + } +, { + "emoji": "🔛" + , "description": "on with exclamation mark with left right arrow above" + , "aliases": [ + "on" + ] + , "tags": [ + ] + } +, { + "emoji": "🔜" + , "description": "soon with rightwards arrow above" + , "aliases": [ + "soon" + ] + , "tags": [ + ] + } +, { + "emoji": "🔃" + , "description": "clockwise downwards and upwards open circle arrows" + , "aliases": [ + "arrows_clockwise" + ] + , "tags": [ + ] + } +, { + "emoji": "🕛" + , "description": "clock face twelve oclock" + , "aliases": [ + "clock12" + ] + , "tags": [ + ] + } +, { + "emoji": "🕧" + , "description": "clock face twelve-thirty" + , "aliases": [ + "clock1230" + ] + , "tags": [ + ] + } +, { + "emoji": "🕐" + , "description": "clock face one oclock" + , "aliases": [ + "clock1" + ] + , "tags": [ + ] + } +, { + "emoji": "🕜" + , "description": "clock face one-thirty" + , "aliases": [ + "clock130" + ] + , "tags": [ + ] + } +, { + "emoji": "🕑" + , "description": "clock face two oclock" + , "aliases": [ + "clock2" + ] + , "tags": [ + ] + } +, { + "emoji": "🕝" + , "description": "clock face two-thirty" + , "aliases": [ + "clock230" + ] + , "tags": [ + ] + } +, { + "emoji": "🕒" + , "description": "clock face three oclock" + , "aliases": [ + "clock3" + ] + , "tags": [ + ] + } +, { + "emoji": "🕞" + , "description": "clock face three-thirty" + , "aliases": [ + "clock330" + ] + , "tags": [ + ] + } +, { + "emoji": "🕓" + , "description": "clock face four oclock" + , "aliases": [ + "clock4" + ] + , "tags": [ + ] + } +, { + "emoji": "🕟" + , "description": "clock face four-thirty" + , "aliases": [ + "clock430" + ] + , "tags": [ + ] + } +, { + "emoji": "🕔" + , "description": "clock face five oclock" + , "aliases": [ + "clock5" + ] + , "tags": [ + ] + } +, { + "emoji": "🕠" + , "description": "clock face five-thirty" + , "aliases": [ + "clock530" + ] + , "tags": [ + ] + } +, { + "emoji": "🕕" + , "description": "clock face six oclock" + , "aliases": [ + "clock6" + ] + , "tags": [ + ] + } +, { + "emoji": "🕖" + , "description": "clock face seven oclock" + , "aliases": [ + "clock7" + ] + , "tags": [ + ] + } +, { + "emoji": "🕗" + , "description": "clock face eight oclock" + , "aliases": [ + "clock8" + ] + , "tags": [ + ] + } +, { + "emoji": "🕘" + , "description": "clock face nine oclock" + , "aliases": [ + "clock9" + ] + , "tags": [ + ] + } +, { + "emoji": "🕙" + , "description": "clock face ten oclock" + , "aliases": [ + "clock10" + ] + , "tags": [ + ] + } +, { + "emoji": "🕚" + , "description": "clock face eleven oclock" + , "aliases": [ + "clock11" + ] + , "tags": [ + ] + } +, { + "emoji": "🕡" + , "description": "clock face six-thirty" + , "aliases": [ + "clock630" + ] + , "tags": [ + ] + } +, { + "emoji": "🕢" + , "description": "clock face seven-thirty" + , "aliases": [ + "clock730" + ] + , "tags": [ + ] + } +, { + "emoji": "🕣" + , "description": "clock face eight-thirty" + , "aliases": [ + "clock830" + ] + , "tags": [ + ] + } +, { + "emoji": "🕤" + , "description": "clock face nine-thirty" + , "aliases": [ + "clock930" + ] + , "tags": [ + ] + } +, { + "emoji": "🕥" + , "description": "clock face ten-thirty" + , "aliases": [ + "clock1030" + ] + , "tags": [ + ] + } +, { + "emoji": "🕦" + , "description": "clock face eleven-thirty" + , "aliases": [ + "clock1130" + ] + , "tags": [ + ] + } +, { + "emoji": "✖️" + , "description": "heavy multiplication x" + , "aliases": [ + "heavy_multiplication_x" + ] + , "tags": [ + ] + } +, { + "emoji": "➕" + , "description": "heavy plus sign" + , "aliases": [ + "heavy_plus_sign" + ] + , "tags": [ + ] + } +, { + "emoji": "➖" + , "description": "heavy minus sign" + , "aliases": [ + "heavy_minus_sign" + ] + , "tags": [ + ] + } +, { + "emoji": "➗" + , "description": "heavy division sign" + , "aliases": [ + "heavy_division_sign" + ] + , "tags": [ + ] + } +, { + "emoji": "♠️" + , "description": "black spade suit" + , "aliases": [ + "spades" + ] + , "tags": [ + ] + } +, { + "emoji": "♥️" + , "description": "black heart suit" + , "aliases": [ + "hearts" + ] + , "tags": [ + ] + } +, { + "emoji": "♣️" + , "description": "black club suit" + , "aliases": [ + "clubs" + ] + , "tags": [ + ] + } +, { + "emoji": "♦️" + , "description": "black diamond suit" + , "aliases": [ + "diamonds" + ] + , "tags": [ + ] + } +, { + "emoji": "💮" + , "description": "white flower" + , "aliases": [ + "white_flower" + ] + , "tags": [ + ] + } +, { + "emoji": "💯" + , "description": "hundred points symbol" + , "aliases": [ + "100" + ] + , "tags": [ + "score" + , "perfect" + ] + } +, { + "emoji": "✔️" + , "description": "heavy check mark" + , "aliases": [ + "heavy_check_mark" + ] + , "tags": [ + ] + } +, { + "emoji": "☑️" + , "description": "ballot box with check" + , "aliases": [ + "ballot_box_with_check" + ] + , "tags": [ + ] + } +, { + "emoji": "🔘" + , "description": "radio button" + , "aliases": [ + "radio_button" + ] + , "tags": [ + ] + } +, { + "emoji": "🔗" + , "description": "link symbol" + , "aliases": [ + "link" + ] + , "tags": [ + ] + } +, { + "emoji": "➰" + , "description": "curly loop" + , "aliases": [ + "curly_loop" + ] + , "tags": [ + ] + } +, { + "emoji": "〰️" + , "description": "wavy dash" + , "aliases": [ + "wavy_dash" + ] + , "tags": [ + ] + } +, { + "emoji": "〽️" + , "description": "part alternation mark" + , "aliases": [ + "part_alternation_mark" + ] + , "tags": [ + ] + } +, { + "emoji": "🔱" + , "description": "trident emblem" + , "aliases": [ + "trident" + ] + , "tags": [ + ] + } +, { + "emoji": "◼️" + , "description": "black medium square" + , "aliases": [ + "black_medium_square" + ] + , "tags": [ + ] + } +, { + "emoji": "◻️" + , "description": "white medium square" + , "aliases": [ + "white_medium_square" + ] + , "tags": [ + ] + } +, { + "emoji": "◾" + , "description": "black medium small square" + , "aliases": [ + "black_medium_small_square" + ] + , "tags": [ + ] + } +, { + "emoji": "◽" + , "description": "white medium small square" + , "aliases": [ + "white_medium_small_square" + ] + , "tags": [ + ] + } +, { + "emoji": "▪️" + , "description": "black small square" + , "aliases": [ + "black_small_square" + ] + , "tags": [ + ] + } +, { + "emoji": "▫️" + , "description": "white small square" + , "aliases": [ + "white_small_square" + ] + , "tags": [ + ] + } +, { + "emoji": "🔺" + , "description": "up-pointing red triangle" + , "aliases": [ + "small_red_triangle" + ] + , "tags": [ + ] + } +, { + "emoji": "🔲" + , "description": "black square button" + , "aliases": [ + "black_square_button" + ] + , "tags": [ + ] + } +, { + "emoji": "🔳" + , "description": "white square button" + , "aliases": [ + "white_square_button" + ] + , "tags": [ + ] + } +, { + "emoji": "⚫" + , "description": "medium black circle" + , "aliases": [ + "black_circle" + ] + , "tags": [ + ] + } +, { + "emoji": "⚪" + , "description": "medium white circle" + , "aliases": [ + "white_circle" + ] + , "tags": [ + ] + } +, { + "emoji": "🔴" + , "description": "large red circle" + , "aliases": [ + "red_circle" + ] + , "tags": [ + ] + } +, { + "emoji": "🔵" + , "description": "large blue circle" + , "aliases": [ + "large_blue_circle" + ] + , "tags": [ + ] + } +, { + "emoji": "🔻" + , "description": "down-pointing red triangle" + , "aliases": [ + "small_red_triangle_down" + ] + , "tags": [ + ] + } +, { + "emoji": "⬜" + , "description": "white large square" + , "aliases": [ + "white_large_square" + ] + , "tags": [ + ] + } +, { + "emoji": "⬛" + , "description": "black large square" + , "aliases": [ + "black_large_square" + ] + , "tags": [ + ] + } +, { + "emoji": "🔶" + , "description": "large orange diamond" + , "aliases": [ + "large_orange_diamond" + ] + , "tags": [ + ] + } +, { + "emoji": "🔷" + , "description": "large blue diamond" + , "aliases": [ + "large_blue_diamond" + ] + , "tags": [ + ] + } +, { + "emoji": "🔸" + , "description": "small orange diamond" + , "aliases": [ + "small_orange_diamond" + ] + , "tags": [ + ] + } +, { + "emoji": "🔹" + , "description": "small blue diamond" + , "aliases": [ + "small_blue_diamond" + ] + , "tags": [ + ] + } +] diff --git a/lib/es6_module_transpiler/tilt/es6_module_transpiler_template.rb b/lib/es6_module_transpiler/tilt/es6_module_transpiler_template.rb index 4081325d50..54f96a52fe 100644 --- a/lib/es6_module_transpiler/tilt/es6_module_transpiler_template.rb +++ b/lib/es6_module_transpiler/tilt/es6_module_transpiler_template.rb @@ -1,4 +1,5 @@ require 'execjs' +require '6to5' module Tilt class ES6ModuleTranspilerTemplate < Tilt::Template @@ -14,6 +15,7 @@ module Tilt def self.create_new_context ctx = V8::Context.new(timeout: 5000) + ctx.eval("var self = this; #{File.read(ES6to5::Source.path)}") ctx.eval("module = {}; exports = {};"); ctx.load("#{Rails.root}/lib/es6_module_transpiler/support/es6-module-transpiler.js") ctx @@ -70,7 +72,9 @@ module Tilt # For backwards compatibility with plugins, for now export the Global format too. # We should eventually have an upgrade system for plugins to use ES6 or some other # resolve based API. - if ENV['DISCOURSE_NO_CONSTANTS'].nil? && scope.logical_path =~ /(discourse|admin)\/(controllers|components|views|routes|mixins|models)\/(.*)/ + if ENV['DISCOURSE_NO_CONSTANTS'].nil? && + scope.logical_path =~ /(discourse|admin)\/(controllers|components|views|routes|mixins|models)\/(.*)/ + type = Regexp.last_match[2] file_name = Regexp.last_match[3].gsub(/[\-\/]/, '_') class_name = file_name.classify @@ -81,7 +85,7 @@ module Tilt end require_name = module_name(scope.root_path, scope.logical_path) - if require_name !~ /\-test$/ + if require_name !~ /\-test$/ && require_name !~ /^discourse\/plugins\// result = "#{class_name}#{type.classify}" # HAX @@ -95,8 +99,16 @@ module Tilt # Include JS code for JSHint unless Rails.env.production? - req_path = "/assets/#{scope.logical_path}.js" - @output << "\nwindow.__jshintSrc = window.__jshintSrc || {}; window.__jshintSrc['#{req_path}'] = #{source.to_json};\n" + if scope.pathname.to_s =~ /js\.es6/ + extension = "js.es6" + elsif scope.pathname.to_s =~ /\.es6/ + extension = "es6" + else + extension = "js" + end + req_path = "/assets/#{scope.logical_path}.#{extension}" + + @output << "\nwindow.__jshintSrc = window.__jshintSrc || {}; window.__jshintSrc['#{req_path}'] = #{data.to_json};\n" end @output @@ -105,7 +117,9 @@ module Tilt private def generate_source(scope) - "new module.exports.Compiler(#{::JSON.generate(data, quirks_mode: true)}, '#{module_name(scope.root_path, scope.logical_path)}', #{compiler_options}).#{compiler_method}()" + js_source = ::JSON.generate(data, quirks_mode: true) + js_source = "to5.transform(#{js_source}, {ast: false, whitelist: ['es6.constants', 'es6.properties.shorthand', 'es6.arrowFunctions', 'es6.blockScoping']})['code']" + "new module.exports.Compiler(#{js_source}, '#{module_name(scope.root_path, scope.logical_path)}', #{compiler_options}).#{compiler_method}()" end def module_name(root_path, logical_path) diff --git a/lib/excerpt_parser.rb b/lib/excerpt_parser.rb index 511daa7d87..bb2606a1a2 100644 --- a/lib/excerpt_parser.rb +++ b/lib/excerpt_parser.rb @@ -17,16 +17,15 @@ class ExcerptParser < Nokogiri::XML::SAX::Document def self.get_excerpt(html, length, options) html ||= '' - if (html.include? 'excerpt') && (SPAN_REGEX === html) - length = html.length - end + length = html.length if html.include?('excerpt') && SPAN_REGEX === html me = self.new(length, options) parser = Nokogiri::HTML::SAX::Parser.new(me) catch(:done) do parser.parse(html) end - me.excerpt.strip! - me.excerpt + excerpt = me.excerpt.strip + excerpt = CGI.unescapeHTML(excerpt) if options[:text_entities] == true + excerpt end def escape_attribute(v) @@ -57,7 +56,7 @@ class ExcerptParser < Nokogiri::XML::SAX::Document elsif attributes["title"] characters("[#{attributes["title"]}]") else - characters("[image]") + characters("[#{I18n.t 'excerpt_image'}]") end characters("(#{attributes['src']})") if @markdown_images diff --git a/lib/flag_query.rb b/lib/flag_query.rb index 2fe90c1c19..aed258c827 100644 --- a/lib/flag_query.rb +++ b/lib/flag_query.rb @@ -6,7 +6,9 @@ module FlagQuery guardian = Guardian.new(current_user) if !guardian.is_admin? - actions = actions.where('category_id in (?)', guardian.allowed_category_ids) + actions = actions.where('category_id IN (:allowed_category_ids) OR archetype = :private_message', + allowed_category_ids: guardian.allowed_category_ids, + private_message: Archetype.private_message) end post_ids = actions.limit(per_page) @@ -107,26 +109,24 @@ module FlagQuery ] end - protected - - def self.flagged_post_actions(filter) - post_actions = PostAction.flags - .joins("INNER JOIN posts ON posts.id = post_actions.post_id") - .joins("INNER JOIN topics ON topics.id = posts.topic_id") - .joins("LEFT JOIN users ON users.id = posts.user_id") - - if filter == "old" - post_actions.where("post_actions.disagreed_at IS NOT NULL OR - post_actions.deferred_at IS NOT NULL OR - post_actions.agreed_at IS NOT NULL") - else - post_actions.active - .where("posts.deleted_at" => nil) - .where("topics.deleted_at" => nil) - end + def self.flagged_post_actions(filter) + post_actions = PostAction.flags + .joins("INNER JOIN posts ON posts.id = post_actions.post_id") + .joins("INNER JOIN topics ON topics.id = posts.topic_id") + .joins("LEFT JOIN users ON users.id = posts.user_id") + if filter == "old" + post_actions.where("post_actions.disagreed_at IS NOT NULL OR + post_actions.deferred_at IS NOT NULL OR + post_actions.agreed_at IS NOT NULL") + else + post_actions.active + .where("posts.deleted_at" => nil) + .where("topics.deleted_at" => nil) end + end + private def self.excerpt(cooked) diff --git a/lib/freedom_patches/better_handlebars_errors.rb b/lib/freedom_patches/better_handlebars_errors.rb new file mode 100644 index 0000000000..0f41bd2781 --- /dev/null +++ b/lib/freedom_patches/better_handlebars_errors.rb @@ -0,0 +1,16 @@ +module Ember + module Handlebars + class Template < Tilt::Template + + # Wrap in an IIFE in development mode to get the correct filename + def compile_ember_handlebars(string) + if ::Rails.env.development? + "(function() { try { return Ember.Handlebars.compile(#{indent(string).inspect}); } catch(err) { throw err; } })()" + else + "Handlebars.compile(#{indent(string).inspect});" + end + end + end + end +end + diff --git a/lib/freedom_patches/ember_compat_handlebars.rb b/lib/freedom_patches/ember_compat_handlebars.rb index 9786194724..b3cdd4285e 100644 --- a/lib/freedom_patches/ember_compat_handlebars.rb +++ b/lib/freedom_patches/ember_compat_handlebars.rb @@ -4,10 +4,6 @@ module Barber class EmberCompatPrecompiler < Barber::Precompiler - def self.call(template) - "Handlebars.template(#{compile(template)})" - end - def sources [handlebars, precompiler] end @@ -18,7 +14,7 @@ module Barber #{File.read(Rails.root + "app/assets/javascripts/discourse/lib/ember_compat_handlebars.js")} var Barber = { precompile: function(string) { - return Discourse.EmberCompatHandlebars.precompile(string).toString(); + return Discourse.EmberCompatHandlebars.precompile(string,false).toString(); } }; END diff --git a/lib/freedom_patches/translate_accelerator.rb b/lib/freedom_patches/translate_accelerator.rb index e878e3374e..af237e9b6f 100644 --- a/lib/freedom_patches/translate_accelerator.rb +++ b/lib/freedom_patches/translate_accelerator.rb @@ -1,4 +1,35 @@ +# This patch performs 2 functions +# +# 1. It caches all translations which drastically improves +# translation performance in an LRU cache +# +# 2. It patches I18n so it only loads the translations it needs +# on demand +# +# This patch depends on the convention that locale yml files must be named [locale_name].yml + module I18n + module Backend + + class Simple + def available_locales + # in case you are wondering this is: + # Dir.glob( File.join(Rails.root, 'config', 'locales', 'client.*.yml') ) + # .map {|x| x.split('.')[-2]}.sort + LocaleSiteSetting.supported_locales.map(&:to_sym) + end + end + + module Base + # force explicit loading + def load_translations(*filenames) + unless filenames.empty? + filenames.flatten.each { |filename| load_file(filename) } + end + end + + end + end # this accelerates translation a tiny bit (halves the time it takes) class << self alias_method :translate_no_cache, :translate @@ -6,16 +37,35 @@ module I18n LRU_CACHE_SIZE = 2000 def reload! + @loaded_locales = [] @cache = nil reload_no_cache! end + LOAD_MUTEX = Mutex.new + def load_locale(locale) + LOAD_MUTEX.synchronize do + return if @loaded_locales.include?(locale) + + if @loaded_locales.empty? + # load all rb files + I18n.backend.load_translations(I18n.load_path.grep(/\.rb$/)) + end + + # load it + I18n.backend.load_translations(I18n.load_path.grep Regexp.new("\\.#{locale}\\.yml$")) + + @loaded_locales << locale + end + end + def translate(*args) @cache ||= LruRedux::ThreadSafeCache.new(LRU_CACHE_SIZE) found = true k = [args, config.locale, config.backend.object_id] t = @cache.fetch(k) { found = false } unless found + load_locale(config.locale) unless @loaded_locales.include?(config.locale) begin t = translate_no_cache(*args) rescue MissingInterpolationArgument diff --git a/lib/guardian.rb b/lib/guardian.rb index f0998e0577..d644e5485c 100644 --- a/lib/guardian.rb +++ b/lib/guardian.rb @@ -4,6 +4,7 @@ require_dependency 'guardian/post_guardian' require_dependency 'guardian/topic_guardian' require_dependency 'guardian/user_guardian' require_dependency 'guardian/post_revision_guardian' +require_dependency 'guardian/group_guardian' # The guardian is responsible for confirming access to various site resources and operations class Guardian @@ -13,6 +14,7 @@ class Guardian include TopicGuardian include UserGuardian include PostRevisionGuardian + include GroupGuardian class AnonymousUser def blank?; true; end @@ -249,6 +251,12 @@ class Guardian @can_see_emails end + def can_export_entity?(entity_type) + return true if is_staff? + return false if entity_type == "admin" + UserExport.where(user_id: @user.id, created_at: (Time.zone.now.beginning_of_day..Time.zone.now.end_of_day)).count == 0 + end + private def is_my_own?(obj) diff --git a/lib/guardian/group_guardian.rb b/lib/guardian/group_guardian.rb new file mode 100644 index 0000000000..308d95b2be --- /dev/null +++ b/lib/guardian/group_guardian.rb @@ -0,0 +1,11 @@ +#mixin for all guardian methods dealing with group permissions +module GroupGuardian + + # Edit authority for groups means membership changes only. + # Automatic groups are not represented in the GROUP_USERS + # table and thus do not allow membership changes. + def can_edit_group?(group) + (group.managers.include?(user) || is_admin?) && !group.automatic + end + +end diff --git a/lib/guardian/post_guardian.rb b/lib/guardian/post_guardian.rb index 114bb14128..4e6c579540 100644 --- a/lib/guardian/post_guardian.rb +++ b/lib/guardian/post_guardian.rb @@ -9,14 +9,18 @@ module PostGuardian already_did_flagging = taken.any? && (taken & PostActionType.flag_types.values).any? if authenticated? && post + + return false if action_key == :notify_moderators && !SiteSetting.enable_private_messages + # we allow flagging for trust level 1 and higher - (is_flag && @user.has_trust_level?(TrustLevel[1]) && not(already_did_flagging)) || + # always allowed for private messages + (is_flag && not(already_did_flagging) && (@user.has_trust_level?(TrustLevel[1]) || post.topic.private_message?)) || # not a flagging action, and haven't done it already not(is_flag || already_taken_this_action) && # nothing except flagging on archived topics - not(post.topic.archived?) && + not(post.topic.try(:archived?)) && # nothing except flagging on deleted posts not(post.trashed?) && @@ -27,6 +31,9 @@ module PostGuardian # new users can't notify_user because they are not allowed to send private messages not(action_key == :notify_user && !@user.has_trust_level?(TrustLevel[1])) && + # can't send private messages if they're disabled globally + not(action_key == :notify_user && !SiteSetting.enable_private_messages) && + # no voting more than once on single vote topics not(action_key == :vote && opts[:voted_in_topic] && post.topic.has_meta_data_boolean?(:single_vote)) end @@ -169,7 +176,7 @@ module PostGuardian end def can_rebake? - is_staff? + is_staff? || @user.has_trust_level?(TrustLevel[4]) end def can_see_flagged_posts? diff --git a/lib/guardian/topic_guardian.rb b/lib/guardian/topic_guardian.rb index f2829fb830..a3ec5abb65 100644 --- a/lib/guardian/topic_guardian.rb +++ b/lib/guardian/topic_guardian.rb @@ -67,6 +67,19 @@ module TopicGuardian # not secure, or I can see it !topic.read_restricted_category? || can_see_category?(topic.category) - end + + def filter_allowed_categories(records) + unless is_admin? + allowed_ids = allowed_category_ids + if allowed_ids.length > 0 + records = records.where('topics.category_id IS NULL or topics.category_id IN (?)', allowed_ids) + else + records = records.where('topics.category_id IS NULL') + end + records = records.references(:categories) + end + records + end + end diff --git a/lib/guardian/user_guardian.rb b/lib/guardian/user_guardian.rb index ba8689f6e1..6c0206d525 100644 --- a/lib/guardian/user_guardian.rb +++ b/lib/guardian/user_guardian.rb @@ -51,4 +51,12 @@ module UserGuardian is_admin? || (is_staff? && SiteSetting.show_email_on_profile) end + def restrict_user_fields?(user) + user.trust_level == TrustLevel[0] && anonymous? + end + + def can_see_staff_info?(user) + user && is_staff? + end + end diff --git a/lib/ip_addr.rb b/lib/ip_addr.rb new file mode 100644 index 0000000000..c80501c3bb --- /dev/null +++ b/lib/ip_addr.rb @@ -0,0 +1,35 @@ +class IPAddr + + def self.handle_wildcards(val) + return if val.blank? + + num_wildcards = val.count('*') + + return val if num_wildcards == 0 + + # strip ranges like "/16" from the end if present + v = val.gsub(/\/.*/, '') + + return if v[v.index('*')..-1] =~ /[^\.\*]/ + + parts = v.split('.') + (4 - parts.size).times { parts << '*' } # support strings like 192.* + v = parts.join('.') + + "#{v.gsub('*', '0')}/#{32 - (v.count('*') * 8)}" + end + + def to_cidr_s + if @addr + mask = @mask_addr.to_s(2).count('1') + if mask == 32 + to_s + else + "#{to_s}/#{mask}" + end + else + nil + end + end + +end diff --git a/lib/js_locale_helper.rb b/lib/js_locale_helper.rb index e040350ceb..2211015300 100644 --- a/lib/js_locale_helper.rb +++ b/lib/js_locale_helper.rb @@ -13,6 +13,7 @@ module JsLocaleHelper Dir["#{Rails.root}/plugins/*/config/locales/client.#{locale_str}.yml"].each do |file| plugin_translations.deep_merge! YAML::load(File.open(file)) end + # merge translations (plugin translations overwrite default translations) translations[locale_str]['js'].deep_merge!(plugin_translations[locale_str]['js']) if translations[locale_str] && plugin_translations[locale_str] && plugin_translations[locale_str]['js'] @@ -22,7 +23,7 @@ module JsLocaleHelper # For now, let's leave it split out in the translation file in case we want to split # it again later, so we'll merge the JSON ourselves. admin_contents = translations[locale_str].delete('admin_js') - translations[locale_str]['js'].merge!(admin_contents) if admin_contents.present? + translations[locale_str]['js'].deep_merge!(admin_contents) if admin_contents.present? translations[locale_str]['js'].deep_merge!(plugin_translations[locale_str]['admin_js']) if translations[locale_str] && plugin_translations[locale_str] && plugin_translations[locale_str]['admin_js'] message_formats = strip_out_message_formats!(translations[locale_str]['js']) diff --git a/lib/middleware/anonymous_cache.rb b/lib/middleware/anonymous_cache.rb index db8192fc12..d315c53a50 100644 --- a/lib/middleware/anonymous_cache.rb +++ b/lib/middleware/anonymous_cache.rb @@ -60,8 +60,12 @@ module Middleware @env["REQUEST_METHOD"] == "GET" end + def has_auth_cookie? + CurrentUser.has_auth_cookie?(@env) + end + def cacheable? - !!(!CurrentUser.has_auth_cookie?(@env) && get?) + !!(!has_auth_cookie? && get?) end def cached diff --git a/lib/middleware/request_tracker.rb b/lib/middleware/request_tracker.rb new file mode 100644 index 0000000000..3922dd193a --- /dev/null +++ b/lib/middleware/request_tracker.rb @@ -0,0 +1,79 @@ +require_dependency 'middleware/anonymous_cache' + +class Middleware::RequestTracker + + def initialize(app, settings={}) + @app = app + end + + def self.log_request_on_site(data,host) + RailsMultisite::ConnectionManagement.with_hostname(host) do + log_request(data) + end + end + + def self.log_request(data) + status = data[:status] + track_view = data[:track_view] + + if track_view + if data[:is_crawler] + ApplicationRequest.increment!(:page_view_crawler) + elsif data[:has_auth_cookie] + ApplicationRequest.increment!(:page_view_logged_in) + else + ApplicationRequest.increment!(:page_view_anon) + end + end + + ApplicationRequest.increment!(:http_total) + + if status >= 500 + ApplicationRequest.increment!(:http_5xx) + elsif data[:is_background] + ApplicationRequest.increment!(:http_background) + elsif status >= 400 + ApplicationRequest.increment!(:http_4xx) + elsif status >= 300 + ApplicationRequest.increment!(:http_3xx) + elsif status >= 200 && status < 300 + ApplicationRequest.increment!(:http_2xx) + end + + end + + TRACK_VIEW = "HTTP_DISCOURSE_TRACK_VIEW".freeze + CONTENT_TYPE = "Content-Type".freeze + def self.get_data(env,result) + + status,headers = result + status = status.to_i + + helper = Middleware::AnonymousCache::Helper.new(env) + request = Rack::Request.new(env) + { + status: status, + is_crawler: helper.is_crawler?, + has_auth_cookie: helper.has_auth_cookie?, + is_background: request.path =~ /^\/message-bus\// || request.path == /\/topics\/timings/, + track_view: (env[TRACK_VIEW] || (request.get? && !request.xhr? && headers[CONTENT_TYPE] =~ /text\/html/)) && status == 200 + } + end + + def call(env) + result = @app.call(env) + ensure + + # we got to skip this on error ... its just logging + data = self.class.get_data(env,result) rescue nil + host = RailsMultisite::ConnectionManagement.host(env) + + if data + Scheduler::Defer.later("Track view", _db=nil) do + self.class.log_request_on_site(data,host) + end + end + + end + +end diff --git a/lib/onebox/engine/discourse_local_onebox.rb b/lib/onebox/engine/discourse_local_onebox.rb index 8245821490..7857f55556 100644 --- a/lib/onebox/engine/discourse_local_onebox.rb +++ b/lib/onebox/engine/discourse_local_onebox.rb @@ -14,7 +14,6 @@ module Onebox uri = URI::parse(@url) route = Rails.application.routes.recognize_path(uri.path) - args = {original_url: @url} # Figure out what kind of onebox to show based on the URL case route[:controller] @@ -57,37 +56,23 @@ module Onebox } end - category_name = '' - parent_category_name = '' - category = topic.category - if category && !category.uncategorized? - category_name = "#{category.name}" - if !category.parent_category_id.nil? - parent_category = Category.find_by(id: category.parent_category_id) - parent_category_name = "#{parent_category.name}" - end - end - quote = post.excerpt(SiteSetting.post_onebox_maxlength) - args.merge! title: topic.title, - avatar: PrettyText.avatar_img(topic.user.avatar_template, 'tiny'), - posts_count: topic.posts_count, - last_post: FreedomPatches::Rails4.time_ago_in_words(topic.last_posted_at, false, scope: :'datetime.distance_in_words_verbose'), - age: FreedomPatches::Rails4.time_ago_in_words(topic.created_at, false, scope: :'datetime.distance_in_words_verbose'), - views: topic.views, - posters: posters, - quote: quote, - category_name: category_name, - parent_category_name: parent_category_name, - topic: topic.id + args = { original_url: @url, + title: topic.title, + avatar: PrettyText.avatar_img(topic.user.avatar_template, 'tiny'), + posts_count: topic.posts_count, + last_post: FreedomPatches::Rails4.time_ago_in_words(topic.last_posted_at, false, scope: :'datetime.distance_in_words_verbose'), + age: FreedomPatches::Rails4.time_ago_in_words(topic.created_at, false, scope: :'datetime.distance_in_words_verbose'), + views: topic.views, + posters: posters, + quote: quote, + category_html: CategoryBadge.html_for(topic.category), + topic: topic.id } - @template = 'topic' + return Mustache.render(File.read("#{Rails.root}/lib/onebox/templates/discourse_topic_onebox.hbs"), args) end - end - return nil unless @template - Mustache.render(File.read("#{Rails.root}/lib/onebox/templates/discourse_#{@template}_onebox.hbs"), args) rescue ActionController::RoutingError nil end diff --git a/lib/onebox/templates/discourse_topic_onebox.hbs b/lib/onebox/templates/discourse_topic_onebox.hbs index 2446ec6a9a..a070f974f1 100644 --- a/lib/onebox/templates/discourse_topic_onebox.hbs +++ b/lib/onebox/templates/discourse_topic_onebox.hbs @@ -2,7 +2,7 @@
{{{avatar}}} - {{title}} {{{parent_category_name}}} {{{category_name}}} + {{title}} {{{category_html}}}
{{{quote}}}
diff --git a/lib/plugin/instance.rb b/lib/plugin/instance.rb index a3e008d734..99de257293 100644 --- a/lib/plugin/instance.rb +++ b/lib/plugin/instance.rb @@ -5,8 +5,17 @@ require_dependency 'plugin/auth_provider' class Plugin::Instance - attr_reader :auth_providers, :assets, :styles, :color_schemes attr_accessor :path, :metadata + attr_reader :admin_route + + # Memoized array readers + [:assets, :auth_providers, :color_schemes, :initializers, :javascripts, :styles].each do |att| + class_eval %Q{ + def #{att} + @#{att} ||= [] + end + } + end def self.find_all(parent_path) [].tap { |plugins| @@ -22,22 +31,48 @@ class Plugin::Instance def initialize(metadata=nil, path=nil) @metadata = metadata @path = path - @assets = [] - @color_schemes = [] - # Automatically include all ES6 JS files if @path - dir = File.dirname(@path) - Dir.glob("#{dir}/assets/javascripts/**/*.js.es6") do |f| - relative = f.sub("#{dir}/assets/", "") - register_asset(relative) - end + # Automatically include all ES6 JS and hbs files + root_path = "#{File.dirname(@path)}/assets/javascripts" + DiscoursePluginRegistry.register_glob(root_path, 'js.es6') + DiscoursePluginRegistry.register_glob(root_path, 'hbs') end - end - def name - metadata.name + def add_admin_route(label, location) + @admin_route = {label: label, location: location} + end + + def enabled? + return @enabled_site_setting ? SiteSetting.send(@enabled_site_setting) : true + end + + delegate :name, to: :metadata + + def add_to_serializer(serializer, attr, &block) + klass = "#{serializer.to_s.classify}Serializer".constantize + klass.attributes(attr) + klass.send(:define_method, attr, &block) + + # Don't include serialized methods if the plugin is disabled + plugin = self + klass.send(:define_method, "include_#{attr}?") do + plugin.enabled? + end + end + + # Extend a class but check that the plugin is enabled + def add_to_class(klass, attr, &block) + klass = klass.to_s.classify.constantize + + hidden_method_name = "#{attr}_without_enable_check".to_sym + klass.send(:define_method, hidden_method_name, &block) + + plugin = self + klass.send(:define_method, attr) do |*args| + send(hidden_method_name, *args) if plugin.enabled? + end end # will make sure all the assets this plugin needs are registered @@ -82,8 +117,14 @@ class Plugin::Instance end def after_initialize(&block) - @after_initialize ||= [] - @after_initialize << block + initializers << block + end + + # A proxy to `DiscourseEvent.on` which does nothing if the plugin is disabled + def on(event_name, &block) + DiscourseEvent.on(event_name) do |*args| + block.call(*args) if enabled? + end end def notify_after_initialize @@ -91,21 +132,22 @@ class Plugin::Instance ColorScheme.create_from_base(name: c[:name], colors: c[:colors]) unless ColorScheme.where(name: c[:name]).exists? end - if @after_initialize - @after_initialize.each do |callback| - callback.call - end + initializers.each do |callback| + callback.call(self) end end + def listen_for(event_name) + return unless self.respond_to?(event_name) + DiscourseEvent.on(event_name, &self.method(event_name)) + end + def register_css(style) - @styles ||= [] - @styles << style + styles << style end def register_javascript(js) - @javascripts ||= [] - @javascripts << js + javascripts << js end def register_custom_html(hash) @@ -123,29 +165,24 @@ class Plugin::Instance end def automatic_assets - css = "" - js = "" + css = styles.join("\n") + js = javascripts.join("\n") - css = @styles.join("\n") if @styles - js = @javascripts.join("\n") if @javascripts + auth_providers.each do |auth| + overrides = "" + overrides = ", titleOverride: '#{auth.title}'" if auth.title + overrides << ", messageOverride: '#{auth.message}'" if auth.message + overrides << ", frameWidth: '#{auth.frame_width}'" if auth.frame_width + overrides << ", frameHeight: '#{auth.frame_height}'" if auth.frame_height - unless auth_providers.blank? - auth_providers.each do |auth| - overrides = "" - overrides = ", titleOverride: '#{auth.title}'" if auth.title - overrides << ", messageOverride: '#{auth.message}'" if auth.message - overrides << ", frameWidth: '#{auth.frame_width}'" if auth.frame_width - overrides << ", frameHeight: '#{auth.frame_height}'" if auth.frame_height + js << "Discourse.LoginMethod.register(Discourse.LoginMethod.create({name: '#{auth.name}'#{overrides}}));\n" - js << "Discourse.LoginMethod.register(Discourse.LoginMethod.create({name: '#{auth.name}'#{overrides}}));\n" + if auth.glyph + css << ".btn-social.#{auth.name}:before{ content: '#{auth.glyph}'; }\n" + end - if auth.glyph - css << ".btn-social.#{auth.name}:before{ content: '#{auth.glyph}'; }\n" - end - - if auth.background_color - css << ".btn-social.#{auth.name}{ background: #{auth.background_color}; }\n" - end + if auth.background_color + css << ".btn-social.#{auth.name}{ background: #{auth.background_color}; }\n" end end @@ -172,13 +209,12 @@ class Plugin::Instance if auto_assets = generate_automatic_assets! assets.concat auto_assets.map{|a| [a]} end - unless assets.blank? - register_assets! - # TODO possibly amend this to a rails engine - Rails.configuration.assets.paths << auto_generated_path - Rails.configuration.assets.paths << File.dirname(path) + "/assets" - end + register_assets! unless assets.blank? + + # TODO possibly amend this to a rails engine + Rails.configuration.assets.paths << auto_generated_path + Rails.configuration.assets.paths << File.dirname(path) + "/assets" public_data = File.dirname(path) + "/public" if Dir.exists?(public_data) @@ -193,12 +229,11 @@ class Plugin::Instance def auth_provider(opts) - @auth_providers ||= [] provider = Plugin::AuthProvider.new [:glyph, :background_color, :title, :message, :frame_width, :frame_height, :authenticator].each do |sym| provider.send "#{sym}=", opts.delete(sym) end - @auth_providers << provider + auth_providers << provider end @@ -232,35 +267,15 @@ class Plugin::Instance end end + def enabled_site_setting(setting) + @enabled_site_setting = setting + end + protected def register_assets! assets.each do |asset, opts| - if asset =~ /\.js$|\.js\.erb$|\.js\.es6$/ - if opts == :admin - DiscoursePluginRegistry.admin_javascripts << asset - else - if opts == :server_side - DiscoursePluginRegistry.server_side_javascripts << asset - end - DiscoursePluginRegistry.javascripts << asset - end - elsif asset =~ /\.css$|\.scss$/ - if opts == :mobile - DiscoursePluginRegistry.mobile_stylesheets << asset - elsif opts == :desktop - DiscoursePluginRegistry.desktop_stylesheets << asset - elsif opts == :variables - DiscoursePluginRegistry.sass_variables << asset - else - DiscoursePluginRegistry.stylesheets << asset - end - - elsif asset =~ /\.hbs$/ - DiscoursePluginRegistry.handlebars << asset - elsif asset =~ /\.js\.handlebars$/ - DiscoursePluginRegistry.handlebars << asset - end + DiscoursePluginRegistry.register_asset(asset, opts) end end diff --git a/lib/plugin/metadata.rb b/lib/plugin/metadata.rb index 65686ede45..d031e2b211 100644 --- a/lib/plugin/metadata.rb +++ b/lib/plugin/metadata.rb @@ -2,7 +2,7 @@ module Plugin; end class Plugin::Metadata - FIELDS ||= [:name, :about, :version, :authors] + FIELDS ||= [:name, :about, :version, :authors, :url] attr_accessor *FIELDS def self.parse(text) diff --git a/lib/post_creator.rb b/lib/post_creator.rb index af0a1c3e7f..d87aa172e9 100644 --- a/lib/post_creator.rb +++ b/lib/post_creator.rb @@ -20,6 +20,14 @@ class PostCreator # created_at - Post creation time (optional) # auto_track - Automatically track this topic if needed (default true) # custom_fields - Custom fields to be added to the post, Hash (default nil) + # post_type - Whether this is a regular post or moderator post. + # no_bump - Do not cause this post to bump the topic. + # cooking_options - Options for rendering the text + # cook_method - Method of cooking the post. + # :regular - Pass through Markdown parser and strip bad HTML + # :raw_html - Perform no processing + # via_email - Mark this post as arriving via email + # raw_email - Full text of arriving email (to store) # # When replying to a topic: # topic_id - topic we're replying to @@ -33,7 +41,8 @@ class PostCreator # target_usernames - comma delimited list of usernames for membership (private message) # target_group_names - comma delimited list of groups for membership (private message) # meta_data - Topic meta data hash - # cooking_options - Options for rendering the text + # created_at - Topic creation time (optional) + # pinned_at - Topic pinned time (optional) # def initialize(user, opts) # TODO: we should reload user in case it is tainted, should take in a user_id as opposed to user diff --git a/lib/post_destroyer.rb b/lib/post_destroyer.rb index 9bba2444d4..fe99b6c9fe 100644 --- a/lib/post_destroyer.rb +++ b/lib/post_destroyer.rb @@ -54,8 +54,9 @@ class PostDestroyer elsif @user.staff? || @user.id == @post.user_id user_recovered end - @post.topic.recover! if @post.post_number == 1 - @post.topic.update_statistics + topic = Topic.with_deleted.find @post.topic_id + topic.recover! if @post.post_number == 1 + topic.update_statistics end def staff_recovered @@ -88,6 +89,7 @@ class PostDestroyer end update_associated_category_latest_topic update_user_counts + TopicUser.update_post_action_cache(topic_id: @post.topic_id) end @post.publish_change_to_clients! :deleted if @post.topic @@ -95,11 +97,13 @@ class PostDestroyer # When a user 'deletes' their own post. We just change the text. def mark_for_deletion - Post.transaction do - @post.revise(@user, { raw: I18n.t('js.post.deleted_by_author', count: SiteSetting.delete_removed_posts_after) }, force_new_version: true) - @post.update_column(:user_deleted, true) - @post.update_flagged_posts_count - @post.topic_links.each(&:destroy) + I18n.with_locale(SiteSetting.default_locale) do + Post.transaction do + @post.revise(@user, { raw: I18n.t('js.post.deleted_by_author', count: SiteSetting.delete_removed_posts_after) }, force_new_version: true) + @post.update_column(:user_deleted, true) + @post.update_flagged_posts_count + @post.topic_links.each(&:destroy) + end end end @@ -112,16 +116,15 @@ class PostDestroyer end end - private def make_previous_post_the_last_one last_post = Post.where("topic_id = ? and id <> ?", @post.topic_id, @post.id).order('created_at desc').limit(1).first if last_post.present? @post.topic.update_attributes( - last_posted_at: last_post.created_at, - last_post_user_id: last_post.user_id, - highest_post_number: last_post.post_number + last_posted_at: last_post.created_at, + last_post_user_id: last_post.user_id, + highest_post_number: last_post.post_number ) end end diff --git a/lib/post_revisor.rb b/lib/post_revisor.rb index dc2efb994b..c612e04535 100644 --- a/lib/post_revisor.rb +++ b/lib/post_revisor.rb @@ -2,8 +2,43 @@ require "edit_rate_limiter" class PostRevisor + # Helps us track changes to a topic. + # + # It's passed to `track_topic_fields` callbacks so they can record if they + # changed a value or not. This is needed for things like custom fields. + class TopicChanges + attr_reader :topic, :user + + def initialize(topic, user) + @topic = topic + @user = user + @changed = {} + @errored = false + end + + def errored? + @errored + end + + def guardian + @guardian ||= Guardian.new(@user) + end + + def record_change(field_name, previous_val, new_val) + return if previous_val == new_val + diff[field_name] = [previous_val, new_val] + end + + def check_result(res) + @errored = true if !res + end + + def diff + @diff ||= {} + end + end + POST_TRACKED_FIELDS = %w{raw cooked edit_reason user_id wiki post_type} - TOPIC_TRACKED_FIELDS = %w{title category_id} attr_reader :category_changed @@ -12,6 +47,31 @@ class PostRevisor @topic = topic || post.topic end + def self.tracked_topic_fields + @@tracked_topic_fields ||= {} + @@tracked_topic_fields + end + + def self.track_topic_field(field, &block) + tracked_topic_fields[field] = block + + # Define it in the serializer unless it already has been defined + unless PostRevisionSerializer.instance_methods(false).include?("#{field}_changes".to_sym) + PostRevisionSerializer.add_compared_field(field) + end + end + + # Fields we want to record revisions for by default + track_topic_field(:title) do |tc, title| + tc.record_change('title', tc.topic.title, title) + tc.topic.title = title + end + + track_topic_field(:category_id) do |tc, category_id| + tc.record_change('category_id', tc.topic.category_id, category_id) + tc.check_result(tc.topic.change_category_to_id(category_id)) + end + # AVAILABLE OPTIONS: # - revised_at: changes the date of the revision # - force_new_version: bypass ninja-edit window @@ -23,6 +83,8 @@ class PostRevisor @fields = fields.with_indifferent_access @opts = opts + @topic_changes = TopicChanges.new(@topic, editor) + # some normalization @fields[:raw] = cleanup_whitespaces(@fields[:raw]) if @fields.has_key?(:raw) @fields[:user_id] = @fields[:user_id].to_i if @fields.has_key?(:user_id) @@ -40,10 +102,14 @@ class PostRevisor @version_changed = false @post_successfully_saved = true - @topic_successfully_saved = true - @validate_post = @opts[:validate_post] || !@opts[:skip_validations] - @validate_topic = @opts[:validate_topic] || !@opts[:skip_validations] + @validate_post = true + @validate_post = @opts[:validate_post] if @opts.has_key?(:validate_post) + @validate_post = !@opts[:skip_validations] if @opts.has_key?(:skip_validations) + + @validate_topic = true + @validate_topic = @opts[:validate_topic] if @opts.has_key?(:validate_topic) + @validate_topic = !@opts[:validate_topic] if @opts.has_key?(:skip_validations) Post.transaction do revise_post @@ -89,10 +155,7 @@ class PostRevisor end def topic_changed? - TOPIC_TRACKED_FIELDS.each do |field| - return true if @fields.has_key?(field) && @fields[field] != @topic.send(field) - end - false + PostRevisor.tracked_topic_fields.keys.any? {|f| @fields.has_key?(f)} end def revise_post @@ -148,6 +211,7 @@ class PostRevisor clear_flags_and_unhide_post @post.extract_quoted_post_numbers + @post_successfully_saved = @post.save(validate: @validate_post) @post.save_reply_relationships end @@ -169,10 +233,16 @@ class PostRevisor end def update_topic - @topic.title = @fields[:title] if @fields.has_key?(:title) Topic.transaction do - @topic_successfully_saved = @topic.change_category_to_id(@fields[:category_id]) if @fields.has_key?(:category_id) - @topic_successfully_saved &&= @topic.save(validate: @validate_topic) + PostRevisor.tracked_topic_fields.each do |f, cb| + if !@topic_changes.errored? && @fields.has_key?(f) + cb.call(@topic_changes, @fields[f]) + end + end + + unless @topic_changes.errored? + @topic_changes.check_result(@topic.save(validate: @validate_topic)) + end end end @@ -183,7 +253,7 @@ class PostRevisor end def create_revision - modifications = post_changes.merge(topic_changes) + modifications = post_changes.merge(@topic_changes.diff) PostRevision.create!( user_id: @post.last_editor_id, post_id: @post.id, @@ -195,7 +265,7 @@ class PostRevisor def update_revision return unless revision = PostRevision.find_by(post_id: @post.id, number: @post.version) revision.user_id = @post.last_editor_id - modifications = post_changes.merge(topic_changes) + modifications = post_changes.merge(@topic_changes.diff) modifications.keys.each do |field| if revision.modifications.has_key?(field) old_value = revision.modifications[field][0] @@ -205,17 +275,13 @@ class PostRevisor revision.modifications[field] = modifications[field] end end - revision.save + revision.save if modifications.length end def post_changes @post.previous_changes.slice(*POST_TRACKED_FIELDS) end - def topic_changes - @topic.previous_changes.slice(*TOPIC_TRACKED_FIELDS) - end - def perform_edit return if bypass_rate_limiter? EditRateLimiter.new(@editor).performed! @@ -306,7 +372,8 @@ class PostRevisor end def successfully_saved_post_and_topic - @post_successfully_saved && @topic_successfully_saved + @post_successfully_saved && !@topic_changes.errored? end end + diff --git a/lib/pretty_text.rb b/lib/pretty_text.rb index 81846f75e7..d095bd0c6a 100644 --- a/lib/pretty_text.rb +++ b/lib/pretty_text.rb @@ -1,11 +1,13 @@ require 'v8' require 'nokogiri' +require_dependency 'url_helper' require_dependency 'excerpt_parser' require_dependency 'post' module PrettyText class Helpers + include UrlHelper def t(key, opts) key = "js." + key @@ -21,15 +23,15 @@ module PrettyText # function here are available to v8 def avatar_template(username) return "" unless username - user = User.find_by(username_lower: username.downcase) - user.avatar_template if user.present? + return "" unless user.present? + schemaless absolute user.avatar_template end def is_username_valid(username) return false unless username username = username.downcase - return User.exec_sql('SELECT 1 FROM users WHERE username_lower = ?', username).values.length == 1 + User.exec_sql('SELECT 1 FROM users WHERE username_lower = ?', username).values.length == 1 end end @@ -75,12 +77,14 @@ module PrettyText "app/assets/javascripts/discourse/lib/markdown.js" ) - Dir["#{Rails.root}/app/assets/javascripts/discourse/dialects/**.js"].sort.each do |dialect| - unless dialect =~ /\/dialect\.js$/ - ctx.load(dialect) - end + Dir["#{app_root}/app/assets/javascripts/discourse/dialects/**.js"].sort.each do |dialect| + ctx.load(dialect) unless dialect =~ /\/dialect\.js$/ end + # emojis + emoji = ERB.new(File.read("#{app_root}/app/assets/javascripts/discourse/lib/emoji/emoji.js.erb")) + ctx.eval(emoji.result) + # Load server side javascripts if DiscoursePluginRegistry.server_side_javascripts.present? DiscoursePluginRegistry.server_side_javascripts.each do |ssjs| @@ -94,8 +98,8 @@ module PrettyText end end - ctx['quoteTemplate'] = File.open(app_root + 'app/assets/javascripts/discourse/templates/quote.hbs') {|f| f.read} - ctx['quoteEmailTemplate'] = File.open(app_root + 'lib/assets/quote_email.hbs') {|f| f.read} + ctx['quoteTemplate'] = File.read("#{app_root}/app/assets/javascripts/discourse/templates/quote.hbs") + ctx['quoteEmailTemplate'] = File.read("#{app_root}/lib/assets/quote_email.hbs") ctx.eval("HANDLEBARS_TEMPLATES = { 'quote': Handlebars.compile(quoteTemplate), 'quote_email': Handlebars.compile(quoteEmailTemplate), @@ -126,7 +130,9 @@ module PrettyText context.eval("Discourse.SiteSettings = #{SiteSetting.client_settings_json};") context.eval("Discourse.CDN = '#{Rails.configuration.action_controller.asset_host}';") context.eval("Discourse.BaseUrl = 'http://#{RailsMultisite::ConnectionManagement.current_hostname}';") - context.eval("Discourse.getURL = function(url) {return '#{Discourse::base_uri}' + url};") + + context.eval("Discourse.getURL = function(url) { return '#{Discourse::base_uri}' + url };") + context.eval("Discourse.getURLWithCDN = function(url) { url = Discourse.getURL(url); if (Discourse.CDN) { url = Discourse.CDN + url; } return url; };") end def self.markdown(text, opts=nil) @@ -152,6 +158,11 @@ module PrettyText end end + # custom emojis + Emoji.custom.each do |emoji| + context.eval("Discourse.Dialect.registerEmoji('#{emoji.name}', '#{emoji.url}');") + end + context.eval('opts["mentionLookup"] = function(u){return helpers.is_username_valid(u);}') context.eval('opts["lookupAvatar"] = function(p){return Discourse.Utilities.avatarImg({size: "tiny", avatarTemplate: helpers.avatar_template(p)});}') baked = context.eval('Discourse.Markdown.markdownConverter(opts).makeHtml(raw)') diff --git a/lib/rate_limiter.rb b/lib/rate_limiter.rb index 817d7389a4..b3e544912f 100644 --- a/lib/rate_limiter.rb +++ b/lib/rate_limiter.rb @@ -4,6 +4,10 @@ require_dependency 'rate_limiter/on_create_record' # A redis backed rate limiter. class RateLimiter + def self.key_prefix + "l-rate-limit:" + end + def self.disable @disabled = true end @@ -17,9 +21,13 @@ class RateLimiter @disabled || Rails.env.test? end + def self.clear_all! + $redis.delete_prefixed(RateLimiter.key_prefix) + end + def initialize(user, key, max, secs) @user = user - @key = "l-rate-limit:#{@user && @user.id}:#{key}" + @key = "#{RateLimiter.key_prefix}:#{@user && @user.id}:#{key}" @max = max @secs = secs end @@ -64,10 +72,10 @@ class RateLimiter end def is_under_limit? - # number of events in buffer less than max allowed? OR - ($redis.llen(@key) < @max) || - # age bigger than silding window size? - (age_of_oldest > @secs) + # number of events in buffer less than max allowed? OR + ($redis.llen(@key) < @max) || + # age bigger than silding window size? + (age_of_oldest > @secs) end def rate_unlimited? diff --git a/lib/sass/discourse_sass_importer.rb b/lib/sass/discourse_sass_importer.rb index 26721cdbb6..b9ad83da3e 100644 --- a/lib/sass/discourse_sass_importer.rb +++ b/lib/sass/discourse_sass_importer.rb @@ -50,7 +50,7 @@ class DiscourseSassImporter < Sass::Importers::Filesystem contents = "" Category.where('background_url IS NOT NULL').each do |c| if c.background_url.present? - contents << "body.category-#{c.id} { background-image: url(#{c.background_url}) }\n" + contents << "body.category-#{c.full_slug} { background-image: url(#{c.background_url}) }\n" end end return Sass::Engine.new(contents, options.merge( diff --git a/lib/sass/discourse_stylesheets.rb b/lib/sass/discourse_stylesheets.rb index 028def076e..1ad84bc30e 100644 --- a/lib/sass/discourse_stylesheets.rb +++ b/lib/sass/discourse_stylesheets.rb @@ -3,9 +3,9 @@ require_dependency 'distributed_cache' class DiscourseStylesheets - CACHE_PATH = 'uploads/stylesheet-cache' - MANIFEST_DIR = "#{Rails.root}/tmp/cache/assets/#{Rails.env}" - MANIFEST_FULL_PATH = "#{MANIFEST_DIR}/stylesheet-manifest" + CACHE_PATH ||= 'uploads/stylesheet-cache' + MANIFEST_DIR ||= "#{Rails.root}/tmp/cache/assets/#{Rails.env}" + MANIFEST_FULL_PATH ||= "#{MANIFEST_DIR}/stylesheet-manifest" @lock = Mutex.new @@ -23,7 +23,7 @@ class DiscourseStylesheets builder = self.new(target) builder.compile unless File.exists?(builder.stylesheet_fullpath) builder.ensure_digestless_file - tag = %[] + tag = %[] cache[target] = tag @@ -56,10 +56,17 @@ class DiscourseStylesheets end def self.max_file_mtime - [ "#{Rails.root}/app/assets/stylesheets/**/*.*css", - "#{Rails.root}/plugins/**/*.*css", - "#{Rails.root}/plugins/**/plugin.rb" ].map do |pattern| - Dir.glob(pattern).map { |x| File.mtime(x) }.max + globs = ["#{Rails.root}/app/assets/stylesheets/**/*.*css"] + + for path in (Discourse.plugins || []).map { |plugin| File.dirname(plugin.path) } + globs += [ + "#{path}/plugin.rb", + "#{path}/**/*.*css", + ] + end + + globs.map do |pattern| + Dir.glob(pattern).map { |x| File.mtime(x) }.max end.compact.max.to_i end @@ -103,9 +110,14 @@ class DiscourseStylesheets "#{cache_fullpath}/#{stylesheet_filename_no_digest}" end + def stylesheet_cdnpath + "#{GlobalSetting.cdn_url}#{stylesheet_relpath}?__ws=#{Discourse.current_hostname}" + end + def stylesheet_relpath "/#{CACHE_PATH}/#{stylesheet_filename}" end + def stylesheet_relpath_no_digest "/#{CACHE_PATH}/#{stylesheet_filename_no_digest}" end diff --git a/lib/scheduler/defer.rb b/lib/scheduler/defer.rb index 89522f08ab..7f20afe0ca 100644 --- a/lib/scheduler/defer.rb +++ b/lib/scheduler/defer.rb @@ -4,9 +4,17 @@ module Scheduler @async = !Rails.env.test? @queue = Queue.new @mutex = Mutex.new + @paused = false @thread = nil - start_thread + end + def pause + stop! + @paused = true + end + + def resume + @paused = false end # for test @@ -14,22 +22,29 @@ module Scheduler @async = val end - def later(desc = nil, &blk) + def later(desc = nil, db=RailsMultisite::ConnectionManagement.current_db, &blk) if @async - start_thread unless @thread.alive? - @queue << [RailsMultisite::ConnectionManagement.current_db, blk, desc] + start_thread unless (@thread && @thread.alive?) || @paused + @queue << [db, blk, desc] else blk.call end end def stop! - @thread.kill + @thread.kill if @thread && @thread.alive? + @thread = nil end # test only def stopped? - !@thread.alive? + !(@thread && @thread.alive?) + end + + def do_all_work + while !@queue.empty? + do_work(_non_block=true) + end end private @@ -45,10 +60,11 @@ module Scheduler end end - def do_work - db, job, desc = @queue.deq + # using non_block to match Ruby #deq + def do_work(non_block=false) + db, job, desc = @queue.deq(non_block) begin - RailsMultisite::ConnectionManagement.establish_connection(db: db) + RailsMultisite::ConnectionManagement.establish_connection(db: db) if db job.call rescue => ex Discourse.handle_exception(ex, {message: "Running deferred code '#{desc}'"}) @@ -62,6 +78,16 @@ module Scheduler end class Defer + + module Unicorn + def process_client(client) + Defer.pause + super(client) + Defer.do_all_work + Defer.resume + end + end + extend Deferrable initialize end diff --git a/lib/search.rb b/lib/search.rb index 2330ddc694..454aa5b7c5 100644 --- a/lib/search.rb +++ b/lib/search.rb @@ -17,7 +17,7 @@ class Search end def self.facets - %w(topic category user) + %w(topic category user private_messages) end def self.long_locale @@ -106,6 +106,12 @@ class Search @search_context = @opts[:search_context] @include_blurbs = @opts[:include_blurbs] || false @limit = Search.per_facet + + if @search_pms && @guardian.user + @opts[:type_filter] = "private_messages" + @search_context = @guardian.user + end + if @opts[:type_filter].present? @limit = Search.per_filter end @@ -183,6 +189,12 @@ class Search elsif word == 'in:tracking' @notification_level = TopicUser.notification_levels[:tracking] nil + elsif word == 'in:private' + @search_pms = true + nil + elsif word == 'in:bookmarks' + @bookmarked_only = true + nil else word end @@ -277,12 +289,24 @@ class Search def posts_query(limit, opts=nil) opts ||= {} posts = Post - .joins(:post_search_data, {:topic => :category}) + .joins(:post_search_data, :topic) + .joins("LEFT JOIN categories ON categories.id = topics.category_id") .where("topics.deleted_at" => nil) .where("topics.visible") - .where("topics.archetype <> ?", Archetype.private_message) - if @search_context.present? && @search_context.is_a?(Topic) + is_topic_search = @search_context.present? && @search_context.is_a?(Topic) + + if opts[:private_messages] || (is_topic_search && @search_context.private_message?) + posts = posts.where("topics.archetype = ?", Archetype.private_message) + + unless @guardian.is_admin? + posts = posts.where("topics.id IN (SELECT topic_id FROM topic_allowed_users WHERE user_id = ?)", @guardian.user.id) + end + else + posts = posts.where("topics.archetype <> ?", Archetype.private_message) + end + + if is_topic_search posts = posts.joins('JOIN users u ON u.id = posts.user_id') posts = posts.where("posts.raw || ' ' || u.username || ' ' || u.name ilike ?", "%#{@term}%") else @@ -310,11 +334,15 @@ class Search end if @guardian.user - if @liked_only + if @liked_only || @bookmarked_only + + post_action_type = PostActionType.types[:like] if @liked_only + post_action_type = PostActionType.types[:bookmark] if @bookmarked_only + posts = posts.where("posts.id IN ( SELECT pa.post_id FROM post_actions pa WHERE pa.user_id = #{@guardian.user.id} AND - pa.post_action_type_id = #{PostActionType.types[:like]} + pa.post_action_type_id = #{post_action_type} )") end @@ -336,7 +364,13 @@ class Search if @search_context.present? if @search_context.is_a?(User) - posts = posts.where("posts.user_id = #{@search_context.id}") + + if opts[:private_messages] + posts = posts.where("topics.id IN (SELECT topic_id FROM topic_allowed_users WHERE user_id = ?)", @search_context.id) + else + posts = posts.where("posts.user_id = #{@search_context.id}") + end + elsif @search_context.is_a?(Category) posts = posts.where("topics.category_id = #{@search_context.id}") elsif @search_context.is_a?(Topic) @@ -407,9 +441,10 @@ class Search end end - def aggregate_search + def aggregate_search(opts = {}) - post_sql = posts_query(@limit, aggregate_search: true) + post_sql = posts_query(@limit, aggregate_search: true, + private_messages: opts[:private_messages]) .select('topics.id', 'min(post_number) post_number') .group('topics.id') .to_sql @@ -417,6 +452,10 @@ class Search # double wrapping so we get correct row numbers post_sql = "SELECT *, row_number() over() row_number FROM (#{post_sql}) xxx" + # p Topic.exec_sql(post_sql).to_a + # puts post_sql + # p Topic.exec_sql("SELECT topic_id FROM topic_allowed_users WHERE user_id = 2").to_a + posts = Post.includes(:topic => :category) .joins("JOIN (#{post_sql}) x ON x.id = posts.topic_id AND x.post_number = posts.post_number") .order('row_number') @@ -426,6 +465,12 @@ class Search end end + def private_messages_search + raise Discourse::InvalidAccess.new("anonymous can not search PMs") unless @guardian.user + + aggregate_search(private_messages: true) + end + def topic_search if @search_context.is_a?(Topic) posts = posts_query(@limit).where('posts.topic_id = ?', @search_context.id).includes(:topic => :category) diff --git a/lib/single_sign_on.rb b/lib/single_sign_on.rb index bd01633c96..928e0b00ca 100644 --- a/lib/single_sign_on.rb +++ b/lib/single_sign_on.rb @@ -1,7 +1,8 @@ class SingleSignOn ACCESSORS = [:nonce, :name, :username, :email, :avatar_url, :avatar_force_update, - :about_me, :external_id] + :about_me, :external_id, :return_sso_url, :admin, :moderator] FIXNUMS = [] + BOOLS = [:avatar_force_update, :admin, :moderator] NONCE_EXPIRY_TIME = 10.minutes attr_accessor(*ACCESSORS) @@ -21,7 +22,12 @@ class SingleSignOn parsed = Rack::Utils.parse_query(payload) if sso.sign(parsed["sso"]) != parsed["sig"] - raise RuntimeError, "Bad signature for payload" + diags = "\n\nsso: #{parsed["sso"]}\n\nsig: #{parsed["sig"]}\n\nexpected sig: #{sso.sign(parsed["sso"])}" + if parsed["sso"] =~ /[^a-zA-Z0-9=\r\n\/+]/m + raise RuntimeError, "The SSO field should be Base64 encoded, using only A-Z, a-z, 0-9, +, /, and = characters. Your input contains characters we don't understand as Base64, see http://en.wikipedia.org/wiki/Base64 #{diags}" + else + raise RuntimeError, "Bad signature for payload #{diags}" + end end decoded = Base64.decode64(parsed["sso"]) @@ -30,6 +36,9 @@ class SingleSignOn ACCESSORS.each do |k| val = decoded_hash[k.to_s] val = val.to_i if FIXNUMS.include? k + if BOOLS.include? k + val = ["true", "false"].include?(val) ? val == "true" : nil + end sso.send("#{k}=", val) end @@ -77,7 +86,7 @@ class SingleSignOn def unsigned_payload payload = {} ACCESSORS.each do |k| - next unless (val = send k) + next if (val = send k) == nil payload[k] = val end diff --git a/lib/site_setting_extension.rb b/lib/site_setting_extension.rb index ec8b7c16df..69538720f7 100644 --- a/lib/site_setting_extension.rb +++ b/lib/site_setting_extension.rb @@ -46,6 +46,10 @@ module SiteSettingExtension @choices ||= {} end + def shadowed_settings + @shadowed_settings ||= [] + end + def hidden_settings @hidden_settings ||= [] end @@ -54,6 +58,10 @@ module SiteSettingExtension @refresh_settings ||= [] end + def previews + @previews ||= {} + end + def validators @validators ||= {} end @@ -64,31 +72,52 @@ module SiteSettingExtension self.defaults[name] = default categories[name] = opts[:category] || :uncategorized current_value = current.has_key?(name) ? current[name] : default + if opts[:enum] enum = opts[:enum] enums[name] = enum.is_a?(String) ? enum.constantize : enum end + if opts[:choices] choices.has_key?(name) ? choices[name].concat(opts[:choices]) : choices[name] = opts[:choices] end + if opts[:type] == 'list' lists << name end + if opts[:hidden] hidden_settings << name end + + # You can "shadow" a site setting with a GlobalSetting. If the GlobalSetting + # exists it will be used instead of the setting and the setting will be hidden. + # Useful for things like API keys on multisite. + if opts[:shadowed_by_global] && GlobalSetting.respond_to?(name) + hidden_settings << name + shadowed_settings << name + current_value = GlobalSetting.send(name) + end + if opts[:refresh] refresh_settings << name end - if validator_type = validator_for(opts[:type] || get_data_type(name, defaults[name])) - validators[name] = {class: validator_type, opts: opts} + if opts[:preview] + previews[name] = opts[:preview] + end + + opts[:validator] = opts[:validator].try(:constantize) + type = opts[:type] || get_data_type(name, defaults[name]) + + if validator_type = opts[:validator] || validator_for(type) + validators[name] = { class: validator_type, opts: opts } end current[name] = current_value - setup_methods(name, current_value) + setup_methods(name) end end @@ -124,7 +153,7 @@ module SiteSettingExtension # Retrieve all settings def all_settings(include_hidden=false) @defaults - .reject{|s, _| hidden_settings.include?(s) || include_hidden} + .reject{|s, _| hidden_settings.include?(s) && !include_hidden} .map do |s, v| value = send(s) type = types[get_data_type(s, value)] @@ -134,7 +163,8 @@ module SiteSettingExtension default: v.to_s, type: type.to_s, value: value.to_s, - category: categories[s] + category: categories[s], + preview: previews[s] } opts.merge!({valid_values: enum_class(s).values, translate_names: enum_class(s).translate_names?}) if type == :enum opts[:choices] = choices[s] if choices.has_key? s @@ -147,7 +177,10 @@ module SiteSettingExtension end def self.client_settings_cache_key - "client_settings_json" + # NOTE: we use the git version in the key to ensure + # that we don't end up caching the incorrect version + # in cases where we are cycling unicorns + "client_settings_json_#{Discourse.git_version}" end # refresh all the site settings @@ -163,21 +196,24 @@ module SiteSettingExtension # add defaults, cause they are cached new_hash = defaults.merge(new_hash) - changes,deletions = diff_hash(new_hash, old) - - if deletions.length > 0 || changes.length > 0 - changes.each do |name, val| - current[name] = val - end - deletions.each do |name,val| - current[name] = defaults[name] - end + # add shadowed + shadowed_settings.each do |ss| + new_hash[ss] = GlobalSetting.send(ss) end + + changes, deletions = diff_hash(new_hash, old) + + changes.each do |name, val| + current[name] = val + end + deletions.each do |name, val| + current[name] = defaults[name] + end + clear_cache! end end - def ensure_listen_for_changes unless @subscribed MessageBus.subscribe("/site_settings") do |message| @@ -273,7 +309,7 @@ module SiteSettingExtension } value = domain_array.join("|") end - return value + value end def set(name, value) @@ -355,11 +391,10 @@ module SiteSettingExtension end - def setup_methods(name, current_value) + def setup_methods(name) + clean_name = name.to_s.sub("?", "").to_sym - clean_name = name.to_s.sub("?", "") - - eval "define_singleton_method :#{clean_name} do + define_singleton_method clean_name do c = @containers[provider.current_site] if c c[name] @@ -369,14 +404,13 @@ module SiteSettingExtension end end - define_singleton_method :#{clean_name}? do - #{clean_name} + define_singleton_method "#{clean_name}?" do + self.send clean_name end - define_singleton_method :#{clean_name}= do |val| - add_override!(:#{name}, val) + define_singleton_method "#{clean_name}=" do |val| + add_override!(name, val) end - " end def enum_class(name) @@ -388,8 +422,7 @@ module SiteSettingExtension url = "http://#{url}" if URI.parse(url).scheme.nil? url = URI.parse(url).host end - return url + url end end - diff --git a/lib/spam_handler.rb b/lib/spam_handler.rb index 1df501babd..6fc184e748 100644 --- a/lib/spam_handler.rb +++ b/lib/spam_handler.rb @@ -4,21 +4,21 @@ class SpamHandler return false if SiteSetting.max_new_accounts_per_registration_ip <= 0 tl2_plus_accounts_with_same_ip = User.where("trust_level >= ?", TrustLevel[2]) - .where("ip_address = ?", ip_address.to_s) + .where(ip_address: ip_address.to_s) .count return false if tl2_plus_accounts_with_same_ip > 0 staff_user_ids = Group[:staff].user_ids - [-1] staff_members_with_same_ip = User.where(id: staff_user_ids) - .where("ip_address = ?", ip_address.to_s) + .where(ip_address: ip_address.to_s) .count return false if staff_members_with_same_ip > 0 tl0_accounts_with_same_ip = User.unscoped .where(trust_level: TrustLevel[0]) - .where("ip_address = ?", ip_address.to_s) + .where(ip_address: ip_address.to_s) .count tl0_accounts_with_same_ip >= SiteSetting.max_new_accounts_per_registration_ip diff --git a/lib/suggested_topics_builder.rb b/lib/suggested_topics_builder.rb index 3051fdf34b..73158495f8 100644 --- a/lib/suggested_topics_builder.rb +++ b/lib/suggested_topics_builder.rb @@ -70,14 +70,6 @@ class SuggestedTopicsBuilder SiteSetting.suggested_topics - @results.count{|r| r.category_id == @category_id} end - def category_full? - if @category_id - - else - full? - end - end - def size @results.size end diff --git a/lib/summarize.rb b/lib/summarize.rb deleted file mode 100644 index d6e23595f4..0000000000 --- a/lib/summarize.rb +++ /dev/null @@ -1,27 +0,0 @@ -# Summarize a HTML field into regular text. Used currently -# for meta tags - -require 'sanitize' - -class Summarize - - def initialize(text) - @text = text - end - - def self.max_length - 500 - end - - def summary - return nil if @text.blank? - - result = Sanitize.clean(@text) - result.gsub!(/\n/, ' ') - result.strip! - - return result if result.length <= Summarize.max_length - "#{result[0..Summarize.max_length]}..." - end - -end diff --git a/lib/system_message.rb b/lib/system_message.rb index 95e39e6d1b..176298839c 100644 --- a/lib/system_message.rb +++ b/lib/system_message.rb @@ -50,7 +50,7 @@ class SystemMessage site_name: SiteSetting.title, username: @recipient.username, user_preferences_url: "#{Discourse.base_url}/users/#{@recipient.username_lower}/preferences", - new_user_tips: SiteText.text_for(:usage_tips), + new_user_tips: SiteText.text_for(:usage_tips, base_url: Discourse.base_url), site_password: "", base_url: Discourse.base_url, } diff --git a/lib/tasks/assets.rake b/lib/tasks/assets.rake index 1c0ac999bc..2bd571a1bf 100644 --- a/lib/tasks/assets.rake +++ b/lib/tasks/assets.rake @@ -11,56 +11,61 @@ task 'assets:precompile:before' do puts "Purging temp files" `rm -fr #{Rails.root}/tmp/cache` + if Rails.configuration.assets.js_compressor == :uglifier && !`which uglifyjs`.empty? && !ENV['SKIP_NODE_UGLIFY'] + $node_uglify = true + end + + puts "Bundling assets" + # in the past we applied a patch that removed asset postfixes, but it is terrible practice # leaving very complicated build issues # https://github.com/rails/sprockets-rails/issues/49 - # let's make precompile faster using redis magic require 'sprockets' require 'digest/sha1' - module ::Sprockets - def self.redis - @redis ||= - ( - redis_url = GlobalSetting.asset_redis_url - if redis_url.present? - uri = URI.parse(redis_url) - options = {} - options[:password] = uri.password if uri.password.present? - options[:host] = uri.host - options[:port] = uri.port || 6379 - Redis.new(options) - else - DiscourseRedis.raw_connection + if $node_uglify + # monkey patch asset pipeline not to gzip, compress: false is broken + class ::Sprockets::Asset + # Save asset to disk. + def write_to(filename, options = {}) + # Gzip contents if filename has '.gz' + return if File.extname(filename) == '.gz' + + begin + FileUtils.mkdir_p File.dirname(filename) + + File.open("#{filename}+", 'wb') do |f| + f.write to_s end - ) - end - def self.cache_compiled(type, data) - # add cache breaker here if uglifier options change - digest = Digest::SHA1.hexdigest(data) << "v1" - key = "SPROCKETS_#{type}_#{digest}" - if compiled = redis.get(key) - redis.expire(key, 1.week) - else - compiled = yield - redis.setex(key, 1.week, compiled) - end - compiled - end + # Atomic write + FileUtils.mv("#{filename}+", filename) - class UglifierCompressor + # Set mtime correctly + File.utime(mtime, mtime, filename) - def evaluate(context, locals, &block) - ::Sprockets.cache_compiled("uglifier", data) do - Uglifier.new(:comments => :none, - :screw_ie8 => false, - :output => {max_line_len: 1024}).compile(data) + nil + ensure + # Ensure tmp file gets cleaned up + FileUtils.rm("#{filename}+") if File.exist?("#{filename}+") end end + + end + + module ::Sprockets + + class UglifierCompressor + + def evaluate(context, locals, &block) + # monkey patch cause we do this later, no idea how to cleanly disable + data + end + + end end end @@ -79,7 +84,82 @@ task 'assets:precompile:css' => 'environment' do end end +def assets_path + "#{Rails.root}/public/assets" +end + +def compress_node(from,to) + to_path = "#{assets_path}/#{to}" + + source_map_root = (d=File.dirname(from)) == "." ? "/assets" : "/assets/#{d}" + + cmd = "uglifyjs '#{assets_path}/#{from}' -p relative -c -m -o '#{to_path}' --source-map-root '#{source_map_root}' --source-map '#{assets_path}/#{to}.map' --source-map-url '/assets/#{to}.map'" + + + STDERR.puts cmd + `#{cmd} 2>&1` + +end + +def compress_ruby(from,to) + data = File.read("#{assets_path}/#{from}") + + uglified, map = Uglifier.new(comments: :none, + screw_ie8: false, + source_filename: File.basename(from), + output_filename: File.basename(to) + ) + .compile_with_map(data) + dest = "#{assets_path}/#{to}" + + File.write(dest, uglified << "\n//# sourceMappingURL=/assets/#{to}.map") + File.write(dest + ".map", map) +end + +def gzip(path) + STDERR.puts "gzip #{path}" + STDERR.puts `gzip -f -k -9 #{path}` +end + +def compress(from,to) + if @has_uglifyjs ||= !`which uglifyjs`.empty? + compress_node(from,to) + else + compress_ruby(from,to) + end +end + task 'assets:precompile' => 'assets:precompile:before' do # Run after assets:precompile Rake::Task["assets:precompile:css"].invoke + + if $node_uglify + puts "Compressing Javascript and Generating Source Maps" + manifest = Sprockets::Manifest.new(assets_path) + manifest.files + .select{|k,v| k =~ /\.js$/} + .each do |file, info| + + path = "#{assets_path}/#{file}" + _file = (d = File.dirname(file)) == "." ? "_#{file}" : "#{d}/_#{File.basename(file)}" + _path = "#{assets_path}/#{_file}" + + if File.exists?(_path) + STDERR.puts "Skipping: #{file} already compressed" + else + STDERR.puts "Compressing: #{file}" + FileUtils.mv(path, _path) + compress(_file,file) + + info["size"] = File.size(path) + info["mtime"] = File.mtime(path).iso8601 + gzip(path) + end + + end + + # protected + manifest.send :save + end + end diff --git a/lib/tasks/emoji.rake b/lib/tasks/emoji.rake new file mode 100644 index 0000000000..4fa8c1d4f6 --- /dev/null +++ b/lib/tasks/emoji.rake @@ -0,0 +1,63 @@ +desc "update emoji images" +task "emoji:update" => :environment do + download_emojis_for("emoji_one", "https://raw.githubusercontent.com/Ranks/emojione/master/assets/png/%s.png", uppercase: true, leading_zeros: true) + download_emojis_for("twitter", "https://raw.githubusercontent.com/twitter/twemoji/gh-pages/72x72/%s.png", lowercase: true) + download_emojis_for("apple", "https://raw.githubusercontent.com/github/gemoji/master/images/emoji/unicode/%s.png", lowercase: true, leading_zeros: true) + # download_google_emojis("~/Desktop/images/%s.png") +end + +def download_emojis_for(set, url_template, options={}) + puts "Downloading emojis for #{set}..." + + path = "public/images/emoji/#{set}" + FileUtils.rm_rf(path) rescue nil + FileUtils.mkdir_p(path) rescue nil + + uppercase = options[:uppercase] == true + lowercase = options[:lowercase] == true + leading_zeros = options[:leading_zeros] == true + + Emoji.all.each do |emoji| + codepoints = emoji["emoji"].codepoints.map { |c| c.to_s(16).rjust(leading_zeros ? 4 : 0, '0') } + filename = codepoints.join('-').sub(/-fe0f\b/, '') + filename = filename.downcase if lowercase + filename = filename.upcase if uppercase + puts "#{filename} -> #{emoji["emoji"]}" + url = url_template % filename + data = open(url).read rescue nil + next if data.nil? + FileUtils.cd(path) do + emoji["aliases"].each do |name| + File.open("#{name}.png", "wb") { |f| f << data } + ImageOptim.new.optimize_image("#{name}.png") rescue nil + end + end + end +end + +# extracted from the NotoColorEmoji font +GOOGLE_EMOJIS = {35=>1, 48=>2, 49=>3, 50=>4, 51=>5, 52=>6, 53=>7, 54=>8, 55=>9, 56=>10, 57=>11, 169=>12, 174=>13, 8252=>14, 8265=>15, 8419=>16, 8482=>17, 8505=>18, 8596=>19, 8597=>20, 8598=>21, 8599=>22, 8600=>23, 8601=>24, 8617=>25, 8618=>26, 8986=>27, 8987=>28, 9193=>29, 9194=>30, 9195=>31, 9196=>32, 9200=>33, 9203=>34, 9410=>35, 9642=>36, 9643=>37, 9654=>38, 9664=>39, 9723=>40, 9724=>41, 9725=>42, 9726=>43, 9728=>44, 9729=>45, 9742=>46, 9745=>47, 9748=>48, 9749=>49, 9757=>50, 9786=>51, 9800=>52, 9801=>53, 9802=>54, 9803=>55, 9804=>56, 9805=>57, 9806=>58, 9807=>59, 9808=>60, 9809=>61, 9810=>62, 9811=>63, 9824=>64, 9827=>65, 9829=>66, 9830=>67, 9832=>68, 9851=>69, 9855=>70, 9875=>71, 9888=>72, 9889=>73, 9898=>74, 9899=>75, 9917=>76, 9918=>77, 9924=>78, 9925=>79, 9934=>80, 9940=>81, 9962=>82, 9970=>83, 9971=>84, 9973=>85, 9978=>86, 9981=>87, 9986=>88, 9989=>89, 9992=>90, 9993=>91, 9994=>92, 9995=>93, 9996=>94, 9999=>95, 10002=>96, 10004=>97, 10006=>98, 10024=>99, 10035=>100, 10036=>101, 10052=>102, 10055=>103, 10060=>104, 10062=>105, 10067=>106, 10068=>107, 10069=>108, 10071=>109, 10084=>110, 10133=>111, 10134=>112, 10135=>113, 10145=>114, 10160=>115, 10175=>116, 10548=>117, 10549=>118, 11013=>119, 11014=>120, 11015=>121, 11035=>122, 11036=>123, 11088=>124, 11093=>125, 12336=>126, 12349=>127, 12951=>128, 12953=>129, 126980=>130, 127183=>131, 127344=>132, 127345=>133, 127358=>134, 127359=>135, 127374=>136, 127377=>137, 127378=>138, 127379=>139, 127380=>140, 127381=>141, 127382=>142, 127383=>143, 127384=>144, 127385=>145, 127386=>146, 127462=>147, 127463=>148, 127464=>149, 127465=>150, 127466=>151, 127467=>152, 127468=>153, 127469=>154, 127470=>155, 127471=>156, 127472=>157, 127473=>158, 127474=>159, 127475=>160, 127476=>161, 127477=>162, 127478=>163, 127479=>164, 127480=>165, 127481=>166, 127482=>167, 127483=>168, 127484=>169, 127485=>170, 127486=>171, 127487=>172, 127489=>173, 127490=>174, 127514=>175, 127535=>176, 127538=>177, 127539=>178, 127540=>179, 127541=>180, 127542=>181, 127543=>182, 127544=>183, 127545=>184, 127546=>185, 127568=>186, 127569=>187, 127744=>188, 127745=>189, 127746=>190, 127747=>191, 127748=>192, 127749=>193, 127750=>194, 127751=>195, 127752=>196, 127753=>197, 127754=>198, 127755=>199, 127756=>200, 127757=>201, 127758=>202, 127759=>203, 127760=>204, 127761=>205, 127762=>206, 127763=>207, 127764=>208, 127765=>209, 127766=>210, 127767=>211, 127768=>212, 127769=>213, 127770=>214, 127771=>215, 127772=>216, 127773=>217, 127774=>218, 127775=>219, 127776=>220, 127792=>221, 127793=>222, 127794=>223, 127795=>224, 127796=>225, 127797=>226, 127799=>227, 127800=>228, 127801=>229, 127802=>230, 127803=>231, 127804=>232, 127805=>233, 127806=>234, 127807=>235, 127808=>236, 127809=>237, 127810=>238, 127811=>239, 127812=>240, 127813=>241, 127814=>242, 127815=>243, 127816=>244, 127817=>245, 127818=>246, 127819=>247, 127820=>248, 127821=>249, 127822=>250, 127823=>251, 127824=>252, 127825=>253, 127826=>254, 127827=>255, 127828=>256, 127829=>257, 127830=>258, 127831=>259, 127832=>260, 127833=>261, 127834=>262, 127835=>263, 127836=>264, 127837=>265, 127838=>266, 127839=>267, 127840=>268, 127841=>269, 127842=>270, 127843=>271, 127844=>272, 127845=>273, 127846=>274, 127847=>275, 127848=>276, 127849=>277, 127850=>278, 127851=>279, 127852=>280, 127853=>281, 127854=>282, 127855=>283, 127856=>284, 127857=>285, 127858=>286, 127859=>287, 127860=>288, 127861=>289, 127862=>290, 127863=>291, 127864=>292, 127865=>293, 127866=>294, 127867=>295, 127868=>296, 127872=>297, 127873=>298, 127874=>299, 127875=>300, 127876=>301, 127877=>302, 127878=>303, 127879=>304, 127880=>305, 127881=>306, 127882=>307, 127883=>308, 127884=>309, 127885=>310, 127886=>311, 127887=>312, 127888=>313, 127889=>314, 127890=>315, 127891=>316, 127904=>317, 127905=>318, 127906=>319, 127907=>320, 127908=>321, 127909=>322, 127910=>323, 127911=>324, 127912=>325, 127913=>326, 127914=>327, 127915=>328, 127916=>329, 127917=>330, 127918=>331, 127919=>332, 127920=>333, 127921=>334, 127922=>335, 127923=>336, 127924=>337, 127925=>338, 127926=>339, 127927=>340, 127928=>341, 127929=>342, 127930=>343, 127931=>344, 127932=>345, 127933=>346, 127934=>347, 127935=>348, 127936=>349, 127937=>350, 127938=>351, 127939=>352, 127940=>353, 127942=>354, 127943=>355, 127944=>356, 127945=>357, 127946=>358, 127968=>359, 127969=>360, 127970=>361, 127971=>362, 127972=>363, 127973=>364, 127974=>365, 127975=>366, 127976=>367, 127977=>368, 127978=>369, 127979=>370, 127980=>371, 127981=>372, 127982=>373, 127983=>374, 127984=>375, 128000=>376, 128001=>377, 128002=>378, 128003=>379, 128004=>380, 128005=>381, 128006=>382, 128007=>383, 128008=>384, 128009=>385, 128010=>386, 128011=>387, 128012=>388, 128013=>389, 128014=>390, 128015=>391, 128016=>392, 128017=>393, 128018=>394, 128019=>395, 128020=>396, 128021=>397, 128022=>398, 128023=>399, 128024=>400, 128025=>401, 128026=>402, 128027=>403, 128028=>404, 128029=>405, 128030=>406, 128031=>407, 128032=>408, 128033=>409, 128034=>410, 128035=>411, 128036=>412, 128037=>413, 128038=>414, 128039=>415, 128040=>416, 128041=>417, 128042=>418, 128043=>419, 128044=>420, 128045=>421, 128046=>422, 128047=>423, 128048=>424, 128049=>425, 128050=>426, 128051=>427, 128052=>428, 128053=>429, 128054=>430, 128055=>431, 128056=>432, 128057=>433, 128058=>434, 128059=>435, 128060=>436, 128061=>437, 128062=>438, 128064=>439, 128066=>440, 128067=>441, 128068=>442, 128069=>443, 128070=>444, 128071=>445, 128072=>446, 128073=>447, 128074=>448, 128075=>449, 128076=>450, 128077=>451, 128078=>452, 128079=>453, 128080=>454, 128081=>455, 128082=>456, 128083=>457, 128084=>458, 128085=>459, 128086=>460, 128087=>461, 128088=>462, 128089=>463, 128090=>464, 128091=>465, 128092=>466, 128093=>467, 128094=>468, 128095=>469, 128096=>470, 128097=>471, 128098=>472, 128099=>473, 128100=>474, 128101=>475, 128102=>476, 128103=>477, 128104=>478, 128105=>479, 128106=>480, 128107=>481, 128108=>482, 128109=>483, 128110=>484, 128111=>485, 128112=>486, 128113=>487, 128114=>488, 128115=>489, 128116=>490, 128117=>491, 128118=>492, 128119=>493, 128120=>494, 128121=>495, 128122=>496, 128123=>497, 128124=>498, 128125=>499, 128126=>500, 128127=>501, 128128=>502, 128129=>503, 128130=>504, 128131=>505, 128132=>506, 128133=>507, 128134=>508, 128135=>509, 128136=>510, 128137=>511, 128138=>512, 128139=>513, 128140=>514, 128141=>515, 128142=>516, 128143=>517, 128144=>518, 128145=>519, 128146=>520, 128147=>521, 128148=>522, 128149=>523, 128150=>524, 128151=>525, 128152=>526, 128153=>527, 128154=>528, 128155=>529, 128156=>530, 128157=>531, 128158=>532, 128159=>533, 128160=>534, 128161=>535, 128162=>536, 128163=>537, 128164=>538, 128165=>539, 128166=>540, 128167=>541, 128168=>542, 128169=>543, 128170=>544, 128171=>545, 128172=>546, 128173=>547, 128174=>548, 128175=>549, 128176=>550, 128177=>551, 128178=>552, 128179=>553, 128180=>554, 128181=>555, 128182=>556, 128183=>557, 128184=>558, 128185=>559, 128186=>560, 128187=>561, 128188=>562, 128189=>563, 128190=>564, 128191=>565, 128192=>566, 128193=>567, 128194=>568, 128195=>569, 128196=>570, 128197=>571, 128198=>572, 128199=>573, 128200=>574, 128201=>575, 128202=>576, 128203=>577, 128204=>578, 128205=>579, 128206=>580, 128207=>581, 128208=>582, 128209=>583, 128210=>584, 128211=>585, 128212=>586, 128213=>587, 128214=>588, 128215=>589, 128216=>590, 128217=>591, 128218=>592, 128219=>593, 128220=>594, 128221=>595, 128222=>596, 128223=>597, 128224=>598, 128225=>599, 128226=>600, 128227=>601, 128228=>602, 128229=>603, 128230=>604, 128231=>605, 128232=>606, 128233=>607, 128234=>608, 128235=>609, 128236=>610, 128237=>611, 128238=>612, 128239=>613, 128240=>614, 128241=>615, 128242=>616, 128243=>617, 128244=>618, 128245=>619, 128246=>620, 128247=>621, 128249=>622, 128250=>623, 128251=>624, 128252=>625, 128256=>626, 128257=>627, 128258=>628, 128259=>629, 128260=>630, 128261=>631, 128262=>632, 128263=>633, 128264=>634, 128265=>635, 128266=>636, 128267=>637, 128268=>638, 128269=>639, 128270=>640, 128271=>641, 128272=>642, 128273=>643, 128274=>644, 128275=>645, 128276=>646, 128277=>647, 128278=>648, 128279=>649, 128280=>650, 128281=>651, 128282=>652, 128283=>653, 128284=>654, 128285=>655, 128286=>656, 128287=>657, 128288=>658, 128289=>659, 128290=>660, 128291=>661, 128292=>662, 128293=>663, 128294=>664, 128295=>665, 128296=>666, 128297=>667, 128298=>668, 128299=>669, 128300=>670, 128301=>671, 128302=>672, 128303=>673, 128304=>674, 128305=>675, 128306=>676, 128307=>677, 128308=>678, 128309=>679, 128310=>680, 128311=>681, 128312=>682, 128313=>683, 128314=>684, 128315=>685, 128316=>686, 128317=>687, 128336=>688, 128337=>689, 128338=>690, 128339=>691, 128340=>692, 128341=>693, 128342=>694, 128343=>695, 128344=>696, 128345=>697, 128346=>698, 128347=>699, 128348=>700, 128349=>701, 128350=>702, 128351=>703, 128352=>704, 128353=>705, 128354=>706, 128355=>707, 128356=>708, 128357=>709, 128358=>710, 128359=>711, 128507=>712, 128508=>713, 128509=>714, 128510=>715, 128511=>716, 128512=>717, 128513=>718, 128514=>719, 128515=>720, 128516=>721, 128517=>722, 128518=>723, 128519=>724, 128520=>725, 128521=>726, 128522=>727, 128523=>728, 128524=>729, 128525=>730, 128526=>731, 128527=>732, 128528=>733, 128529=>734, 128530=>735, 128531=>736, 128532=>737, 128533=>738, 128534=>739, 128535=>740, 128536=>741, 128537=>742, 128538=>743, 128539=>744, 128540=>745, 128541=>746, 128542=>747, 128543=>748, 128544=>749, 128545=>750, 128546=>751, 128547=>752, 128548=>753, 128549=>754, 128550=>755, 128551=>756, 128552=>757, 128553=>758, 128554=>759, 128555=>760, 128556=>761, 128557=>762, 128558=>763, 128559=>764, 128560=>765, 128561=>766, 128562=>767, 128563=>768, 128564=>769, 128565=>770, 128566=>771, 128567=>772, 128568=>773, 128569=>774, 128570=>775, 128571=>776, 128572=>777, 128573=>778, 128574=>779, 128575=>780, 128576=>781, 128581=>782, 128582=>783, 128583=>784, 128584=>785, 128585=>786, 128586=>787, 128587=>788, 128588=>789, 128589=>790, 128590=>791, 128591=>792, 128640=>793, 128641=>794, 128642=>795, 128643=>796, 128644=>797, 128645=>798, 128646=>799, 128647=>800, 128648=>801, 128649=>802, 128650=>803, 128651=>804, 128652=>805, 128653=>806, 128654=>807, 128655=>808, 128656=>809, 128657=>810, 128658=>811, 128659=>812, 128660=>813, 128661=>814, 128662=>815, 128663=>816, 128664=>817, 128665=>818, 128666=>819, 128667=>820, 128668=>821, 128669=>822, 128670=>823, 128671=>824, 128672=>825, 128673=>826, 128674=>827, 128675=>828, 128676=>829, 128677=>830, 128678=>831, 128679=>832, 128680=>833, 128681=>834, 128682=>835, 128683=>836, 128684=>837, 128685=>838, 128686=>839, 128687=>840, 128688=>841, 128689=>842, 128690=>843, 128691=>844, 128692=>845, 128693=>846, 128694=>847, 128695=>848, 128696=>849, 128697=>850, 128698=>851, 128699=>852, 128700=>853, 128701=>854, 128702=>855, 128703=>856, 128704=>857, 128705=>858, 128706=>859, 128707=>860, 128708=>861, 128709=>862, 1041637=>978, 1041638=>1088, 1041639=>943, 1041640=>926, 1041641=>974, 1041642=>945, 1041643=>936, 1041644=>1050, 1041645=>917, 1041646=>986, 1042476=>863, 1042478=>865, 1042479=>866, 1042480=>867, 1042481=>868, 1042482=>869, 1042483=>870, 1042484=>871, 1042485=>872, 1042486=>873, 1042487=>864}.freeze + +def download_google_emojis(url_template) + puts "Downloading emojis for google..." + + path = "public/images/google" + FileUtils.rm_rf(path) rescue nil + FileUtils.mkdir_p(path) rescue nil + + Emoji.all.each do |emoji| + codepoint = emoji["emoji"].codepoints.first + filename = GOOGLE_EMOJIS[codepoint] + next if filename.nil? + puts "#{filename} -> #{emoji["emoji"]}" + url = url_template % filename + data = File.open(url, "rb").read + next if data.nil? + FileUtils.cd(path) do + emoji["aliases"].each do |name| + File.open("#{name}.png", "wb") { |f| f << data } + ImageOptim.new.optimize_image("#{name}.png") rescue nil + end + end + end +end diff --git a/lib/tasks/integration.rake b/lib/tasks/integration.rake index 528a955b36..c2f6b8a304 100644 --- a/lib/tasks/integration.rake +++ b/lib/tasks/integration.rake @@ -4,7 +4,7 @@ desc 'Creates the integration fixtures. Requires a development instance running. task 'integration:create_fixtures' => :environment do fixtures = { - discovery: ["/latest.json", "/categories.json", "/category/bug/l/latest.json"], + discovery: ["/latest.json", "/categories.json", "/c/bug/l/latest.json"], topic: ["/t/280.json"], user: ["/users/eviltrout.json", "/user_actions.json", diff --git a/lib/topic_creator.rb b/lib/topic_creator.rb index 42b7d94e1a..179e8d2333 100644 --- a/lib/topic_creator.rb +++ b/lib/topic_creator.rb @@ -86,6 +86,8 @@ class TopicCreator topic_params[:created_at] = Time.zone.parse(@opts[:created_at].to_s) if @opts[:created_at].present? + topic_params[:pinned_at] = Time.zone.parse(@opts[:pinned_at].to_s) if @opts[:pinned_at].present? + topic_params end @@ -114,7 +116,7 @@ class TopicCreator @topic.subtype = TopicSubtype.user_to_user unless @topic.subtype unless @opts[:target_usernames].present? || @opts[:target_group_names].present? - @topic.errors.add(:archetype, :cant_send_pm) + @topic.errors.add(:base, :no_user_selected) @errors = @topic.errors raise ActiveRecord::Rollback.new end @@ -150,7 +152,7 @@ class TopicCreator def check_can_send_permission!(topic,item) unless @guardian.can_send_private_message?(item) - topic.errors.add(:archetype, :cant_send_pm) + topic.errors.add(:base, :cant_send_pm) @errors = topic.errors raise ActiveRecord::Rollback.new end diff --git a/lib/topic_list_responder.rb b/lib/topic_list_responder.rb new file mode 100644 index 0000000000..0513330085 --- /dev/null +++ b/lib/topic_list_responder.rb @@ -0,0 +1,24 @@ +# Helps us respond with a topic list from a controller +module TopicListResponder + + def respond_with_list(list) + discourse_expires_in 1.minute + + list.draft_key = Draft::NEW_TOPIC + list.draft_sequence = DraftSequence.current(current_user, Draft::NEW_TOPIC) + list.draft = Draft.get(current_user, list.draft_key, list.draft_sequence) if current_user + + respond_to do |format| + format.html do + @list = list + store_preloaded(list.preload_key, MultiJson.dump(TopicListSerializer.new(list, scope: guardian))) + render 'list/list' + end + format.json do + render_serialized(list, TopicListSerializer) + end + end + end + +end + diff --git a/lib/topic_query.rb b/lib/topic_query.rb index a38bd8b67e..3485e59d86 100644 --- a/lib/topic_query.rb +++ b/lib/topic_query.rb @@ -25,6 +25,8 @@ class TopicQuery status state search + slow_platform + filter ).map(&:to_sym) # Maps `order` to a columns in `topics` @@ -56,9 +58,9 @@ class TopicQuery # When logged in we start with different results if @user builder.add_results(unread_results(topic: topic, per_page: builder.results_left), :high) - builder.add_results(new_results(topic: topic, per_page: builder.category_results_left), :high) unless builder.category_full? + builder.add_results(new_results(topic: topic, per_page: builder.category_results_left)) unless builder.full? end - builder.add_results(random_suggested(topic, builder.results_left, builder.excluded_topic_ids), :low) unless builder.full? + builder.add_results(random_suggested(topic, builder.results_left, builder.excluded_topic_ids)) unless builder.full? create_list(:suggested, {}, builder.results) end @@ -68,11 +70,6 @@ class TopicQuery create_list(:latest, {}, latest_results) end - # The starred topics - def list_starred - create_list(:starred) {|topics| topics.where('tu.starred') } - end - def list_read create_list(:read, unordered: true) do |topics| topics.order('COALESCE(tu.last_visited_at, topics.bumped_at) DESC') @@ -91,6 +88,10 @@ class TopicQuery create_list(:posted) {|l| l.where('tu.posted') } end + def list_bookmarks + create_list(:bookmarks) {|l| l.where('tu.bookmarked') } + end + def list_top_for(period) score = "#{period}_score" create_list(:top, unordered: true) do |topics| @@ -153,17 +154,43 @@ class TopicQuery .where("COALESCE(tu.notification_level, :regular) >= :tracking", regular: TopicUser.notification_levels[:regular], tracking: TopicUser.notification_levels[:tracking]) end + def create_list(filter, options={}, topics = nil) + topics ||= default_results(options) + topics = yield(topics) if block_given? + list = TopicList.new(filter, @user, topics, options.merge(@options)) + list.per_page = per_page_setting + list + end + + def latest_results(options={}) + result = default_results(options) + result = remove_muted_categories(result, @user, exclude: options[:category]) + result + end + + def unread_results(options={}) + result = TopicQuery.unread_filter(default_results(options.reverse_merge(:unordered => true))) + .order('CASE WHEN topics.user_id = tu.user_id THEN 1 ELSE 2 END') + + suggested_ordering(result, options) + end + + def new_results(options={}) + result = TopicQuery.new_filter(default_results(options.reverse_merge(:unordered => true)), @user.treat_as_new_topic_start_date) + result = remove_muted_categories(result, @user, exclude: options[:category]) + suggested_ordering(result, options) + end + protected - def create_list(filter, options={}, topics = nil) - topics ||= default_results(options) - topics = yield(topics) if block_given? - TopicList.new(filter, @user, topics, options.merge(@options)) + def per_page_setting + @options[:slow_platform] ? 15 : 30 end + def private_messages_for(user) options = @options - options.reverse_merge!(per_page: SiteSetting.topics_per_page) + options.reverse_merge!(per_page: per_page_setting) # Start with a list of all topics result = Topic.includes(:allowed_users) @@ -213,7 +240,7 @@ class TopicQuery end if sort_column == 'op_likes' - return result.order("(SELECT like_count FROM posts p3 WHERE p3.topic_id = topics.id AND p3.post_number = 1) #{sort_dir}") + return result.includes(:first_post).order("(SELECT like_count FROM posts p3 WHERE p3.topic_id = topics.id AND p3.post_number = 1) #{sort_dir}") end result.order("topics.#{sort_column} #{sort_dir}") @@ -230,10 +257,10 @@ class TopicQuery # Create results based on a bunch of default options def default_results(options={}) options.reverse_merge!(@options) - options.reverse_merge!(per_page: SiteSetting.topics_per_page) + options.reverse_merge!(per_page: per_page_setting) # Start with a list of all topics - result = Topic + result = Topic.unscoped if @user result = result.joins("LEFT OUTER JOIN topic_users AS tu ON (topics.id = tu.topic_id AND tu.user_id = #{@user.id.to_i})") @@ -286,6 +313,7 @@ class TopicQuery notification_level = ?)', @user.id, level) end + require_deleted_clause = true if status = options[:status] case status when 'open' @@ -294,34 +322,44 @@ class TopicQuery result = result.where('topics.closed') when 'archived' result = result.where('topics.archived') - when 'visible' + when 'listed' result = result.where('topics.visible') - when 'invisible' + when 'unlisted' result = result.where('NOT topics.visible') + when 'deleted' + guardian = Guardian.new(@user) + if guardian.is_staff? + result = result.where('topics.deleted_at IS NOT NULL') + require_deleted_clause = false + end end end + if (filter=options[:filter]) && @user + action = + if filter == "bookmarked" + PostActionType.types[:bookmark] + elsif filter == "liked" + PostActionType.types[:like] + end + if action + result = result.where('topics.id IN (SELECT pp.topic_id + FROM post_actions pa + JOIN posts pp ON pp.id = pa.post_id + WHERE pa.user_id = :user_id AND + pa.post_action_type_id = :action AND + pa.deleted_at IS NULL + )', user_id: @user.id, + action: action + ) + end + end + + result = result.where('topics.deleted_at IS NULL') if require_deleted_clause result = result.where('topics.posts_count <= ?', options[:max_posts]) if options[:max_posts].present? result = result.where('topics.posts_count >= ?', options[:min_posts]) if options[:min_posts].present? - guardian = Guardian.new(@user) - if !guardian.is_admin? - allowed_ids = guardian.allowed_category_ids - if allowed_ids.length > 0 - result = result.where('topics.category_id IS NULL or topics.category_id IN (?)', allowed_ids) - else - result = result.where('topics.category_id IS NULL') - end - result = result.references(:categories) - end - - result - end - - def latest_results(options={}) - result = default_results(options) - result = remove_muted_categories(result, @user, exclude: options[:category]) - result + Guardian.new(@user).filter_allowed_categories(result) end def remove_muted_categories(list, user, opts=nil) @@ -345,19 +383,6 @@ class TopicQuery end - def unread_results(options={}) - result = TopicQuery.unread_filter(default_results(options.reverse_merge(:unordered => true))) - .order('CASE WHEN topics.user_id = tu.user_id THEN 1 ELSE 2 END') - - suggested_ordering(result, options) - end - - def new_results(options={}) - result = TopicQuery.new_filter(default_results(options.reverse_merge(:unordered => true)), @user.treat_as_new_topic_start_date) - result = remove_muted_categories(result, @user, exclude: options[:category]) - suggested_ordering(result, options) - end - def random_suggested(topic, count, excluded_topic_ids=[]) result = default_results(unordered: true, per_page: count).where(closed: false, archived: false) excluded_topic_ids += Category.pluck(:topic_id).compact diff --git a/lib/topic_query_sql.rb b/lib/topic_query_sql.rb index 465737ce12..9decf581a6 100644 --- a/lib/topic_query_sql.rb +++ b/lib/topic_query_sql.rb @@ -5,14 +5,8 @@ module TopicQuerySQL class << self - # use the constants in conjuction with COALESCE to determine the order with regard to pinned - # topics that have been cleared by the user. There might be a cleaner way to do this. def lowest_date - "2010-01-01" - end - - def highest_date - "infinity" + "1900-01-01" end def order_by_category_sql(dir) @@ -23,17 +17,18 @@ module TopicQuerySQL def order_with_pinned_sql "CASE WHEN (COALESCE(topics.pinned_at, '#{lowest_date}') > COALESCE(tu.cleared_pinned_at, '#{lowest_date}')) - THEN '#{highest_date}' - ELSE topics.bumped_at + THEN topics.pinned_at + interval '9999 years' + ELSE topics.bumped_at END DESC" end # If you've clearned the pin, use bumped_at, otherwise put it at the top def order_nocategory_with_pinned_sql "CASE - WHEN topics.pinned_globally and (COALESCE(topics.pinned_at, '#{lowest_date}') > COALESCE(tu.cleared_pinned_at, '#{lowest_date}')) - THEN '#{highest_date}' - ELSE topics.bumped_at + WHEN topics.pinned_globally + AND (COALESCE(topics.pinned_at, '#{lowest_date}') > COALESCE(tu.cleared_pinned_at, '#{lowest_date}')) + THEN topics.pinned_at + interval '9999 years' + ELSE topics.bumped_at END DESC" end @@ -42,7 +37,7 @@ module TopicQuerySQL end def order_nocategory_basic_bumped - "CASE WHEN topics.pinned_globally and (topics.pinned_at IS NOT NULL) THEN 0 ELSE 1 END, topics.bumped_at DESC" + "CASE WHEN topics.pinned_globally AND (topics.pinned_at IS NOT NULL) THEN 0 ELSE 1 END, topics.bumped_at DESC" end def order_top_for(score) @@ -51,7 +46,7 @@ module TopicQuerySQL def order_top_with_pinned_category_for(score) # display pinned topics first - "CASE WHEN COALESCE(topics.pinned_at, '#{lowest_date}') > COALESCE(tu.cleared_pinned_at, '#{lowest_date}') THEN 1 ELSE 0 END DESC, + "CASE WHEN (COALESCE(topics.pinned_at, '#{lowest_date}') > COALESCE(tu.cleared_pinned_at, '#{lowest_date}')) THEN 0 ELSE 1 END, top_topics.#{score} DESC, topics.bumped_at DESC" end diff --git a/lib/topic_view.rb b/lib/topic_view.rb index a0f6b072b6..b9ffd64341 100644 --- a/lib/topic_view.rb +++ b/lib/topic_view.rb @@ -1,14 +1,21 @@ require_dependency 'guardian' require_dependency 'topic_query' require_dependency 'filter_best_posts' -require_dependency 'summarize' require_dependency 'gaps' class TopicView - attr_reader :topic, :posts, :guardian, :filtered_posts + attr_reader :topic, :posts, :guardian, :filtered_posts, :chunk_size attr_accessor :draft, :draft_key, :draft_sequence, :user_custom_fields + def self.slow_chunk_size + 10 + end + + def self.chunk_size + 20 + end + def initialize(topic_id, user=nil, options={}) @user = user @guardian = Guardian.new(@user) @@ -21,7 +28,8 @@ class TopicView @page = @page.to_i @page = 1 if @page.zero? - @limit ||= SiteSetting.posts_chunksize + @chunk_size = options[:slow_platform] ? TopicView.slow_chunk_size : TopicView.chunk_size + @limit ||= @chunk_size setup_filtered_posts @@ -115,7 +123,8 @@ class TopicView def summary return nil if desired_post.blank? # TODO, this is actually quite slow, should be cached in the post table - Summarize.new(desired_post.cooked).summary + excerpt = desired_post.excerpt(500, strip_links: true, text_entities: true) + (excerpt || "").gsub(/\n/, ' ').strip end def image_url @@ -203,6 +212,7 @@ class TopicView def post_counts_by_user @post_counts_by_user ||= Post.where(topic_id: @topic.id) + .where("user_id IS NOT NULL") .group(:user_id) .order("count_all DESC") .limit(24) @@ -332,7 +342,7 @@ class TopicView # Filters if @filter == 'summary' - @filtered_posts = @filtered_posts.summary + @filtered_posts = @filtered_posts.summary(@topic.id) @contains_gaps = true end diff --git a/lib/trust_level.rb b/lib/trust_level.rb index 6fba142eb3..a81f5fb383 100644 --- a/lib/trust_level.rb +++ b/lib/trust_level.rb @@ -24,7 +24,7 @@ class TrustLevel end def valid?(level) - valid_range === level + valid_range === level end def valid_range diff --git a/lib/validators/email_validator.rb b/lib/validators/email_validator.rb index 6f3e8872e0..e0e759571f 100644 --- a/lib/validators/email_validator.rb +++ b/lib/validators/email_validator.rb @@ -2,11 +2,11 @@ class EmailValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) if (setting = SiteSetting.email_domains_whitelist).present? - unless email_in_restriction_setting?(setting, value) + unless email_in_restriction_setting?(setting, value) || is_developer?(value) record.errors.add(attribute, I18n.t(:'user.email.not_allowed')) end elsif (setting = SiteSetting.email_domains_blacklist).present? - if email_in_restriction_setting?(setting, value) + if email_in_restriction_setting?(setting, value) && !is_developer?(value) record.errors.add(attribute, I18n.t(:'user.email.not_allowed')) end end @@ -21,6 +21,10 @@ class EmailValidator < ActiveModel::EachValidator value =~ regexp end + def is_developer?(value) + Rails.configuration.respond_to?(:developer_emails) && Rails.configuration.developer_emails.include?(value) + end + def self.email_regex /^[a-zA-Z0-9!#\$%&'*+\/=?\^_`{|}~\-]+(?:\.[a-zA-Z0-9!#\$%&'\*+\/=?\^_`{|}~\-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?$/ end diff --git a/lib/validators/reply_by_email_address_validator.rb b/lib/validators/reply_by_email_address_validator.rb new file mode 100644 index 0000000000..f5c19d2bdb --- /dev/null +++ b/lib/validators/reply_by_email_address_validator.rb @@ -0,0 +1,17 @@ +class ReplyByEmailAddressValidator + def initialize(opts={}) + @opts = opts + end + + def valid_value?(val) + return true if val.blank? + + !!(val =~ /@/i) && + !!(val =~ /%{reply_key}/i) && + val.gsub(/\+?%{reply_key}/i, "") != SiteSetting.notification_email + end + + def error_message + I18n.t('site_settings.errors.invalid_reply_by_email_address') + end +end diff --git a/lib/version.rb b/lib/version.rb index 0d840115d9..0ba9285277 100644 --- a/lib/version.rb +++ b/lib/version.rb @@ -3,9 +3,9 @@ module Discourse unless defined? ::Discourse::VERSION module VERSION #:nodoc: MAJOR = 1 - MINOR = 1 - TINY = 3 - PRE = nil + MINOR = 2 + TINY = 0 + PRE = 'beta9' STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') end diff --git a/plugins/.gitkeep b/plugins/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/emoji/README.md b/plugins/emoji/README.md deleted file mode 100644 index b8a6fa9828..0000000000 --- a/plugins/emoji/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Discourse Emoji Gem - -Adds Emoji support to discourse. Thanks to the gemoji gem for the assets. diff --git a/plugins/emoji/Rakefile b/plugins/emoji/Rakefile deleted file mode 100644 index 56bb7292a1..0000000000 --- a/plugins/emoji/Rakefile +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env rake -require "bundler/gem_tasks" -require "rspec/core/rake_task" - -RSpec::Core::RakeTask.new(:test) do |spec| - spec.pattern = 'spec/*_spec.rb' -end diff --git a/plugins/emoji/assets/javascripts/emoji.js.erb b/plugins/emoji/assets/javascripts/emoji.js.erb deleted file mode 100644 index 147f91ca34..0000000000 --- a/plugins/emoji/assets/javascripts/emoji.js.erb +++ /dev/null @@ -1,186 +0,0 @@ -(function() { - var emoji = <%= Dir.glob(File.expand_path("../../../public/images/*.png", __FILE__)).map{|f| File.basename(f).split(".")[0]}.inspect %>; - - var _extendedEmoji = {}; - Discourse.Dialect.registerEmoji = function(code, url) { - _extendedEmoji[code] = url; - }; - - function urlFor(code) { - var url = _extendedEmoji[code]; - if (!url && emoji.indexOf(code) !== -1) { - url = Discourse.getURL('/plugins/emoji/images/' + code + '.png'); - } - return url; - } - - function imageFor(code) { - var url = urlFor(code); - if (url) { - return ['img', {href: url, title: ':' + code + ':', 'class': 'emoji', alt: code}]; - } - } - - // Also support default emotions - var translations = { - ':)' : 'smile', - ':-)' : 'smile', - ':(' : 'frowning', - ':-(' : 'frowning', - ';)' : 'wink', - ';-)' : 'wink', - ':\'(' : 'cry', - ':\'-(' : 'cry', - ':-\'(' : 'cry', - ':p' : 'stuck_out_tongue', - ':P' : 'stuck_out_tongue', - ':-P' : 'stuck_out_tongue', - ':O' : 'open_mouth', - ':-O' : 'open_mouth', - ':D' : 'smiley', - ':-D' : 'smiley', - ':|' : 'expressionless', - ':-|' : 'expressionless', - ";P" : 'stuck_out_tongue_winking_eye', - ";-P" : 'stuck_out_tongue_winking_eye', - ';)' : 'wink', - ';-)' : 'wink', - ":$" : 'blush', - ":-$" : 'blush' - }; - - function checkPrev(prev) { - if (prev && prev.length) { - var lastToken = prev[prev.length-1]; - if (lastToken && lastToken.charAt) { - var lastChar = lastToken.charAt(lastToken.length-1); - if (lastChar !== ' ' && lastChar !== "\n") return false; - } - } - return true; - } - - var translationsWithColon = {}; - Object.keys(translations).forEach(function (t) { - if (t[0] === ':') { - translationsWithColon[t] = translations[t]; - } else { - var replacement = translations[t]; - Discourse.Dialect.inlineReplace(t, function (token, match, prev) { - if (!Discourse.SiteSettings.enable_emoji) { return token; } - return checkPrev(prev) ? imageFor(replacement) : token; - }); - } - }); - - function escapeRegExp(s) { - return s.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&') - } - - var translationColonRegexp = new RegExp(Object.keys(translationsWithColon).map(function (t) { - return "(" + escapeRegExp(t) + ")"; - }).join("|")); - - Discourse.Dialect.registerInline(':', function(text, match, prev) { - if (!Discourse.SiteSettings.enable_emoji) { return; } - - var endPos = text.indexOf(':', 1), - firstSpace = text.search(/\s/), - contents; - - if (!checkPrev(prev)) { return; } - - // If there is no trailing colon, check our translations that begin with colons - if (endPos === -1 || (firstSpace !== -1 && endPos > firstSpace)) { - translationColonRegexp.lastIndex = 0; - var m = translationColonRegexp.exec(text); - if (m && m[0] && text.indexOf(m[0]) === 0) { - - // Check outer edge - var lastChar = text.charAt(m[0].length); - if (lastChar && (lastChar !== ' ' && lastChar !== "\n")) return; - contents = imageFor(translationsWithColon[m[0]]); - if (contents) { - return [m[0].length, contents]; - } - } - return; - } - - // Simple find and replace from our array - var between = text.slice(1, endPos); - contents = imageFor(between); - if (contents) { - return [endPos+1, contents]; - } - }); - - // TODO: Make this a proper ES6 import - var ComposerView = (Discourse && Discourse.ComposerView) || (typeof require !== "undefined" && require('discourse/views/composer').default); - if (ComposerView) { - ComposerView.on("initWmdEditor", function(event){ - if (!Discourse.SiteSettings.enable_emoji) { return; } - - var baseUrl = Discourse.getURL("/"); - - template = Handlebars.compile("
" + - "" + - "
"); - - var toSearch = emoji.concat(Object.keys(_extendedEmoji)); - $('#wmd-input').autocomplete({ - template: template, - key: ":", - transformComplete: function(v){ return v.code + ":"; }, - dataSource: function(term){ - return new Ember.RSVP.Promise(function(resolve) { - var full = ":" + term; - term = term.toLowerCase(); - - if (term === "") { - return resolve(["smile", "smiley", "wink", "sunny", "blush"]); - } - - if (translations[full]) { - return resolve([translations[full]]); - } - - var options = []; - var i; - for (i=0; i < toSearch.length; i++) { - if (toSearch[i].indexOf(term) === 0) { - options.push(toSearch[i]); - if(options.length > 4) { break; } - } - } - - if (options.length <= 4) { - for (i=0; i < toSearch.length; i++) { - if (toSearch[i].indexOf(term) > 0) { - options.push(toSearch[i]); - if(options.length > 4) { break; } - } - } - } - - return resolve(options); - }).then(function(list) { - return list.map(function(i) { - return {code: i, src: urlFor(i)}; - }); - }); - } - }); - }); - } - - Discourse.Markdown.whiteListTag('img', 'class', 'emoji'); -}).call(this); diff --git a/plugins/emoji/assets/stylesheets/emoji.css b/plugins/emoji/assets/stylesheets/emoji.css deleted file mode 100644 index a7e070e28c..0000000000 --- a/plugins/emoji/assets/stylesheets/emoji.css +++ /dev/null @@ -1,5 +0,0 @@ -body img.emoji { - width: 20px; - height: 20px; - vertical-align: middle; -} diff --git a/plugins/emoji/config/locales/client.en.yml b/plugins/emoji/config/locales/client.en.yml deleted file mode 100644 index 6315b630f8..0000000000 --- a/plugins/emoji/config/locales/client.en.yml +++ /dev/null @@ -1,6 +0,0 @@ -en: - admin_js: - admin: - site_settings: - categories: - plugins: "Plugins" diff --git a/plugins/emoji/config/locales/client.pl_PL.yml b/plugins/emoji/config/locales/client.pl_PL.yml deleted file mode 100644 index 4e9d062473..0000000000 --- a/plugins/emoji/config/locales/client.pl_PL.yml +++ /dev/null @@ -1,6 +0,0 @@ -pl_PL: - admin_js: - admin: - site_settings: - categories: - plugins: "Wtyczki" diff --git a/plugins/emoji/config/locales/client.ru.yml b/plugins/emoji/config/locales/client.ru.yml deleted file mode 100644 index a4eea97ab1..0000000000 --- a/plugins/emoji/config/locales/client.ru.yml +++ /dev/null @@ -1,6 +0,0 @@ -ru: - admin_js: - admin: - site_settings: - categories: - plugins: "Плагины" diff --git a/plugins/emoji/config/locales/client.zh_CN.yml b/plugins/emoji/config/locales/client.zh_CN.yml deleted file mode 100644 index cdf886ae89..0000000000 --- a/plugins/emoji/config/locales/client.zh_CN.yml +++ /dev/null @@ -1,6 +0,0 @@ -zh_CN: - admin_js: - admin: - site_settings: - categories: - plugins: "插件" diff --git a/plugins/emoji/config/locales/server.en.yml b/plugins/emoji/config/locales/server.en.yml deleted file mode 100644 index 5da8351cf6..0000000000 --- a/plugins/emoji/config/locales/server.en.yml +++ /dev/null @@ -1,3 +0,0 @@ -en: - site_settings: - enable_emoji: "Enable the emoji plugin" diff --git a/plugins/emoji/config/locales/server.pl_PL.yml b/plugins/emoji/config/locales/server.pl_PL.yml deleted file mode 100644 index 93376e2bf5..0000000000 --- a/plugins/emoji/config/locales/server.pl_PL.yml +++ /dev/null @@ -1,3 +0,0 @@ -pl_PL: - site_settings: - enable_emoji: "Włącz wyświetlanie Emoji" diff --git a/plugins/emoji/config/locales/server.ru.yml b/plugins/emoji/config/locales/server.ru.yml deleted file mode 100644 index 13d10c3cf5..0000000000 --- a/plugins/emoji/config/locales/server.ru.yml +++ /dev/null @@ -1,3 +0,0 @@ -ru: - site_settings: - enable_emoji: "Включить плагин emoji" diff --git a/plugins/emoji/config/locales/server.zh_CN.yml b/plugins/emoji/config/locales/server.zh_CN.yml deleted file mode 100644 index 5124f0651d..0000000000 --- a/plugins/emoji/config/locales/server.zh_CN.yml +++ /dev/null @@ -1,3 +0,0 @@ -zh_CN: - site_settings: - enable_emoji: "启用绘文字(emoji)插件" diff --git a/plugins/emoji/config/settings.yml b/plugins/emoji/config/settings.yml deleted file mode 100644 index 10896b1013..0000000000 --- a/plugins/emoji/config/settings.yml +++ /dev/null @@ -1,4 +0,0 @@ -plugins: - enable_emoji: - default: true - client: true diff --git a/plugins/emoji/plugin.rb b/plugins/emoji/plugin.rb deleted file mode 100644 index 261b668634..0000000000 --- a/plugins/emoji/plugin.rb +++ /dev/null @@ -1,14 +0,0 @@ -# name: emoji -# about: emoji support for Discourse -# version: 0.1 -# authors: Sam Saffron, Robin Ward - -register_asset('javascripts/emoji.js.erb', :server_side) -register_asset('stylesheets/emoji.css') - -after_initialize do - - # whitelist emojis so that new user can post emojis - Post::white_listed_image_classes << "emoji" - -end diff --git a/plugins/emoji/public/images/+1.png b/plugins/emoji/public/images/+1.png deleted file mode 120000 index 5db29d6679..0000000000 --- a/plugins/emoji/public/images/+1.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f44d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/-1.png b/plugins/emoji/public/images/-1.png deleted file mode 120000 index 5b63ed9312..0000000000 --- a/plugins/emoji/public/images/-1.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f44e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/100.png b/plugins/emoji/public/images/100.png deleted file mode 120000 index e30cb95244..0000000000 --- a/plugins/emoji/public/images/100.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4af.png \ No newline at end of file diff --git a/plugins/emoji/public/images/1234.png b/plugins/emoji/public/images/1234.png deleted file mode 120000 index 4bf1900645..0000000000 --- a/plugins/emoji/public/images/1234.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f522.png \ No newline at end of file diff --git a/plugins/emoji/public/images/8ball.png b/plugins/emoji/public/images/8ball.png deleted file mode 120000 index 6c016fe572..0000000000 --- a/plugins/emoji/public/images/8ball.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3b1.png \ No newline at end of file diff --git a/plugins/emoji/public/images/a.png b/plugins/emoji/public/images/a.png deleted file mode 120000 index 18baa8b96e..0000000000 --- a/plugins/emoji/public/images/a.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f170.png \ No newline at end of file diff --git a/plugins/emoji/public/images/ab.png b/plugins/emoji/public/images/ab.png deleted file mode 120000 index 6f9b70186c..0000000000 --- a/plugins/emoji/public/images/ab.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f18e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/abc.png b/plugins/emoji/public/images/abc.png deleted file mode 120000 index 129ae4d246..0000000000 --- a/plugins/emoji/public/images/abc.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f524.png \ No newline at end of file diff --git a/plugins/emoji/public/images/abcd.png b/plugins/emoji/public/images/abcd.png deleted file mode 120000 index 8e0e9ab617..0000000000 --- a/plugins/emoji/public/images/abcd.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f521.png \ No newline at end of file diff --git a/plugins/emoji/public/images/accept.png b/plugins/emoji/public/images/accept.png deleted file mode 120000 index 2209f8c9b7..0000000000 --- a/plugins/emoji/public/images/accept.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f251.png \ No newline at end of file diff --git a/plugins/emoji/public/images/aerial_tramway.png b/plugins/emoji/public/images/aerial_tramway.png deleted file mode 120000 index 4c5c67d0be..0000000000 --- a/plugins/emoji/public/images/aerial_tramway.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6a1.png \ No newline at end of file diff --git a/plugins/emoji/public/images/airplane.png b/plugins/emoji/public/images/airplane.png deleted file mode 120000 index f23c42ec98..0000000000 --- a/plugins/emoji/public/images/airplane.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2708.png \ No newline at end of file diff --git a/plugins/emoji/public/images/alarm_clock.png b/plugins/emoji/public/images/alarm_clock.png deleted file mode 120000 index d252dcded5..0000000000 --- a/plugins/emoji/public/images/alarm_clock.png +++ /dev/null @@ -1 +0,0 @@ -unicode/23f0.png \ No newline at end of file diff --git a/plugins/emoji/public/images/alien.png b/plugins/emoji/public/images/alien.png deleted file mode 120000 index b9b26dda62..0000000000 --- a/plugins/emoji/public/images/alien.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f47d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/ambulance.png b/plugins/emoji/public/images/ambulance.png deleted file mode 120000 index 980cdca7e0..0000000000 --- a/plugins/emoji/public/images/ambulance.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f691.png \ No newline at end of file diff --git a/plugins/emoji/public/images/anchor.png b/plugins/emoji/public/images/anchor.png deleted file mode 120000 index 5d08956426..0000000000 --- a/plugins/emoji/public/images/anchor.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2693.png \ No newline at end of file diff --git a/plugins/emoji/public/images/angel.png b/plugins/emoji/public/images/angel.png deleted file mode 120000 index 949743f558..0000000000 --- a/plugins/emoji/public/images/angel.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f47c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/anger.png b/plugins/emoji/public/images/anger.png deleted file mode 120000 index dfdd816cd1..0000000000 --- a/plugins/emoji/public/images/anger.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4a2.png \ No newline at end of file diff --git a/plugins/emoji/public/images/angry.png b/plugins/emoji/public/images/angry.png deleted file mode 120000 index 284a52e3fa..0000000000 --- a/plugins/emoji/public/images/angry.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f620.png \ No newline at end of file diff --git a/plugins/emoji/public/images/anguished.png b/plugins/emoji/public/images/anguished.png deleted file mode 120000 index 8fe3bf3db9..0000000000 --- a/plugins/emoji/public/images/anguished.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f627.png \ No newline at end of file diff --git a/plugins/emoji/public/images/ant.png b/plugins/emoji/public/images/ant.png deleted file mode 120000 index c40148e514..0000000000 --- a/plugins/emoji/public/images/ant.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f41c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/apple.png b/plugins/emoji/public/images/apple.png deleted file mode 120000 index 4eb02bb660..0000000000 --- a/plugins/emoji/public/images/apple.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f34e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/aquarius.png b/plugins/emoji/public/images/aquarius.png deleted file mode 120000 index b6d30383d0..0000000000 --- a/plugins/emoji/public/images/aquarius.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2652.png \ No newline at end of file diff --git a/plugins/emoji/public/images/aries.png b/plugins/emoji/public/images/aries.png deleted file mode 120000 index ca868c7f6b..0000000000 --- a/plugins/emoji/public/images/aries.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2648.png \ No newline at end of file diff --git a/plugins/emoji/public/images/arrow_backward.png b/plugins/emoji/public/images/arrow_backward.png deleted file mode 120000 index 5c0c90da3e..0000000000 --- a/plugins/emoji/public/images/arrow_backward.png +++ /dev/null @@ -1 +0,0 @@ -unicode/25c0.png \ No newline at end of file diff --git a/plugins/emoji/public/images/arrow_double_down.png b/plugins/emoji/public/images/arrow_double_down.png deleted file mode 120000 index 548e425074..0000000000 --- a/plugins/emoji/public/images/arrow_double_down.png +++ /dev/null @@ -1 +0,0 @@ -unicode/23ec.png \ No newline at end of file diff --git a/plugins/emoji/public/images/arrow_double_up.png b/plugins/emoji/public/images/arrow_double_up.png deleted file mode 120000 index 0c01270502..0000000000 --- a/plugins/emoji/public/images/arrow_double_up.png +++ /dev/null @@ -1 +0,0 @@ -unicode/23eb.png \ No newline at end of file diff --git a/plugins/emoji/public/images/arrow_down.png b/plugins/emoji/public/images/arrow_down.png deleted file mode 120000 index 566f72c231..0000000000 --- a/plugins/emoji/public/images/arrow_down.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2b07.png \ No newline at end of file diff --git a/plugins/emoji/public/images/arrow_down_small.png b/plugins/emoji/public/images/arrow_down_small.png deleted file mode 120000 index 8431dfccce..0000000000 --- a/plugins/emoji/public/images/arrow_down_small.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f53d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/arrow_forward.png b/plugins/emoji/public/images/arrow_forward.png deleted file mode 120000 index 7ffbaf72b4..0000000000 --- a/plugins/emoji/public/images/arrow_forward.png +++ /dev/null @@ -1 +0,0 @@ -unicode/25b6.png \ No newline at end of file diff --git a/plugins/emoji/public/images/arrow_heading_down.png b/plugins/emoji/public/images/arrow_heading_down.png deleted file mode 120000 index 934028fe2e..0000000000 --- a/plugins/emoji/public/images/arrow_heading_down.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2935.png \ No newline at end of file diff --git a/plugins/emoji/public/images/arrow_heading_up.png b/plugins/emoji/public/images/arrow_heading_up.png deleted file mode 120000 index 25bfe6ac52..0000000000 --- a/plugins/emoji/public/images/arrow_heading_up.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2934.png \ No newline at end of file diff --git a/plugins/emoji/public/images/arrow_left.png b/plugins/emoji/public/images/arrow_left.png deleted file mode 120000 index 3f947d0eaa..0000000000 --- a/plugins/emoji/public/images/arrow_left.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2b05.png \ No newline at end of file diff --git a/plugins/emoji/public/images/arrow_lower_left.png b/plugins/emoji/public/images/arrow_lower_left.png deleted file mode 120000 index 62db4d3725..0000000000 --- a/plugins/emoji/public/images/arrow_lower_left.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2199.png \ No newline at end of file diff --git a/plugins/emoji/public/images/arrow_lower_right.png b/plugins/emoji/public/images/arrow_lower_right.png deleted file mode 120000 index c3748cf7c0..0000000000 --- a/plugins/emoji/public/images/arrow_lower_right.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2198.png \ No newline at end of file diff --git a/plugins/emoji/public/images/arrow_right.png b/plugins/emoji/public/images/arrow_right.png deleted file mode 120000 index 2f9b2c7f53..0000000000 --- a/plugins/emoji/public/images/arrow_right.png +++ /dev/null @@ -1 +0,0 @@ -unicode/27a1.png \ No newline at end of file diff --git a/plugins/emoji/public/images/arrow_right_hook.png b/plugins/emoji/public/images/arrow_right_hook.png deleted file mode 120000 index 5a3039cee5..0000000000 --- a/plugins/emoji/public/images/arrow_right_hook.png +++ /dev/null @@ -1 +0,0 @@ -unicode/21aa.png \ No newline at end of file diff --git a/plugins/emoji/public/images/arrow_up.png b/plugins/emoji/public/images/arrow_up.png deleted file mode 120000 index 90f1430340..0000000000 --- a/plugins/emoji/public/images/arrow_up.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2b06.png \ No newline at end of file diff --git a/plugins/emoji/public/images/arrow_up_down.png b/plugins/emoji/public/images/arrow_up_down.png deleted file mode 120000 index 5e0689401e..0000000000 --- a/plugins/emoji/public/images/arrow_up_down.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2195.png \ No newline at end of file diff --git a/plugins/emoji/public/images/arrow_up_small.png b/plugins/emoji/public/images/arrow_up_small.png deleted file mode 120000 index 9973a430b7..0000000000 --- a/plugins/emoji/public/images/arrow_up_small.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f53c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/arrow_upper_left.png b/plugins/emoji/public/images/arrow_upper_left.png deleted file mode 120000 index 9b8c763fa9..0000000000 --- a/plugins/emoji/public/images/arrow_upper_left.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2196.png \ No newline at end of file diff --git a/plugins/emoji/public/images/arrow_upper_right.png b/plugins/emoji/public/images/arrow_upper_right.png deleted file mode 120000 index ed0e48635f..0000000000 --- a/plugins/emoji/public/images/arrow_upper_right.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2197.png \ No newline at end of file diff --git a/plugins/emoji/public/images/arrows_clockwise.png b/plugins/emoji/public/images/arrows_clockwise.png deleted file mode 120000 index ba9d84f4ab..0000000000 --- a/plugins/emoji/public/images/arrows_clockwise.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f503.png \ No newline at end of file diff --git a/plugins/emoji/public/images/arrows_counterclockwise.png b/plugins/emoji/public/images/arrows_counterclockwise.png deleted file mode 120000 index 6ed37bd08a..0000000000 --- a/plugins/emoji/public/images/arrows_counterclockwise.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f504.png \ No newline at end of file diff --git a/plugins/emoji/public/images/art.png b/plugins/emoji/public/images/art.png deleted file mode 120000 index 62f5e4c552..0000000000 --- a/plugins/emoji/public/images/art.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3a8.png \ No newline at end of file diff --git a/plugins/emoji/public/images/articulated_lorry.png b/plugins/emoji/public/images/articulated_lorry.png deleted file mode 120000 index eb1bd5e5e2..0000000000 --- a/plugins/emoji/public/images/articulated_lorry.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f69b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/astonished.png b/plugins/emoji/public/images/astonished.png deleted file mode 120000 index 1c45fdbbf6..0000000000 --- a/plugins/emoji/public/images/astonished.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f632.png \ No newline at end of file diff --git a/plugins/emoji/public/images/athletic_shoe.png b/plugins/emoji/public/images/athletic_shoe.png deleted file mode 120000 index e103445666..0000000000 --- a/plugins/emoji/public/images/athletic_shoe.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f45f.png \ No newline at end of file diff --git a/plugins/emoji/public/images/atm.png b/plugins/emoji/public/images/atm.png deleted file mode 120000 index c9e7d78798..0000000000 --- a/plugins/emoji/public/images/atm.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3e7.png \ No newline at end of file diff --git a/plugins/emoji/public/images/b.png b/plugins/emoji/public/images/b.png deleted file mode 120000 index bd6f45b31b..0000000000 --- a/plugins/emoji/public/images/b.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f171.png \ No newline at end of file diff --git a/plugins/emoji/public/images/baby.png b/plugins/emoji/public/images/baby.png deleted file mode 120000 index 34b50d27bd..0000000000 --- a/plugins/emoji/public/images/baby.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f476.png \ No newline at end of file diff --git a/plugins/emoji/public/images/baby_bottle.png b/plugins/emoji/public/images/baby_bottle.png deleted file mode 120000 index 45ebf95c95..0000000000 --- a/plugins/emoji/public/images/baby_bottle.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f37c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/baby_chick.png b/plugins/emoji/public/images/baby_chick.png deleted file mode 120000 index a2afe9925c..0000000000 --- a/plugins/emoji/public/images/baby_chick.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f424.png \ No newline at end of file diff --git a/plugins/emoji/public/images/baby_symbol.png b/plugins/emoji/public/images/baby_symbol.png deleted file mode 120000 index 442059ce4b..0000000000 --- a/plugins/emoji/public/images/baby_symbol.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6bc.png \ No newline at end of file diff --git a/plugins/emoji/public/images/back.png b/plugins/emoji/public/images/back.png deleted file mode 120000 index 13583ebef8..0000000000 --- a/plugins/emoji/public/images/back.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f519.png \ No newline at end of file diff --git a/plugins/emoji/public/images/baggage_claim.png b/plugins/emoji/public/images/baggage_claim.png deleted file mode 120000 index f658d3a6bd..0000000000 --- a/plugins/emoji/public/images/baggage_claim.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6c4.png \ No newline at end of file diff --git a/plugins/emoji/public/images/balloon.png b/plugins/emoji/public/images/balloon.png deleted file mode 120000 index eb086d96eb..0000000000 --- a/plugins/emoji/public/images/balloon.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f388.png \ No newline at end of file diff --git a/plugins/emoji/public/images/ballot_box_with_check.png b/plugins/emoji/public/images/ballot_box_with_check.png deleted file mode 120000 index b695327b84..0000000000 --- a/plugins/emoji/public/images/ballot_box_with_check.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2611.png \ No newline at end of file diff --git a/plugins/emoji/public/images/bamboo.png b/plugins/emoji/public/images/bamboo.png deleted file mode 120000 index e53a4c8e9c..0000000000 --- a/plugins/emoji/public/images/bamboo.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f38d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/banana.png b/plugins/emoji/public/images/banana.png deleted file mode 120000 index b68fc18b1d..0000000000 --- a/plugins/emoji/public/images/banana.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f34c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/bangbang.png b/plugins/emoji/public/images/bangbang.png deleted file mode 120000 index 12fbcfcbbb..0000000000 --- a/plugins/emoji/public/images/bangbang.png +++ /dev/null @@ -1 +0,0 @@ -unicode/203c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/bank.png b/plugins/emoji/public/images/bank.png deleted file mode 120000 index 1574a3738f..0000000000 --- a/plugins/emoji/public/images/bank.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3e6.png \ No newline at end of file diff --git a/plugins/emoji/public/images/bar_chart.png b/plugins/emoji/public/images/bar_chart.png deleted file mode 120000 index 9df86b6e17..0000000000 --- a/plugins/emoji/public/images/bar_chart.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4ca.png \ No newline at end of file diff --git a/plugins/emoji/public/images/barber.png b/plugins/emoji/public/images/barber.png deleted file mode 120000 index 8260a72117..0000000000 --- a/plugins/emoji/public/images/barber.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f488.png \ No newline at end of file diff --git a/plugins/emoji/public/images/baseball.png b/plugins/emoji/public/images/baseball.png deleted file mode 120000 index e47b7da5cd..0000000000 --- a/plugins/emoji/public/images/baseball.png +++ /dev/null @@ -1 +0,0 @@ -unicode/26be.png \ No newline at end of file diff --git a/plugins/emoji/public/images/basketball.png b/plugins/emoji/public/images/basketball.png deleted file mode 120000 index 99d5593d69..0000000000 --- a/plugins/emoji/public/images/basketball.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3c0.png \ No newline at end of file diff --git a/plugins/emoji/public/images/bath.png b/plugins/emoji/public/images/bath.png deleted file mode 120000 index 72f5438818..0000000000 --- a/plugins/emoji/public/images/bath.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6c0.png \ No newline at end of file diff --git a/plugins/emoji/public/images/bathtub.png b/plugins/emoji/public/images/bathtub.png deleted file mode 120000 index c1e9bd9eb6..0000000000 --- a/plugins/emoji/public/images/bathtub.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6c1.png \ No newline at end of file diff --git a/plugins/emoji/public/images/battery.png b/plugins/emoji/public/images/battery.png deleted file mode 120000 index afbf9d7db6..0000000000 --- a/plugins/emoji/public/images/battery.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f50b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/bear.png b/plugins/emoji/public/images/bear.png deleted file mode 120000 index f9f1439619..0000000000 --- a/plugins/emoji/public/images/bear.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f43b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/bee.png b/plugins/emoji/public/images/bee.png deleted file mode 120000 index d1768eca5e..0000000000 --- a/plugins/emoji/public/images/bee.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f41d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/beer.png b/plugins/emoji/public/images/beer.png deleted file mode 120000 index 86d1ba6587..0000000000 --- a/plugins/emoji/public/images/beer.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f37a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/beers.png b/plugins/emoji/public/images/beers.png deleted file mode 120000 index 8f22041721..0000000000 --- a/plugins/emoji/public/images/beers.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f37b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/beetle.png b/plugins/emoji/public/images/beetle.png deleted file mode 120000 index 3ef684fcd7..0000000000 --- a/plugins/emoji/public/images/beetle.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f41e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/beginner.png b/plugins/emoji/public/images/beginner.png deleted file mode 120000 index 8fc7324d8d..0000000000 --- a/plugins/emoji/public/images/beginner.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f530.png \ No newline at end of file diff --git a/plugins/emoji/public/images/bell.png b/plugins/emoji/public/images/bell.png deleted file mode 120000 index b85a2240a2..0000000000 --- a/plugins/emoji/public/images/bell.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f514.png \ No newline at end of file diff --git a/plugins/emoji/public/images/bento.png b/plugins/emoji/public/images/bento.png deleted file mode 120000 index 24ca0a3ffe..0000000000 --- a/plugins/emoji/public/images/bento.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f371.png \ No newline at end of file diff --git a/plugins/emoji/public/images/bicyclist.png b/plugins/emoji/public/images/bicyclist.png deleted file mode 120000 index 8481c53663..0000000000 --- a/plugins/emoji/public/images/bicyclist.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6b4.png \ No newline at end of file diff --git a/plugins/emoji/public/images/bike.png b/plugins/emoji/public/images/bike.png deleted file mode 120000 index a92aa799f7..0000000000 --- a/plugins/emoji/public/images/bike.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6b2.png \ No newline at end of file diff --git a/plugins/emoji/public/images/bikini.png b/plugins/emoji/public/images/bikini.png deleted file mode 120000 index 708f6ef1c2..0000000000 --- a/plugins/emoji/public/images/bikini.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f459.png \ No newline at end of file diff --git a/plugins/emoji/public/images/bird.png b/plugins/emoji/public/images/bird.png deleted file mode 120000 index 2d659832a2..0000000000 --- a/plugins/emoji/public/images/bird.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f426.png \ No newline at end of file diff --git a/plugins/emoji/public/images/birthday.png b/plugins/emoji/public/images/birthday.png deleted file mode 120000 index a1a0285283..0000000000 --- a/plugins/emoji/public/images/birthday.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f382.png \ No newline at end of file diff --git a/plugins/emoji/public/images/black_circle.png b/plugins/emoji/public/images/black_circle.png deleted file mode 120000 index 99487ce5c1..0000000000 --- a/plugins/emoji/public/images/black_circle.png +++ /dev/null @@ -1 +0,0 @@ -unicode/26ab.png \ No newline at end of file diff --git a/plugins/emoji/public/images/black_joker.png b/plugins/emoji/public/images/black_joker.png deleted file mode 120000 index f9ece1e8be..0000000000 --- a/plugins/emoji/public/images/black_joker.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f0cf.png \ No newline at end of file diff --git a/plugins/emoji/public/images/black_large_square.png b/plugins/emoji/public/images/black_large_square.png deleted file mode 120000 index f753b44cf8..0000000000 --- a/plugins/emoji/public/images/black_large_square.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2b1b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/black_medium_small_square.png b/plugins/emoji/public/images/black_medium_small_square.png deleted file mode 120000 index bda52623de..0000000000 --- a/plugins/emoji/public/images/black_medium_small_square.png +++ /dev/null @@ -1 +0,0 @@ -unicode/25fe.png \ No newline at end of file diff --git a/plugins/emoji/public/images/black_medium_square.png b/plugins/emoji/public/images/black_medium_square.png deleted file mode 120000 index a33419a644..0000000000 --- a/plugins/emoji/public/images/black_medium_square.png +++ /dev/null @@ -1 +0,0 @@ -unicode/25fc.png \ No newline at end of file diff --git a/plugins/emoji/public/images/black_nib.png b/plugins/emoji/public/images/black_nib.png deleted file mode 120000 index 7f1b374781..0000000000 --- a/plugins/emoji/public/images/black_nib.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2712.png \ No newline at end of file diff --git a/plugins/emoji/public/images/black_small_square.png b/plugins/emoji/public/images/black_small_square.png deleted file mode 120000 index fb7e6b2755..0000000000 --- a/plugins/emoji/public/images/black_small_square.png +++ /dev/null @@ -1 +0,0 @@ -unicode/25aa.png \ No newline at end of file diff --git a/plugins/emoji/public/images/black_square_button.png b/plugins/emoji/public/images/black_square_button.png deleted file mode 120000 index 025e098abc..0000000000 --- a/plugins/emoji/public/images/black_square_button.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f532.png \ No newline at end of file diff --git a/plugins/emoji/public/images/blossom.png b/plugins/emoji/public/images/blossom.png deleted file mode 120000 index f96cde049b..0000000000 --- a/plugins/emoji/public/images/blossom.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f33c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/blowfish.png b/plugins/emoji/public/images/blowfish.png deleted file mode 120000 index a346e5d1e3..0000000000 --- a/plugins/emoji/public/images/blowfish.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f421.png \ No newline at end of file diff --git a/plugins/emoji/public/images/blue_book.png b/plugins/emoji/public/images/blue_book.png deleted file mode 120000 index 26b023c7a9..0000000000 --- a/plugins/emoji/public/images/blue_book.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4d8.png \ No newline at end of file diff --git a/plugins/emoji/public/images/blue_car.png b/plugins/emoji/public/images/blue_car.png deleted file mode 120000 index 013ad21b8d..0000000000 --- a/plugins/emoji/public/images/blue_car.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f699.png \ No newline at end of file diff --git a/plugins/emoji/public/images/blue_heart.png b/plugins/emoji/public/images/blue_heart.png deleted file mode 120000 index 8e50cba222..0000000000 --- a/plugins/emoji/public/images/blue_heart.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f499.png \ No newline at end of file diff --git a/plugins/emoji/public/images/blush.png b/plugins/emoji/public/images/blush.png deleted file mode 120000 index 7b2737082c..0000000000 --- a/plugins/emoji/public/images/blush.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f60a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/boar.png b/plugins/emoji/public/images/boar.png deleted file mode 120000 index 45605fa493..0000000000 --- a/plugins/emoji/public/images/boar.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f417.png \ No newline at end of file diff --git a/plugins/emoji/public/images/boat.png b/plugins/emoji/public/images/boat.png deleted file mode 120000 index e134ebd6c2..0000000000 --- a/plugins/emoji/public/images/boat.png +++ /dev/null @@ -1 +0,0 @@ -unicode/26f5.png \ No newline at end of file diff --git a/plugins/emoji/public/images/bomb.png b/plugins/emoji/public/images/bomb.png deleted file mode 120000 index 6535f3b54d..0000000000 --- a/plugins/emoji/public/images/bomb.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4a3.png \ No newline at end of file diff --git a/plugins/emoji/public/images/book.png b/plugins/emoji/public/images/book.png deleted file mode 120000 index c95e97943c..0000000000 --- a/plugins/emoji/public/images/book.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4d6.png \ No newline at end of file diff --git a/plugins/emoji/public/images/bookmark.png b/plugins/emoji/public/images/bookmark.png deleted file mode 120000 index ed7288f4f2..0000000000 --- a/plugins/emoji/public/images/bookmark.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f516.png \ No newline at end of file diff --git a/plugins/emoji/public/images/bookmark_tabs.png b/plugins/emoji/public/images/bookmark_tabs.png deleted file mode 120000 index 9dbad631b4..0000000000 --- a/plugins/emoji/public/images/bookmark_tabs.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4d1.png \ No newline at end of file diff --git a/plugins/emoji/public/images/books.png b/plugins/emoji/public/images/books.png deleted file mode 120000 index d023c04422..0000000000 --- a/plugins/emoji/public/images/books.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4da.png \ No newline at end of file diff --git a/plugins/emoji/public/images/boom.png b/plugins/emoji/public/images/boom.png deleted file mode 120000 index ac298e5b3a..0000000000 --- a/plugins/emoji/public/images/boom.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4a5.png \ No newline at end of file diff --git a/plugins/emoji/public/images/boot.png b/plugins/emoji/public/images/boot.png deleted file mode 120000 index c14905a0a9..0000000000 --- a/plugins/emoji/public/images/boot.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f462.png \ No newline at end of file diff --git a/plugins/emoji/public/images/bouquet.png b/plugins/emoji/public/images/bouquet.png deleted file mode 120000 index 2410596498..0000000000 --- a/plugins/emoji/public/images/bouquet.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f490.png \ No newline at end of file diff --git a/plugins/emoji/public/images/bow.png b/plugins/emoji/public/images/bow.png deleted file mode 120000 index 6a866fedad..0000000000 --- a/plugins/emoji/public/images/bow.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f647.png \ No newline at end of file diff --git a/plugins/emoji/public/images/bowling.png b/plugins/emoji/public/images/bowling.png deleted file mode 120000 index 1d1b3f320e..0000000000 --- a/plugins/emoji/public/images/bowling.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3b3.png \ No newline at end of file diff --git a/plugins/emoji/public/images/bowtie.png b/plugins/emoji/public/images/bowtie.png deleted file mode 100755 index 28ff0c787d..0000000000 Binary files a/plugins/emoji/public/images/bowtie.png and /dev/null differ diff --git a/plugins/emoji/public/images/boy.png b/plugins/emoji/public/images/boy.png deleted file mode 120000 index 9594fe6e2c..0000000000 --- a/plugins/emoji/public/images/boy.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f466.png \ No newline at end of file diff --git a/plugins/emoji/public/images/bread.png b/plugins/emoji/public/images/bread.png deleted file mode 120000 index 5a0410d697..0000000000 --- a/plugins/emoji/public/images/bread.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f35e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/bride_with_veil.png b/plugins/emoji/public/images/bride_with_veil.png deleted file mode 120000 index 92f4fb37a7..0000000000 --- a/plugins/emoji/public/images/bride_with_veil.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f470.png \ No newline at end of file diff --git a/plugins/emoji/public/images/bridge_at_night.png b/plugins/emoji/public/images/bridge_at_night.png deleted file mode 120000 index fb57a388b1..0000000000 --- a/plugins/emoji/public/images/bridge_at_night.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f309.png \ No newline at end of file diff --git a/plugins/emoji/public/images/briefcase.png b/plugins/emoji/public/images/briefcase.png deleted file mode 120000 index e8007a5a6e..0000000000 --- a/plugins/emoji/public/images/briefcase.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4bc.png \ No newline at end of file diff --git a/plugins/emoji/public/images/broken_heart.png b/plugins/emoji/public/images/broken_heart.png deleted file mode 120000 index 518b1ed883..0000000000 --- a/plugins/emoji/public/images/broken_heart.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f494.png \ No newline at end of file diff --git a/plugins/emoji/public/images/bug.png b/plugins/emoji/public/images/bug.png deleted file mode 120000 index b95dc8634b..0000000000 --- a/plugins/emoji/public/images/bug.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f41b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/bulb.png b/plugins/emoji/public/images/bulb.png deleted file mode 120000 index 27a2776a85..0000000000 --- a/plugins/emoji/public/images/bulb.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4a1.png \ No newline at end of file diff --git a/plugins/emoji/public/images/bullettrain_front.png b/plugins/emoji/public/images/bullettrain_front.png deleted file mode 120000 index 76e8377162..0000000000 --- a/plugins/emoji/public/images/bullettrain_front.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f685.png \ No newline at end of file diff --git a/plugins/emoji/public/images/bullettrain_side.png b/plugins/emoji/public/images/bullettrain_side.png deleted file mode 120000 index 137e01f202..0000000000 --- a/plugins/emoji/public/images/bullettrain_side.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f684.png \ No newline at end of file diff --git a/plugins/emoji/public/images/bus.png b/plugins/emoji/public/images/bus.png deleted file mode 120000 index 72eb3b16f9..0000000000 --- a/plugins/emoji/public/images/bus.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f68c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/busstop.png b/plugins/emoji/public/images/busstop.png deleted file mode 120000 index 3804faaa77..0000000000 --- a/plugins/emoji/public/images/busstop.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f68f.png \ No newline at end of file diff --git a/plugins/emoji/public/images/bust_in_silhouette.png b/plugins/emoji/public/images/bust_in_silhouette.png deleted file mode 120000 index 9d9bf34cd9..0000000000 --- a/plugins/emoji/public/images/bust_in_silhouette.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f464.png \ No newline at end of file diff --git a/plugins/emoji/public/images/busts_in_silhouette.png b/plugins/emoji/public/images/busts_in_silhouette.png deleted file mode 120000 index b36b340fe0..0000000000 --- a/plugins/emoji/public/images/busts_in_silhouette.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f465.png \ No newline at end of file diff --git a/plugins/emoji/public/images/cactus.png b/plugins/emoji/public/images/cactus.png deleted file mode 120000 index 97cf52b0ec..0000000000 --- a/plugins/emoji/public/images/cactus.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f335.png \ No newline at end of file diff --git a/plugins/emoji/public/images/cake.png b/plugins/emoji/public/images/cake.png deleted file mode 120000 index 88b7b6c20c..0000000000 --- a/plugins/emoji/public/images/cake.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f370.png \ No newline at end of file diff --git a/plugins/emoji/public/images/calendar.png b/plugins/emoji/public/images/calendar.png deleted file mode 120000 index fa75c5cd71..0000000000 --- a/plugins/emoji/public/images/calendar.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4c6.png \ No newline at end of file diff --git a/plugins/emoji/public/images/calling.png b/plugins/emoji/public/images/calling.png deleted file mode 120000 index 2db7f57407..0000000000 --- a/plugins/emoji/public/images/calling.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4f2.png \ No newline at end of file diff --git a/plugins/emoji/public/images/camel.png b/plugins/emoji/public/images/camel.png deleted file mode 120000 index 6a908a3fb9..0000000000 --- a/plugins/emoji/public/images/camel.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f42b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/camera.png b/plugins/emoji/public/images/camera.png deleted file mode 120000 index 722dfbb179..0000000000 --- a/plugins/emoji/public/images/camera.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4f7.png \ No newline at end of file diff --git a/plugins/emoji/public/images/cancer.png b/plugins/emoji/public/images/cancer.png deleted file mode 120000 index 1c028f4c70..0000000000 --- a/plugins/emoji/public/images/cancer.png +++ /dev/null @@ -1 +0,0 @@ -unicode/264b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/candy.png b/plugins/emoji/public/images/candy.png deleted file mode 120000 index d2db024ebb..0000000000 --- a/plugins/emoji/public/images/candy.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f36c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/capital_abcd.png b/plugins/emoji/public/images/capital_abcd.png deleted file mode 120000 index 2df445b130..0000000000 --- a/plugins/emoji/public/images/capital_abcd.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f520.png \ No newline at end of file diff --git a/plugins/emoji/public/images/capricorn.png b/plugins/emoji/public/images/capricorn.png deleted file mode 120000 index b90edabd64..0000000000 --- a/plugins/emoji/public/images/capricorn.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2651.png \ No newline at end of file diff --git a/plugins/emoji/public/images/car.png b/plugins/emoji/public/images/car.png deleted file mode 120000 index 5c765c0c78..0000000000 --- a/plugins/emoji/public/images/car.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f697.png \ No newline at end of file diff --git a/plugins/emoji/public/images/card_index.png b/plugins/emoji/public/images/card_index.png deleted file mode 120000 index ed12eadefa..0000000000 --- a/plugins/emoji/public/images/card_index.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4c7.png \ No newline at end of file diff --git a/plugins/emoji/public/images/carousel_horse.png b/plugins/emoji/public/images/carousel_horse.png deleted file mode 120000 index 714231536b..0000000000 --- a/plugins/emoji/public/images/carousel_horse.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3a0.png \ No newline at end of file diff --git a/plugins/emoji/public/images/cat.png b/plugins/emoji/public/images/cat.png deleted file mode 120000 index 700563ce09..0000000000 --- a/plugins/emoji/public/images/cat.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f431.png \ No newline at end of file diff --git a/plugins/emoji/public/images/cat2.png b/plugins/emoji/public/images/cat2.png deleted file mode 120000 index 5b63192d41..0000000000 --- a/plugins/emoji/public/images/cat2.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f408.png \ No newline at end of file diff --git a/plugins/emoji/public/images/cd.png b/plugins/emoji/public/images/cd.png deleted file mode 120000 index 5dd4bc3ec4..0000000000 --- a/plugins/emoji/public/images/cd.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4bf.png \ No newline at end of file diff --git a/plugins/emoji/public/images/chart.png b/plugins/emoji/public/images/chart.png deleted file mode 120000 index 72658a574d..0000000000 --- a/plugins/emoji/public/images/chart.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4b9.png \ No newline at end of file diff --git a/plugins/emoji/public/images/chart_with_downwards_trend.png b/plugins/emoji/public/images/chart_with_downwards_trend.png deleted file mode 120000 index ed6490a910..0000000000 --- a/plugins/emoji/public/images/chart_with_downwards_trend.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4c9.png \ No newline at end of file diff --git a/plugins/emoji/public/images/chart_with_upwards_trend.png b/plugins/emoji/public/images/chart_with_upwards_trend.png deleted file mode 120000 index e50ad380b9..0000000000 --- a/plugins/emoji/public/images/chart_with_upwards_trend.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4c8.png \ No newline at end of file diff --git a/plugins/emoji/public/images/checkered_flag.png b/plugins/emoji/public/images/checkered_flag.png deleted file mode 120000 index 6c229117d9..0000000000 --- a/plugins/emoji/public/images/checkered_flag.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3c1.png \ No newline at end of file diff --git a/plugins/emoji/public/images/cherries.png b/plugins/emoji/public/images/cherries.png deleted file mode 120000 index 4c3c2fc43f..0000000000 --- a/plugins/emoji/public/images/cherries.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f352.png \ No newline at end of file diff --git a/plugins/emoji/public/images/cherry_blossom.png b/plugins/emoji/public/images/cherry_blossom.png deleted file mode 120000 index d008f09b5c..0000000000 --- a/plugins/emoji/public/images/cherry_blossom.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f338.png \ No newline at end of file diff --git a/plugins/emoji/public/images/chestnut.png b/plugins/emoji/public/images/chestnut.png deleted file mode 120000 index 4d6ca48afa..0000000000 --- a/plugins/emoji/public/images/chestnut.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f330.png \ No newline at end of file diff --git a/plugins/emoji/public/images/chicken.png b/plugins/emoji/public/images/chicken.png deleted file mode 120000 index 20eb3ed95a..0000000000 --- a/plugins/emoji/public/images/chicken.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f414.png \ No newline at end of file diff --git a/plugins/emoji/public/images/children_crossing.png b/plugins/emoji/public/images/children_crossing.png deleted file mode 120000 index f91803a5d8..0000000000 --- a/plugins/emoji/public/images/children_crossing.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6b8.png \ No newline at end of file diff --git a/plugins/emoji/public/images/chocolate_bar.png b/plugins/emoji/public/images/chocolate_bar.png deleted file mode 120000 index fc35f40173..0000000000 --- a/plugins/emoji/public/images/chocolate_bar.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f36b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/christmas_tree.png b/plugins/emoji/public/images/christmas_tree.png deleted file mode 120000 index b283cd9ab0..0000000000 --- a/plugins/emoji/public/images/christmas_tree.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f384.png \ No newline at end of file diff --git a/plugins/emoji/public/images/church.png b/plugins/emoji/public/images/church.png deleted file mode 120000 index b0d9fb862d..0000000000 --- a/plugins/emoji/public/images/church.png +++ /dev/null @@ -1 +0,0 @@ -unicode/26ea.png \ No newline at end of file diff --git a/plugins/emoji/public/images/cinema.png b/plugins/emoji/public/images/cinema.png deleted file mode 120000 index fecd94b360..0000000000 --- a/plugins/emoji/public/images/cinema.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3a6.png \ No newline at end of file diff --git a/plugins/emoji/public/images/circus_tent.png b/plugins/emoji/public/images/circus_tent.png deleted file mode 120000 index c481d05f23..0000000000 --- a/plugins/emoji/public/images/circus_tent.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3aa.png \ No newline at end of file diff --git a/plugins/emoji/public/images/city_sunrise.png b/plugins/emoji/public/images/city_sunrise.png deleted file mode 120000 index bf60493e9a..0000000000 --- a/plugins/emoji/public/images/city_sunrise.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f307.png \ No newline at end of file diff --git a/plugins/emoji/public/images/city_sunset.png b/plugins/emoji/public/images/city_sunset.png deleted file mode 120000 index b9f6acafc2..0000000000 --- a/plugins/emoji/public/images/city_sunset.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f306.png \ No newline at end of file diff --git a/plugins/emoji/public/images/cl.png b/plugins/emoji/public/images/cl.png deleted file mode 120000 index b891eb9494..0000000000 --- a/plugins/emoji/public/images/cl.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f191.png \ No newline at end of file diff --git a/plugins/emoji/public/images/clap.png b/plugins/emoji/public/images/clap.png deleted file mode 120000 index e512723d1e..0000000000 --- a/plugins/emoji/public/images/clap.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f44f.png \ No newline at end of file diff --git a/plugins/emoji/public/images/clapper.png b/plugins/emoji/public/images/clapper.png deleted file mode 120000 index aa80aa0d99..0000000000 --- a/plugins/emoji/public/images/clapper.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3ac.png \ No newline at end of file diff --git a/plugins/emoji/public/images/clipboard.png b/plugins/emoji/public/images/clipboard.png deleted file mode 120000 index bed675bb06..0000000000 --- a/plugins/emoji/public/images/clipboard.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4cb.png \ No newline at end of file diff --git a/plugins/emoji/public/images/clock1.png b/plugins/emoji/public/images/clock1.png deleted file mode 120000 index babfece25d..0000000000 --- a/plugins/emoji/public/images/clock1.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f550.png \ No newline at end of file diff --git a/plugins/emoji/public/images/clock10.png b/plugins/emoji/public/images/clock10.png deleted file mode 120000 index 3b4611b0b0..0000000000 --- a/plugins/emoji/public/images/clock10.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f559.png \ No newline at end of file diff --git a/plugins/emoji/public/images/clock1030.png b/plugins/emoji/public/images/clock1030.png deleted file mode 120000 index 832d8ede02..0000000000 --- a/plugins/emoji/public/images/clock1030.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f565.png \ No newline at end of file diff --git a/plugins/emoji/public/images/clock11.png b/plugins/emoji/public/images/clock11.png deleted file mode 120000 index 00d230a4b6..0000000000 --- a/plugins/emoji/public/images/clock11.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f55a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/clock1130.png b/plugins/emoji/public/images/clock1130.png deleted file mode 120000 index 7bd6ac61f1..0000000000 --- a/plugins/emoji/public/images/clock1130.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f566.png \ No newline at end of file diff --git a/plugins/emoji/public/images/clock12.png b/plugins/emoji/public/images/clock12.png deleted file mode 120000 index b578a975e1..0000000000 --- a/plugins/emoji/public/images/clock12.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f55b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/clock1230.png b/plugins/emoji/public/images/clock1230.png deleted file mode 120000 index b8b0dd7a7a..0000000000 --- a/plugins/emoji/public/images/clock1230.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f567.png \ No newline at end of file diff --git a/plugins/emoji/public/images/clock130.png b/plugins/emoji/public/images/clock130.png deleted file mode 120000 index d58c12b71a..0000000000 --- a/plugins/emoji/public/images/clock130.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f55c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/clock2.png b/plugins/emoji/public/images/clock2.png deleted file mode 120000 index 6865c87a34..0000000000 --- a/plugins/emoji/public/images/clock2.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f551.png \ No newline at end of file diff --git a/plugins/emoji/public/images/clock230.png b/plugins/emoji/public/images/clock230.png deleted file mode 120000 index c4bf1af15c..0000000000 --- a/plugins/emoji/public/images/clock230.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f55d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/clock3.png b/plugins/emoji/public/images/clock3.png deleted file mode 120000 index 9295a8d344..0000000000 --- a/plugins/emoji/public/images/clock3.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f552.png \ No newline at end of file diff --git a/plugins/emoji/public/images/clock330.png b/plugins/emoji/public/images/clock330.png deleted file mode 120000 index 68c67d03f9..0000000000 --- a/plugins/emoji/public/images/clock330.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f55e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/clock4.png b/plugins/emoji/public/images/clock4.png deleted file mode 120000 index 94e718f2b1..0000000000 --- a/plugins/emoji/public/images/clock4.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f553.png \ No newline at end of file diff --git a/plugins/emoji/public/images/clock430.png b/plugins/emoji/public/images/clock430.png deleted file mode 120000 index 725b1327a8..0000000000 --- a/plugins/emoji/public/images/clock430.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f55f.png \ No newline at end of file diff --git a/plugins/emoji/public/images/clock5.png b/plugins/emoji/public/images/clock5.png deleted file mode 120000 index fadf05b54a..0000000000 --- a/plugins/emoji/public/images/clock5.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f554.png \ No newline at end of file diff --git a/plugins/emoji/public/images/clock530.png b/plugins/emoji/public/images/clock530.png deleted file mode 120000 index 59f0f4077f..0000000000 --- a/plugins/emoji/public/images/clock530.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f560.png \ No newline at end of file diff --git a/plugins/emoji/public/images/clock6.png b/plugins/emoji/public/images/clock6.png deleted file mode 120000 index 3daab67e93..0000000000 --- a/plugins/emoji/public/images/clock6.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f555.png \ No newline at end of file diff --git a/plugins/emoji/public/images/clock630.png b/plugins/emoji/public/images/clock630.png deleted file mode 120000 index b452a61526..0000000000 --- a/plugins/emoji/public/images/clock630.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f561.png \ No newline at end of file diff --git a/plugins/emoji/public/images/clock7.png b/plugins/emoji/public/images/clock7.png deleted file mode 120000 index a248b84d8a..0000000000 --- a/plugins/emoji/public/images/clock7.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f556.png \ No newline at end of file diff --git a/plugins/emoji/public/images/clock730.png b/plugins/emoji/public/images/clock730.png deleted file mode 120000 index fc8551a853..0000000000 --- a/plugins/emoji/public/images/clock730.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f562.png \ No newline at end of file diff --git a/plugins/emoji/public/images/clock8.png b/plugins/emoji/public/images/clock8.png deleted file mode 120000 index 0238886ce7..0000000000 --- a/plugins/emoji/public/images/clock8.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f557.png \ No newline at end of file diff --git a/plugins/emoji/public/images/clock830.png b/plugins/emoji/public/images/clock830.png deleted file mode 120000 index 3ebfd1a8fe..0000000000 --- a/plugins/emoji/public/images/clock830.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f563.png \ No newline at end of file diff --git a/plugins/emoji/public/images/clock9.png b/plugins/emoji/public/images/clock9.png deleted file mode 120000 index 0dfa6a0820..0000000000 --- a/plugins/emoji/public/images/clock9.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f558.png \ No newline at end of file diff --git a/plugins/emoji/public/images/clock930.png b/plugins/emoji/public/images/clock930.png deleted file mode 120000 index 7071800dd4..0000000000 --- a/plugins/emoji/public/images/clock930.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f564.png \ No newline at end of file diff --git a/plugins/emoji/public/images/closed_book.png b/plugins/emoji/public/images/closed_book.png deleted file mode 120000 index a2d7786f7b..0000000000 --- a/plugins/emoji/public/images/closed_book.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4d5.png \ No newline at end of file diff --git a/plugins/emoji/public/images/closed_lock_with_key.png b/plugins/emoji/public/images/closed_lock_with_key.png deleted file mode 120000 index 49be232101..0000000000 --- a/plugins/emoji/public/images/closed_lock_with_key.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f510.png \ No newline at end of file diff --git a/plugins/emoji/public/images/closed_umbrella.png b/plugins/emoji/public/images/closed_umbrella.png deleted file mode 120000 index fce0465945..0000000000 --- a/plugins/emoji/public/images/closed_umbrella.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f302.png \ No newline at end of file diff --git a/plugins/emoji/public/images/cloud.png b/plugins/emoji/public/images/cloud.png deleted file mode 120000 index 6352e5410a..0000000000 --- a/plugins/emoji/public/images/cloud.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2601.png \ No newline at end of file diff --git a/plugins/emoji/public/images/clubs.png b/plugins/emoji/public/images/clubs.png deleted file mode 120000 index b53d3ad262..0000000000 --- a/plugins/emoji/public/images/clubs.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2663.png \ No newline at end of file diff --git a/plugins/emoji/public/images/cn.png b/plugins/emoji/public/images/cn.png deleted file mode 120000 index 13dae9fb07..0000000000 --- a/plugins/emoji/public/images/cn.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f1e8-1f1f3.png \ No newline at end of file diff --git a/plugins/emoji/public/images/cocktail.png b/plugins/emoji/public/images/cocktail.png deleted file mode 120000 index c104cec5c0..0000000000 --- a/plugins/emoji/public/images/cocktail.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f378.png \ No newline at end of file diff --git a/plugins/emoji/public/images/coffee.png b/plugins/emoji/public/images/coffee.png deleted file mode 120000 index 9e0eb9eae8..0000000000 --- a/plugins/emoji/public/images/coffee.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2615.png \ No newline at end of file diff --git a/plugins/emoji/public/images/cold_sweat.png b/plugins/emoji/public/images/cold_sweat.png deleted file mode 120000 index 9e55bf1e8a..0000000000 --- a/plugins/emoji/public/images/cold_sweat.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f630.png \ No newline at end of file diff --git a/plugins/emoji/public/images/collision.png b/plugins/emoji/public/images/collision.png deleted file mode 120000 index ac298e5b3a..0000000000 --- a/plugins/emoji/public/images/collision.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4a5.png \ No newline at end of file diff --git a/plugins/emoji/public/images/computer.png b/plugins/emoji/public/images/computer.png deleted file mode 120000 index ba794f3d12..0000000000 --- a/plugins/emoji/public/images/computer.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4bb.png \ No newline at end of file diff --git a/plugins/emoji/public/images/confetti_ball.png b/plugins/emoji/public/images/confetti_ball.png deleted file mode 120000 index 4e45ed7552..0000000000 --- a/plugins/emoji/public/images/confetti_ball.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f38a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/confounded.png b/plugins/emoji/public/images/confounded.png deleted file mode 120000 index 1c2a66fc27..0000000000 --- a/plugins/emoji/public/images/confounded.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f616.png \ No newline at end of file diff --git a/plugins/emoji/public/images/confused.png b/plugins/emoji/public/images/confused.png deleted file mode 120000 index fba2ddb94e..0000000000 --- a/plugins/emoji/public/images/confused.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f615.png \ No newline at end of file diff --git a/plugins/emoji/public/images/congratulations.png b/plugins/emoji/public/images/congratulations.png deleted file mode 120000 index 08cd271abc..0000000000 --- a/plugins/emoji/public/images/congratulations.png +++ /dev/null @@ -1 +0,0 @@ -unicode/3297.png \ No newline at end of file diff --git a/plugins/emoji/public/images/construction.png b/plugins/emoji/public/images/construction.png deleted file mode 120000 index 46ccad2b06..0000000000 --- a/plugins/emoji/public/images/construction.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6a7.png \ No newline at end of file diff --git a/plugins/emoji/public/images/construction_worker.png b/plugins/emoji/public/images/construction_worker.png deleted file mode 120000 index d707a0bd43..0000000000 --- a/plugins/emoji/public/images/construction_worker.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f477.png \ No newline at end of file diff --git a/plugins/emoji/public/images/convenience_store.png b/plugins/emoji/public/images/convenience_store.png deleted file mode 120000 index aa75ece8af..0000000000 --- a/plugins/emoji/public/images/convenience_store.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3ea.png \ No newline at end of file diff --git a/plugins/emoji/public/images/cookie.png b/plugins/emoji/public/images/cookie.png deleted file mode 120000 index e26336948b..0000000000 --- a/plugins/emoji/public/images/cookie.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f36a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/cool.png b/plugins/emoji/public/images/cool.png deleted file mode 120000 index 4ba3e49b3e..0000000000 --- a/plugins/emoji/public/images/cool.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f192.png \ No newline at end of file diff --git a/plugins/emoji/public/images/cop.png b/plugins/emoji/public/images/cop.png deleted file mode 120000 index bb0aa2e2aa..0000000000 --- a/plugins/emoji/public/images/cop.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f46e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/copyright.png b/plugins/emoji/public/images/copyright.png deleted file mode 120000 index c3ad410edf..0000000000 --- a/plugins/emoji/public/images/copyright.png +++ /dev/null @@ -1 +0,0 @@ -unicode/00a9.png \ No newline at end of file diff --git a/plugins/emoji/public/images/corn.png b/plugins/emoji/public/images/corn.png deleted file mode 120000 index 9cb7d3ef73..0000000000 --- a/plugins/emoji/public/images/corn.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f33d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/couple.png b/plugins/emoji/public/images/couple.png deleted file mode 120000 index d4e2873018..0000000000 --- a/plugins/emoji/public/images/couple.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f46b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/couple_with_heart.png b/plugins/emoji/public/images/couple_with_heart.png deleted file mode 120000 index 3354817f24..0000000000 --- a/plugins/emoji/public/images/couple_with_heart.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f491.png \ No newline at end of file diff --git a/plugins/emoji/public/images/couplekiss.png b/plugins/emoji/public/images/couplekiss.png deleted file mode 120000 index 7c89b32f9c..0000000000 --- a/plugins/emoji/public/images/couplekiss.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f48f.png \ No newline at end of file diff --git a/plugins/emoji/public/images/cow.png b/plugins/emoji/public/images/cow.png deleted file mode 120000 index 78dd69826f..0000000000 --- a/plugins/emoji/public/images/cow.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f42e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/cow2.png b/plugins/emoji/public/images/cow2.png deleted file mode 120000 index 66e42f9db3..0000000000 --- a/plugins/emoji/public/images/cow2.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f404.png \ No newline at end of file diff --git a/plugins/emoji/public/images/credit_card.png b/plugins/emoji/public/images/credit_card.png deleted file mode 120000 index f1b2040ff8..0000000000 --- a/plugins/emoji/public/images/credit_card.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4b3.png \ No newline at end of file diff --git a/plugins/emoji/public/images/crescent_moon.png b/plugins/emoji/public/images/crescent_moon.png deleted file mode 120000 index c24506ae03..0000000000 --- a/plugins/emoji/public/images/crescent_moon.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f319.png \ No newline at end of file diff --git a/plugins/emoji/public/images/crocodile.png b/plugins/emoji/public/images/crocodile.png deleted file mode 120000 index 74edced8a3..0000000000 --- a/plugins/emoji/public/images/crocodile.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f40a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/crossed_flags.png b/plugins/emoji/public/images/crossed_flags.png deleted file mode 120000 index c8355a7601..0000000000 --- a/plugins/emoji/public/images/crossed_flags.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f38c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/crown.png b/plugins/emoji/public/images/crown.png deleted file mode 120000 index 9c22c50003..0000000000 --- a/plugins/emoji/public/images/crown.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f451.png \ No newline at end of file diff --git a/plugins/emoji/public/images/cry.png b/plugins/emoji/public/images/cry.png deleted file mode 120000 index 6026054822..0000000000 --- a/plugins/emoji/public/images/cry.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f622.png \ No newline at end of file diff --git a/plugins/emoji/public/images/crying_cat_face.png b/plugins/emoji/public/images/crying_cat_face.png deleted file mode 120000 index 59cc6d46bc..0000000000 --- a/plugins/emoji/public/images/crying_cat_face.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f63f.png \ No newline at end of file diff --git a/plugins/emoji/public/images/crystal_ball.png b/plugins/emoji/public/images/crystal_ball.png deleted file mode 120000 index db7f0298c2..0000000000 --- a/plugins/emoji/public/images/crystal_ball.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f52e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/cupid.png b/plugins/emoji/public/images/cupid.png deleted file mode 120000 index 21fd46a7f8..0000000000 --- a/plugins/emoji/public/images/cupid.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f498.png \ No newline at end of file diff --git a/plugins/emoji/public/images/curly_loop.png b/plugins/emoji/public/images/curly_loop.png deleted file mode 120000 index edd47cb2c8..0000000000 --- a/plugins/emoji/public/images/curly_loop.png +++ /dev/null @@ -1 +0,0 @@ -unicode/27b0.png \ No newline at end of file diff --git a/plugins/emoji/public/images/currency_exchange.png b/plugins/emoji/public/images/currency_exchange.png deleted file mode 120000 index f169d71764..0000000000 --- a/plugins/emoji/public/images/currency_exchange.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4b1.png \ No newline at end of file diff --git a/plugins/emoji/public/images/curry.png b/plugins/emoji/public/images/curry.png deleted file mode 120000 index 4302387834..0000000000 --- a/plugins/emoji/public/images/curry.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f35b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/custard.png b/plugins/emoji/public/images/custard.png deleted file mode 120000 index 25b31e9c12..0000000000 --- a/plugins/emoji/public/images/custard.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f36e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/customs.png b/plugins/emoji/public/images/customs.png deleted file mode 120000 index 486f2789ca..0000000000 --- a/plugins/emoji/public/images/customs.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6c3.png \ No newline at end of file diff --git a/plugins/emoji/public/images/cyclone.png b/plugins/emoji/public/images/cyclone.png deleted file mode 120000 index 4af8f622fb..0000000000 --- a/plugins/emoji/public/images/cyclone.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f300.png \ No newline at end of file diff --git a/plugins/emoji/public/images/dancer.png b/plugins/emoji/public/images/dancer.png deleted file mode 120000 index 2ef414f8ca..0000000000 --- a/plugins/emoji/public/images/dancer.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f483.png \ No newline at end of file diff --git a/plugins/emoji/public/images/dancers.png b/plugins/emoji/public/images/dancers.png deleted file mode 120000 index 2d9f681706..0000000000 --- a/plugins/emoji/public/images/dancers.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f46f.png \ No newline at end of file diff --git a/plugins/emoji/public/images/dango.png b/plugins/emoji/public/images/dango.png deleted file mode 120000 index ebdf73e659..0000000000 --- a/plugins/emoji/public/images/dango.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f361.png \ No newline at end of file diff --git a/plugins/emoji/public/images/dart.png b/plugins/emoji/public/images/dart.png deleted file mode 120000 index fc947a9448..0000000000 --- a/plugins/emoji/public/images/dart.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3af.png \ No newline at end of file diff --git a/plugins/emoji/public/images/dash.png b/plugins/emoji/public/images/dash.png deleted file mode 120000 index e78b67a95b..0000000000 --- a/plugins/emoji/public/images/dash.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4a8.png \ No newline at end of file diff --git a/plugins/emoji/public/images/date.png b/plugins/emoji/public/images/date.png deleted file mode 120000 index 9b2460cf23..0000000000 --- a/plugins/emoji/public/images/date.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4c5.png \ No newline at end of file diff --git a/plugins/emoji/public/images/de.png b/plugins/emoji/public/images/de.png deleted file mode 120000 index aa7924088e..0000000000 --- a/plugins/emoji/public/images/de.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f1e9-1f1ea.png \ No newline at end of file diff --git a/plugins/emoji/public/images/deciduous_tree.png b/plugins/emoji/public/images/deciduous_tree.png deleted file mode 120000 index f84c1cd43a..0000000000 --- a/plugins/emoji/public/images/deciduous_tree.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f333.png \ No newline at end of file diff --git a/plugins/emoji/public/images/department_store.png b/plugins/emoji/public/images/department_store.png deleted file mode 120000 index bd1a150cec..0000000000 --- a/plugins/emoji/public/images/department_store.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3ec.png \ No newline at end of file diff --git a/plugins/emoji/public/images/diamond_shape_with_a_dot_inside.png b/plugins/emoji/public/images/diamond_shape_with_a_dot_inside.png deleted file mode 120000 index 9ca70fc936..0000000000 --- a/plugins/emoji/public/images/diamond_shape_with_a_dot_inside.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4a0.png \ No newline at end of file diff --git a/plugins/emoji/public/images/diamonds.png b/plugins/emoji/public/images/diamonds.png deleted file mode 120000 index 1475b75e20..0000000000 --- a/plugins/emoji/public/images/diamonds.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2666.png \ No newline at end of file diff --git a/plugins/emoji/public/images/disappointed.png b/plugins/emoji/public/images/disappointed.png deleted file mode 120000 index 32b4024f00..0000000000 --- a/plugins/emoji/public/images/disappointed.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f61e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/disappointed_relieved.png b/plugins/emoji/public/images/disappointed_relieved.png deleted file mode 120000 index 0ada0bda2b..0000000000 --- a/plugins/emoji/public/images/disappointed_relieved.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f625.png \ No newline at end of file diff --git a/plugins/emoji/public/images/dizzy.png b/plugins/emoji/public/images/dizzy.png deleted file mode 120000 index d8f02d9b40..0000000000 --- a/plugins/emoji/public/images/dizzy.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4ab.png \ No newline at end of file diff --git a/plugins/emoji/public/images/dizzy_face.png b/plugins/emoji/public/images/dizzy_face.png deleted file mode 120000 index 6c1725d0e0..0000000000 --- a/plugins/emoji/public/images/dizzy_face.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f635.png \ No newline at end of file diff --git a/plugins/emoji/public/images/do_not_litter.png b/plugins/emoji/public/images/do_not_litter.png deleted file mode 120000 index 2aad4879c4..0000000000 --- a/plugins/emoji/public/images/do_not_litter.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6af.png \ No newline at end of file diff --git a/plugins/emoji/public/images/dog.png b/plugins/emoji/public/images/dog.png deleted file mode 120000 index 1cfc670448..0000000000 --- a/plugins/emoji/public/images/dog.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f436.png \ No newline at end of file diff --git a/plugins/emoji/public/images/dog2.png b/plugins/emoji/public/images/dog2.png deleted file mode 120000 index e447911e29..0000000000 --- a/plugins/emoji/public/images/dog2.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f415.png \ No newline at end of file diff --git a/plugins/emoji/public/images/dollar.png b/plugins/emoji/public/images/dollar.png deleted file mode 120000 index 5f57b39b6d..0000000000 --- a/plugins/emoji/public/images/dollar.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4b5.png \ No newline at end of file diff --git a/plugins/emoji/public/images/dolls.png b/plugins/emoji/public/images/dolls.png deleted file mode 120000 index c5bbe9f771..0000000000 --- a/plugins/emoji/public/images/dolls.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f38e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/dolphin.png b/plugins/emoji/public/images/dolphin.png deleted file mode 120000 index 608d04011e..0000000000 --- a/plugins/emoji/public/images/dolphin.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f42c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/door.png b/plugins/emoji/public/images/door.png deleted file mode 120000 index 2892c68070..0000000000 --- a/plugins/emoji/public/images/door.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6aa.png \ No newline at end of file diff --git a/plugins/emoji/public/images/doughnut.png b/plugins/emoji/public/images/doughnut.png deleted file mode 120000 index 3f4be9b306..0000000000 --- a/plugins/emoji/public/images/doughnut.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f369.png \ No newline at end of file diff --git a/plugins/emoji/public/images/dragon.png b/plugins/emoji/public/images/dragon.png deleted file mode 120000 index 72564c6535..0000000000 --- a/plugins/emoji/public/images/dragon.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f409.png \ No newline at end of file diff --git a/plugins/emoji/public/images/dragon_face.png b/plugins/emoji/public/images/dragon_face.png deleted file mode 120000 index 91a1c3ae08..0000000000 --- a/plugins/emoji/public/images/dragon_face.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f432.png \ No newline at end of file diff --git a/plugins/emoji/public/images/dress.png b/plugins/emoji/public/images/dress.png deleted file mode 120000 index b8c868679b..0000000000 --- a/plugins/emoji/public/images/dress.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f457.png \ No newline at end of file diff --git a/plugins/emoji/public/images/dromedary_camel.png b/plugins/emoji/public/images/dromedary_camel.png deleted file mode 120000 index cb20f59c15..0000000000 --- a/plugins/emoji/public/images/dromedary_camel.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f42a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/droplet.png b/plugins/emoji/public/images/droplet.png deleted file mode 120000 index cfac4591e0..0000000000 --- a/plugins/emoji/public/images/droplet.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4a7.png \ No newline at end of file diff --git a/plugins/emoji/public/images/dvd.png b/plugins/emoji/public/images/dvd.png deleted file mode 120000 index 086e083fe5..0000000000 --- a/plugins/emoji/public/images/dvd.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4c0.png \ No newline at end of file diff --git a/plugins/emoji/public/images/e-mail.png b/plugins/emoji/public/images/e-mail.png deleted file mode 120000 index 06849d1877..0000000000 --- a/plugins/emoji/public/images/e-mail.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4e7.png \ No newline at end of file diff --git a/plugins/emoji/public/images/ear.png b/plugins/emoji/public/images/ear.png deleted file mode 120000 index e1a626bc4e..0000000000 --- a/plugins/emoji/public/images/ear.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f442.png \ No newline at end of file diff --git a/plugins/emoji/public/images/ear_of_rice.png b/plugins/emoji/public/images/ear_of_rice.png deleted file mode 120000 index 1c346905cc..0000000000 --- a/plugins/emoji/public/images/ear_of_rice.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f33e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/earth_africa.png b/plugins/emoji/public/images/earth_africa.png deleted file mode 120000 index f8c52577bc..0000000000 --- a/plugins/emoji/public/images/earth_africa.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f30d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/earth_americas.png b/plugins/emoji/public/images/earth_americas.png deleted file mode 120000 index 6caf59dac5..0000000000 --- a/plugins/emoji/public/images/earth_americas.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f30e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/earth_asia.png b/plugins/emoji/public/images/earth_asia.png deleted file mode 120000 index 2934b85aa6..0000000000 --- a/plugins/emoji/public/images/earth_asia.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f30f.png \ No newline at end of file diff --git a/plugins/emoji/public/images/egg.png b/plugins/emoji/public/images/egg.png deleted file mode 120000 index 9e55c9fded..0000000000 --- a/plugins/emoji/public/images/egg.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f373.png \ No newline at end of file diff --git a/plugins/emoji/public/images/eggplant.png b/plugins/emoji/public/images/eggplant.png deleted file mode 120000 index a3341a9654..0000000000 --- a/plugins/emoji/public/images/eggplant.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f346.png \ No newline at end of file diff --git a/plugins/emoji/public/images/eight.png b/plugins/emoji/public/images/eight.png deleted file mode 120000 index c585323cd3..0000000000 --- a/plugins/emoji/public/images/eight.png +++ /dev/null @@ -1 +0,0 @@ -unicode/0038.png \ No newline at end of file diff --git a/plugins/emoji/public/images/eight_pointed_black_star.png b/plugins/emoji/public/images/eight_pointed_black_star.png deleted file mode 120000 index 99aab6ae0f..0000000000 --- a/plugins/emoji/public/images/eight_pointed_black_star.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2734.png \ No newline at end of file diff --git a/plugins/emoji/public/images/eight_spoked_asterisk.png b/plugins/emoji/public/images/eight_spoked_asterisk.png deleted file mode 120000 index cfcd8f4f41..0000000000 --- a/plugins/emoji/public/images/eight_spoked_asterisk.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2733.png \ No newline at end of file diff --git a/plugins/emoji/public/images/electric_plug.png b/plugins/emoji/public/images/electric_plug.png deleted file mode 120000 index 1f9b2bb055..0000000000 --- a/plugins/emoji/public/images/electric_plug.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f50c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/elephant.png b/plugins/emoji/public/images/elephant.png deleted file mode 120000 index 66566e5459..0000000000 --- a/plugins/emoji/public/images/elephant.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f418.png \ No newline at end of file diff --git a/plugins/emoji/public/images/email.png b/plugins/emoji/public/images/email.png deleted file mode 120000 index d4e66d388c..0000000000 --- a/plugins/emoji/public/images/email.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2709.png \ No newline at end of file diff --git a/plugins/emoji/public/images/end.png b/plugins/emoji/public/images/end.png deleted file mode 120000 index 8350d8c305..0000000000 --- a/plugins/emoji/public/images/end.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f51a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/envelope.png b/plugins/emoji/public/images/envelope.png deleted file mode 120000 index d4e66d388c..0000000000 --- a/plugins/emoji/public/images/envelope.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2709.png \ No newline at end of file diff --git a/plugins/emoji/public/images/envelope_with_arrow.png b/plugins/emoji/public/images/envelope_with_arrow.png deleted file mode 120000 index 71b7adec1e..0000000000 --- a/plugins/emoji/public/images/envelope_with_arrow.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4e9.png \ No newline at end of file diff --git a/plugins/emoji/public/images/es.png b/plugins/emoji/public/images/es.png deleted file mode 120000 index ed803b9a7d..0000000000 --- a/plugins/emoji/public/images/es.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f1ea-1f1f8.png \ No newline at end of file diff --git a/plugins/emoji/public/images/euro.png b/plugins/emoji/public/images/euro.png deleted file mode 120000 index 3645ce124e..0000000000 --- a/plugins/emoji/public/images/euro.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4b6.png \ No newline at end of file diff --git a/plugins/emoji/public/images/european_castle.png b/plugins/emoji/public/images/european_castle.png deleted file mode 120000 index 43eb0dcaee..0000000000 --- a/plugins/emoji/public/images/european_castle.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3f0.png \ No newline at end of file diff --git a/plugins/emoji/public/images/european_post_office.png b/plugins/emoji/public/images/european_post_office.png deleted file mode 120000 index 431839cc9f..0000000000 --- a/plugins/emoji/public/images/european_post_office.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3e4.png \ No newline at end of file diff --git a/plugins/emoji/public/images/evergreen_tree.png b/plugins/emoji/public/images/evergreen_tree.png deleted file mode 120000 index 3484dccf5f..0000000000 --- a/plugins/emoji/public/images/evergreen_tree.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f332.png \ No newline at end of file diff --git a/plugins/emoji/public/images/exclamation.png b/plugins/emoji/public/images/exclamation.png deleted file mode 120000 index f085c97778..0000000000 --- a/plugins/emoji/public/images/exclamation.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2757.png \ No newline at end of file diff --git a/plugins/emoji/public/images/expressionless.png b/plugins/emoji/public/images/expressionless.png deleted file mode 120000 index c93d7e85e1..0000000000 --- a/plugins/emoji/public/images/expressionless.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f611.png \ No newline at end of file diff --git a/plugins/emoji/public/images/eyeglasses.png b/plugins/emoji/public/images/eyeglasses.png deleted file mode 120000 index 8a3815a68b..0000000000 --- a/plugins/emoji/public/images/eyeglasses.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f453.png \ No newline at end of file diff --git a/plugins/emoji/public/images/eyes.png b/plugins/emoji/public/images/eyes.png deleted file mode 120000 index 06a90075d6..0000000000 --- a/plugins/emoji/public/images/eyes.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f440.png \ No newline at end of file diff --git a/plugins/emoji/public/images/facepunch.png b/plugins/emoji/public/images/facepunch.png deleted file mode 120000 index 2423b707f2..0000000000 --- a/plugins/emoji/public/images/facepunch.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f44a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/factory.png b/plugins/emoji/public/images/factory.png deleted file mode 120000 index eb70c299a9..0000000000 --- a/plugins/emoji/public/images/factory.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3ed.png \ No newline at end of file diff --git a/plugins/emoji/public/images/fallen_leaf.png b/plugins/emoji/public/images/fallen_leaf.png deleted file mode 120000 index b1b22b4501..0000000000 --- a/plugins/emoji/public/images/fallen_leaf.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f342.png \ No newline at end of file diff --git a/plugins/emoji/public/images/family.png b/plugins/emoji/public/images/family.png deleted file mode 120000 index 94c3a5a3c7..0000000000 --- a/plugins/emoji/public/images/family.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f46a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/fast_forward.png b/plugins/emoji/public/images/fast_forward.png deleted file mode 120000 index bc32889d09..0000000000 --- a/plugins/emoji/public/images/fast_forward.png +++ /dev/null @@ -1 +0,0 @@ -unicode/23e9.png \ No newline at end of file diff --git a/plugins/emoji/public/images/fax.png b/plugins/emoji/public/images/fax.png deleted file mode 120000 index 08e6af641a..0000000000 --- a/plugins/emoji/public/images/fax.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4e0.png \ No newline at end of file diff --git a/plugins/emoji/public/images/fearful.png b/plugins/emoji/public/images/fearful.png deleted file mode 120000 index 65d3af1dfa..0000000000 --- a/plugins/emoji/public/images/fearful.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f628.png \ No newline at end of file diff --git a/plugins/emoji/public/images/feelsgood.png b/plugins/emoji/public/images/feelsgood.png deleted file mode 100755 index e3e10fd5b2..0000000000 Binary files a/plugins/emoji/public/images/feelsgood.png and /dev/null differ diff --git a/plugins/emoji/public/images/feet.png b/plugins/emoji/public/images/feet.png deleted file mode 120000 index bbb76f06be..0000000000 --- a/plugins/emoji/public/images/feet.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f43e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/ferris_wheel.png b/plugins/emoji/public/images/ferris_wheel.png deleted file mode 120000 index 5ed10d3869..0000000000 --- a/plugins/emoji/public/images/ferris_wheel.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3a1.png \ No newline at end of file diff --git a/plugins/emoji/public/images/file_folder.png b/plugins/emoji/public/images/file_folder.png deleted file mode 120000 index 9c3f43fdae..0000000000 --- a/plugins/emoji/public/images/file_folder.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4c1.png \ No newline at end of file diff --git a/plugins/emoji/public/images/finnadie.png b/plugins/emoji/public/images/finnadie.png deleted file mode 100755 index 0ca5cdd95a..0000000000 Binary files a/plugins/emoji/public/images/finnadie.png and /dev/null differ diff --git a/plugins/emoji/public/images/fire.png b/plugins/emoji/public/images/fire.png deleted file mode 120000 index a8be27656a..0000000000 --- a/plugins/emoji/public/images/fire.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f525.png \ No newline at end of file diff --git a/plugins/emoji/public/images/fire_engine.png b/plugins/emoji/public/images/fire_engine.png deleted file mode 120000 index 63bbeafe2f..0000000000 --- a/plugins/emoji/public/images/fire_engine.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f692.png \ No newline at end of file diff --git a/plugins/emoji/public/images/fireworks.png b/plugins/emoji/public/images/fireworks.png deleted file mode 120000 index faa7194fb2..0000000000 --- a/plugins/emoji/public/images/fireworks.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f386.png \ No newline at end of file diff --git a/plugins/emoji/public/images/first_quarter_moon.png b/plugins/emoji/public/images/first_quarter_moon.png deleted file mode 120000 index 07b1c4d700..0000000000 --- a/plugins/emoji/public/images/first_quarter_moon.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f313.png \ No newline at end of file diff --git a/plugins/emoji/public/images/first_quarter_moon_with_face.png b/plugins/emoji/public/images/first_quarter_moon_with_face.png deleted file mode 120000 index fec7b969fe..0000000000 --- a/plugins/emoji/public/images/first_quarter_moon_with_face.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f31b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/fish.png b/plugins/emoji/public/images/fish.png deleted file mode 120000 index b1ca9cd265..0000000000 --- a/plugins/emoji/public/images/fish.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f41f.png \ No newline at end of file diff --git a/plugins/emoji/public/images/fish_cake.png b/plugins/emoji/public/images/fish_cake.png deleted file mode 120000 index 090d80978a..0000000000 --- a/plugins/emoji/public/images/fish_cake.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f365.png \ No newline at end of file diff --git a/plugins/emoji/public/images/fishing_pole_and_fish.png b/plugins/emoji/public/images/fishing_pole_and_fish.png deleted file mode 120000 index 89abe1b6a1..0000000000 --- a/plugins/emoji/public/images/fishing_pole_and_fish.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3a3.png \ No newline at end of file diff --git a/plugins/emoji/public/images/fist.png b/plugins/emoji/public/images/fist.png deleted file mode 120000 index 0aa21ff40b..0000000000 --- a/plugins/emoji/public/images/fist.png +++ /dev/null @@ -1 +0,0 @@ -unicode/270a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/five.png b/plugins/emoji/public/images/five.png deleted file mode 120000 index e32fe5268e..0000000000 --- a/plugins/emoji/public/images/five.png +++ /dev/null @@ -1 +0,0 @@ -unicode/0035.png \ No newline at end of file diff --git a/plugins/emoji/public/images/flags.png b/plugins/emoji/public/images/flags.png deleted file mode 120000 index 76811b28d7..0000000000 --- a/plugins/emoji/public/images/flags.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f38f.png \ No newline at end of file diff --git a/plugins/emoji/public/images/flashlight.png b/plugins/emoji/public/images/flashlight.png deleted file mode 120000 index 662d68ae21..0000000000 --- a/plugins/emoji/public/images/flashlight.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f526.png \ No newline at end of file diff --git a/plugins/emoji/public/images/flipper.png b/plugins/emoji/public/images/flipper.png deleted file mode 120000 index 608d04011e..0000000000 --- a/plugins/emoji/public/images/flipper.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f42c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/floppy_disk.png b/plugins/emoji/public/images/floppy_disk.png deleted file mode 120000 index 09b4bf4a59..0000000000 --- a/plugins/emoji/public/images/floppy_disk.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4be.png \ No newline at end of file diff --git a/plugins/emoji/public/images/flower_playing_cards.png b/plugins/emoji/public/images/flower_playing_cards.png deleted file mode 120000 index 78ab229c2f..0000000000 --- a/plugins/emoji/public/images/flower_playing_cards.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3b4.png \ No newline at end of file diff --git a/plugins/emoji/public/images/flushed.png b/plugins/emoji/public/images/flushed.png deleted file mode 120000 index 11e596bf6d..0000000000 --- a/plugins/emoji/public/images/flushed.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f633.png \ No newline at end of file diff --git a/plugins/emoji/public/images/foggy.png b/plugins/emoji/public/images/foggy.png deleted file mode 120000 index f0db32efbb..0000000000 --- a/plugins/emoji/public/images/foggy.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f301.png \ No newline at end of file diff --git a/plugins/emoji/public/images/football.png b/plugins/emoji/public/images/football.png deleted file mode 120000 index b6600ec6bc..0000000000 --- a/plugins/emoji/public/images/football.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3c8.png \ No newline at end of file diff --git a/plugins/emoji/public/images/footprints.png b/plugins/emoji/public/images/footprints.png deleted file mode 120000 index de5fe4383f..0000000000 --- a/plugins/emoji/public/images/footprints.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f463.png \ No newline at end of file diff --git a/plugins/emoji/public/images/fork_and_knife.png b/plugins/emoji/public/images/fork_and_knife.png deleted file mode 120000 index 8daf920680..0000000000 --- a/plugins/emoji/public/images/fork_and_knife.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f374.png \ No newline at end of file diff --git a/plugins/emoji/public/images/fountain.png b/plugins/emoji/public/images/fountain.png deleted file mode 120000 index ddadb5a143..0000000000 --- a/plugins/emoji/public/images/fountain.png +++ /dev/null @@ -1 +0,0 @@ -unicode/26f2.png \ No newline at end of file diff --git a/plugins/emoji/public/images/four.png b/plugins/emoji/public/images/four.png deleted file mode 120000 index a5f8f2a279..0000000000 --- a/plugins/emoji/public/images/four.png +++ /dev/null @@ -1 +0,0 @@ -unicode/0034.png \ No newline at end of file diff --git a/plugins/emoji/public/images/four_leaf_clover.png b/plugins/emoji/public/images/four_leaf_clover.png deleted file mode 120000 index 7b517031a3..0000000000 --- a/plugins/emoji/public/images/four_leaf_clover.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f340.png \ No newline at end of file diff --git a/plugins/emoji/public/images/fr.png b/plugins/emoji/public/images/fr.png deleted file mode 120000 index c17d1b9df5..0000000000 --- a/plugins/emoji/public/images/fr.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f1eb-1f1f7.png \ No newline at end of file diff --git a/plugins/emoji/public/images/free.png b/plugins/emoji/public/images/free.png deleted file mode 120000 index 6ee820ecbe..0000000000 --- a/plugins/emoji/public/images/free.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f193.png \ No newline at end of file diff --git a/plugins/emoji/public/images/fried_shrimp.png b/plugins/emoji/public/images/fried_shrimp.png deleted file mode 120000 index 00c55f30db..0000000000 --- a/plugins/emoji/public/images/fried_shrimp.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f364.png \ No newline at end of file diff --git a/plugins/emoji/public/images/fries.png b/plugins/emoji/public/images/fries.png deleted file mode 120000 index 5a5ee22c42..0000000000 --- a/plugins/emoji/public/images/fries.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f35f.png \ No newline at end of file diff --git a/plugins/emoji/public/images/frog.png b/plugins/emoji/public/images/frog.png deleted file mode 120000 index 7b551cf72f..0000000000 --- a/plugins/emoji/public/images/frog.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f438.png \ No newline at end of file diff --git a/plugins/emoji/public/images/frowning.png b/plugins/emoji/public/images/frowning.png deleted file mode 120000 index 030996a720..0000000000 --- a/plugins/emoji/public/images/frowning.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f626.png \ No newline at end of file diff --git a/plugins/emoji/public/images/fu.png b/plugins/emoji/public/images/fu.png deleted file mode 100755 index 195fbc319d..0000000000 Binary files a/plugins/emoji/public/images/fu.png and /dev/null differ diff --git a/plugins/emoji/public/images/fuelpump.png b/plugins/emoji/public/images/fuelpump.png deleted file mode 120000 index 811fd8d15d..0000000000 --- a/plugins/emoji/public/images/fuelpump.png +++ /dev/null @@ -1 +0,0 @@ -unicode/26fd.png \ No newline at end of file diff --git a/plugins/emoji/public/images/full_moon.png b/plugins/emoji/public/images/full_moon.png deleted file mode 120000 index 13b0a191ce..0000000000 --- a/plugins/emoji/public/images/full_moon.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f315.png \ No newline at end of file diff --git a/plugins/emoji/public/images/full_moon_with_face.png b/plugins/emoji/public/images/full_moon_with_face.png deleted file mode 120000 index 37f8fe7f24..0000000000 --- a/plugins/emoji/public/images/full_moon_with_face.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f31d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/game_die.png b/plugins/emoji/public/images/game_die.png deleted file mode 120000 index be998c49f2..0000000000 --- a/plugins/emoji/public/images/game_die.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3b2.png \ No newline at end of file diff --git a/plugins/emoji/public/images/gb.png b/plugins/emoji/public/images/gb.png deleted file mode 120000 index 68cbaff002..0000000000 --- a/plugins/emoji/public/images/gb.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f1ec-1f1e7.png \ No newline at end of file diff --git a/plugins/emoji/public/images/gem.png b/plugins/emoji/public/images/gem.png deleted file mode 120000 index d0e148e102..0000000000 --- a/plugins/emoji/public/images/gem.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f48e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/gemini.png b/plugins/emoji/public/images/gemini.png deleted file mode 120000 index b061a84951..0000000000 --- a/plugins/emoji/public/images/gemini.png +++ /dev/null @@ -1 +0,0 @@ -unicode/264a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/ghost.png b/plugins/emoji/public/images/ghost.png deleted file mode 120000 index 9eb885c599..0000000000 --- a/plugins/emoji/public/images/ghost.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f47b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/gift.png b/plugins/emoji/public/images/gift.png deleted file mode 120000 index e96bbeb4e4..0000000000 --- a/plugins/emoji/public/images/gift.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f381.png \ No newline at end of file diff --git a/plugins/emoji/public/images/gift_heart.png b/plugins/emoji/public/images/gift_heart.png deleted file mode 120000 index fecb0f328d..0000000000 --- a/plugins/emoji/public/images/gift_heart.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f49d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/girl.png b/plugins/emoji/public/images/girl.png deleted file mode 120000 index f364b9bea7..0000000000 --- a/plugins/emoji/public/images/girl.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f467.png \ No newline at end of file diff --git a/plugins/emoji/public/images/globe_with_meridians.png b/plugins/emoji/public/images/globe_with_meridians.png deleted file mode 120000 index ce26e094ba..0000000000 --- a/plugins/emoji/public/images/globe_with_meridians.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f310.png \ No newline at end of file diff --git a/plugins/emoji/public/images/goat.png b/plugins/emoji/public/images/goat.png deleted file mode 120000 index 684cf9f0b3..0000000000 --- a/plugins/emoji/public/images/goat.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f410.png \ No newline at end of file diff --git a/plugins/emoji/public/images/goberserk.png b/plugins/emoji/public/images/goberserk.png deleted file mode 100755 index 59a742aaaa..0000000000 Binary files a/plugins/emoji/public/images/goberserk.png and /dev/null differ diff --git a/plugins/emoji/public/images/godmode.png b/plugins/emoji/public/images/godmode.png deleted file mode 100755 index 7e3313d9b0..0000000000 Binary files a/plugins/emoji/public/images/godmode.png and /dev/null differ diff --git a/plugins/emoji/public/images/golf.png b/plugins/emoji/public/images/golf.png deleted file mode 120000 index cbead94def..0000000000 --- a/plugins/emoji/public/images/golf.png +++ /dev/null @@ -1 +0,0 @@ -unicode/26f3.png \ No newline at end of file diff --git a/plugins/emoji/public/images/grapes.png b/plugins/emoji/public/images/grapes.png deleted file mode 120000 index 992b8bedb5..0000000000 --- a/plugins/emoji/public/images/grapes.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f347.png \ No newline at end of file diff --git a/plugins/emoji/public/images/green_apple.png b/plugins/emoji/public/images/green_apple.png deleted file mode 120000 index 976a754014..0000000000 --- a/plugins/emoji/public/images/green_apple.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f34f.png \ No newline at end of file diff --git a/plugins/emoji/public/images/green_book.png b/plugins/emoji/public/images/green_book.png deleted file mode 120000 index 3dc3c270c6..0000000000 --- a/plugins/emoji/public/images/green_book.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4d7.png \ No newline at end of file diff --git a/plugins/emoji/public/images/green_heart.png b/plugins/emoji/public/images/green_heart.png deleted file mode 120000 index 54420ad54e..0000000000 --- a/plugins/emoji/public/images/green_heart.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f49a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/grey_exclamation.png b/plugins/emoji/public/images/grey_exclamation.png deleted file mode 120000 index 134a452b40..0000000000 --- a/plugins/emoji/public/images/grey_exclamation.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2755.png \ No newline at end of file diff --git a/plugins/emoji/public/images/grey_question.png b/plugins/emoji/public/images/grey_question.png deleted file mode 120000 index 8f0648965e..0000000000 --- a/plugins/emoji/public/images/grey_question.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2754.png \ No newline at end of file diff --git a/plugins/emoji/public/images/grimacing.png b/plugins/emoji/public/images/grimacing.png deleted file mode 120000 index b6635d36f5..0000000000 --- a/plugins/emoji/public/images/grimacing.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f62c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/grin.png b/plugins/emoji/public/images/grin.png deleted file mode 120000 index 143cbebc6d..0000000000 --- a/plugins/emoji/public/images/grin.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f601.png \ No newline at end of file diff --git a/plugins/emoji/public/images/grinning.png b/plugins/emoji/public/images/grinning.png deleted file mode 120000 index a993ffd1d3..0000000000 --- a/plugins/emoji/public/images/grinning.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f600.png \ No newline at end of file diff --git a/plugins/emoji/public/images/guardsman.png b/plugins/emoji/public/images/guardsman.png deleted file mode 120000 index 1dc9e56f5e..0000000000 --- a/plugins/emoji/public/images/guardsman.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f482.png \ No newline at end of file diff --git a/plugins/emoji/public/images/guitar.png b/plugins/emoji/public/images/guitar.png deleted file mode 120000 index b94136ec31..0000000000 --- a/plugins/emoji/public/images/guitar.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3b8.png \ No newline at end of file diff --git a/plugins/emoji/public/images/gun.png b/plugins/emoji/public/images/gun.png deleted file mode 120000 index 6cf88b7ad0..0000000000 --- a/plugins/emoji/public/images/gun.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f52b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/haircut.png b/plugins/emoji/public/images/haircut.png deleted file mode 120000 index 375fa25db1..0000000000 --- a/plugins/emoji/public/images/haircut.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f487.png \ No newline at end of file diff --git a/plugins/emoji/public/images/hamburger.png b/plugins/emoji/public/images/hamburger.png deleted file mode 120000 index 362ca334ae..0000000000 --- a/plugins/emoji/public/images/hamburger.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f354.png \ No newline at end of file diff --git a/plugins/emoji/public/images/hammer.png b/plugins/emoji/public/images/hammer.png deleted file mode 120000 index e6d5825df5..0000000000 --- a/plugins/emoji/public/images/hammer.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f528.png \ No newline at end of file diff --git a/plugins/emoji/public/images/hamster.png b/plugins/emoji/public/images/hamster.png deleted file mode 120000 index b729566c27..0000000000 --- a/plugins/emoji/public/images/hamster.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f439.png \ No newline at end of file diff --git a/plugins/emoji/public/images/hand.png b/plugins/emoji/public/images/hand.png deleted file mode 120000 index da39fb0cba..0000000000 --- a/plugins/emoji/public/images/hand.png +++ /dev/null @@ -1 +0,0 @@ -unicode/270b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/handbag.png b/plugins/emoji/public/images/handbag.png deleted file mode 120000 index f93a284141..0000000000 --- a/plugins/emoji/public/images/handbag.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f45c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/hankey.png b/plugins/emoji/public/images/hankey.png deleted file mode 120000 index c8c5a772b2..0000000000 --- a/plugins/emoji/public/images/hankey.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4a9.png \ No newline at end of file diff --git a/plugins/emoji/public/images/hash.png b/plugins/emoji/public/images/hash.png deleted file mode 120000 index 2cdff46c54..0000000000 --- a/plugins/emoji/public/images/hash.png +++ /dev/null @@ -1 +0,0 @@ -unicode/0023.png \ No newline at end of file diff --git a/plugins/emoji/public/images/hatched_chick.png b/plugins/emoji/public/images/hatched_chick.png deleted file mode 120000 index e71635571b..0000000000 --- a/plugins/emoji/public/images/hatched_chick.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f425.png \ No newline at end of file diff --git a/plugins/emoji/public/images/hatching_chick.png b/plugins/emoji/public/images/hatching_chick.png deleted file mode 120000 index 3a72c178b7..0000000000 --- a/plugins/emoji/public/images/hatching_chick.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f423.png \ No newline at end of file diff --git a/plugins/emoji/public/images/headphones.png b/plugins/emoji/public/images/headphones.png deleted file mode 120000 index ffbb04629b..0000000000 --- a/plugins/emoji/public/images/headphones.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3a7.png \ No newline at end of file diff --git a/plugins/emoji/public/images/hear_no_evil.png b/plugins/emoji/public/images/hear_no_evil.png deleted file mode 120000 index ecb7fa8122..0000000000 --- a/plugins/emoji/public/images/hear_no_evil.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f649.png \ No newline at end of file diff --git a/plugins/emoji/public/images/heart.png b/plugins/emoji/public/images/heart.png deleted file mode 120000 index 1362baafa4..0000000000 --- a/plugins/emoji/public/images/heart.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2764.png \ No newline at end of file diff --git a/plugins/emoji/public/images/heart_decoration.png b/plugins/emoji/public/images/heart_decoration.png deleted file mode 120000 index 88ad0c85ac..0000000000 --- a/plugins/emoji/public/images/heart_decoration.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f49f.png \ No newline at end of file diff --git a/plugins/emoji/public/images/heart_eyes.png b/plugins/emoji/public/images/heart_eyes.png deleted file mode 120000 index 0d0248be5d..0000000000 --- a/plugins/emoji/public/images/heart_eyes.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f60d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/heart_eyes_cat.png b/plugins/emoji/public/images/heart_eyes_cat.png deleted file mode 120000 index 55eca64e11..0000000000 --- a/plugins/emoji/public/images/heart_eyes_cat.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f63b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/heartbeat.png b/plugins/emoji/public/images/heartbeat.png deleted file mode 120000 index 85191a0053..0000000000 --- a/plugins/emoji/public/images/heartbeat.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f493.png \ No newline at end of file diff --git a/plugins/emoji/public/images/heartpulse.png b/plugins/emoji/public/images/heartpulse.png deleted file mode 120000 index bd1ca76108..0000000000 --- a/plugins/emoji/public/images/heartpulse.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f497.png \ No newline at end of file diff --git a/plugins/emoji/public/images/hearts.png b/plugins/emoji/public/images/hearts.png deleted file mode 120000 index f8bb59c1b4..0000000000 --- a/plugins/emoji/public/images/hearts.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2665.png \ No newline at end of file diff --git a/plugins/emoji/public/images/heavy_check_mark.png b/plugins/emoji/public/images/heavy_check_mark.png deleted file mode 120000 index 30e9f06b7c..0000000000 --- a/plugins/emoji/public/images/heavy_check_mark.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2714.png \ No newline at end of file diff --git a/plugins/emoji/public/images/heavy_division_sign.png b/plugins/emoji/public/images/heavy_division_sign.png deleted file mode 120000 index 6b593c8c02..0000000000 --- a/plugins/emoji/public/images/heavy_division_sign.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2797.png \ No newline at end of file diff --git a/plugins/emoji/public/images/heavy_dollar_sign.png b/plugins/emoji/public/images/heavy_dollar_sign.png deleted file mode 120000 index 5cf869bd0e..0000000000 --- a/plugins/emoji/public/images/heavy_dollar_sign.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4b2.png \ No newline at end of file diff --git a/plugins/emoji/public/images/heavy_exclamation_mark.png b/plugins/emoji/public/images/heavy_exclamation_mark.png deleted file mode 120000 index f085c97778..0000000000 --- a/plugins/emoji/public/images/heavy_exclamation_mark.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2757.png \ No newline at end of file diff --git a/plugins/emoji/public/images/heavy_minus_sign.png b/plugins/emoji/public/images/heavy_minus_sign.png deleted file mode 120000 index 8e032c1e20..0000000000 --- a/plugins/emoji/public/images/heavy_minus_sign.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2796.png \ No newline at end of file diff --git a/plugins/emoji/public/images/heavy_multiplication_x.png b/plugins/emoji/public/images/heavy_multiplication_x.png deleted file mode 120000 index ac6e4c9d81..0000000000 --- a/plugins/emoji/public/images/heavy_multiplication_x.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2716.png \ No newline at end of file diff --git a/plugins/emoji/public/images/heavy_plus_sign.png b/plugins/emoji/public/images/heavy_plus_sign.png deleted file mode 120000 index 820107fff6..0000000000 --- a/plugins/emoji/public/images/heavy_plus_sign.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2795.png \ No newline at end of file diff --git a/plugins/emoji/public/images/helicopter.png b/plugins/emoji/public/images/helicopter.png deleted file mode 120000 index 6dc29b813b..0000000000 --- a/plugins/emoji/public/images/helicopter.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f681.png \ No newline at end of file diff --git a/plugins/emoji/public/images/herb.png b/plugins/emoji/public/images/herb.png deleted file mode 120000 index 5bd41d8d3d..0000000000 --- a/plugins/emoji/public/images/herb.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f33f.png \ No newline at end of file diff --git a/plugins/emoji/public/images/hibiscus.png b/plugins/emoji/public/images/hibiscus.png deleted file mode 120000 index 7297f85ad3..0000000000 --- a/plugins/emoji/public/images/hibiscus.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f33a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/high_brightness.png b/plugins/emoji/public/images/high_brightness.png deleted file mode 120000 index e9b01dcc74..0000000000 --- a/plugins/emoji/public/images/high_brightness.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f506.png \ No newline at end of file diff --git a/plugins/emoji/public/images/high_heel.png b/plugins/emoji/public/images/high_heel.png deleted file mode 120000 index 59ad904176..0000000000 --- a/plugins/emoji/public/images/high_heel.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f460.png \ No newline at end of file diff --git a/plugins/emoji/public/images/hocho.png b/plugins/emoji/public/images/hocho.png deleted file mode 120000 index fd4eefe131..0000000000 --- a/plugins/emoji/public/images/hocho.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f52a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/honey_pot.png b/plugins/emoji/public/images/honey_pot.png deleted file mode 120000 index bf38a2598c..0000000000 --- a/plugins/emoji/public/images/honey_pot.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f36f.png \ No newline at end of file diff --git a/plugins/emoji/public/images/honeybee.png b/plugins/emoji/public/images/honeybee.png deleted file mode 120000 index d1768eca5e..0000000000 --- a/plugins/emoji/public/images/honeybee.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f41d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/horse.png b/plugins/emoji/public/images/horse.png deleted file mode 120000 index 577f99055f..0000000000 --- a/plugins/emoji/public/images/horse.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f434.png \ No newline at end of file diff --git a/plugins/emoji/public/images/horse_racing.png b/plugins/emoji/public/images/horse_racing.png deleted file mode 120000 index 3d98145e4d..0000000000 --- a/plugins/emoji/public/images/horse_racing.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3c7.png \ No newline at end of file diff --git a/plugins/emoji/public/images/hospital.png b/plugins/emoji/public/images/hospital.png deleted file mode 120000 index 2efb289a20..0000000000 --- a/plugins/emoji/public/images/hospital.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3e5.png \ No newline at end of file diff --git a/plugins/emoji/public/images/hotel.png b/plugins/emoji/public/images/hotel.png deleted file mode 120000 index 4e5aa700ce..0000000000 --- a/plugins/emoji/public/images/hotel.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3e8.png \ No newline at end of file diff --git a/plugins/emoji/public/images/hotsprings.png b/plugins/emoji/public/images/hotsprings.png deleted file mode 120000 index b17e6082a8..0000000000 --- a/plugins/emoji/public/images/hotsprings.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2668.png \ No newline at end of file diff --git a/plugins/emoji/public/images/hourglass.png b/plugins/emoji/public/images/hourglass.png deleted file mode 120000 index 2563dece9a..0000000000 --- a/plugins/emoji/public/images/hourglass.png +++ /dev/null @@ -1 +0,0 @@ -unicode/231b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/hourglass_flowing_sand.png b/plugins/emoji/public/images/hourglass_flowing_sand.png deleted file mode 120000 index 604e9e0ef2..0000000000 --- a/plugins/emoji/public/images/hourglass_flowing_sand.png +++ /dev/null @@ -1 +0,0 @@ -unicode/23f3.png \ No newline at end of file diff --git a/plugins/emoji/public/images/house.png b/plugins/emoji/public/images/house.png deleted file mode 120000 index ff45713ec8..0000000000 --- a/plugins/emoji/public/images/house.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3e0.png \ No newline at end of file diff --git a/plugins/emoji/public/images/house_with_garden.png b/plugins/emoji/public/images/house_with_garden.png deleted file mode 120000 index 8ad8f1dda9..0000000000 --- a/plugins/emoji/public/images/house_with_garden.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3e1.png \ No newline at end of file diff --git a/plugins/emoji/public/images/hurtrealbad.png b/plugins/emoji/public/images/hurtrealbad.png deleted file mode 100755 index 146ef1a6a8..0000000000 Binary files a/plugins/emoji/public/images/hurtrealbad.png and /dev/null differ diff --git a/plugins/emoji/public/images/hushed.png b/plugins/emoji/public/images/hushed.png deleted file mode 120000 index ac08f7813a..0000000000 --- a/plugins/emoji/public/images/hushed.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f62f.png \ No newline at end of file diff --git a/plugins/emoji/public/images/ice_cream.png b/plugins/emoji/public/images/ice_cream.png deleted file mode 120000 index bfe0492351..0000000000 --- a/plugins/emoji/public/images/ice_cream.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f368.png \ No newline at end of file diff --git a/plugins/emoji/public/images/icecream.png b/plugins/emoji/public/images/icecream.png deleted file mode 120000 index 14a3231fd8..0000000000 --- a/plugins/emoji/public/images/icecream.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f366.png \ No newline at end of file diff --git a/plugins/emoji/public/images/id.png b/plugins/emoji/public/images/id.png deleted file mode 120000 index eac174f509..0000000000 --- a/plugins/emoji/public/images/id.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f194.png \ No newline at end of file diff --git a/plugins/emoji/public/images/ideograph_advantage.png b/plugins/emoji/public/images/ideograph_advantage.png deleted file mode 120000 index 6605a39355..0000000000 --- a/plugins/emoji/public/images/ideograph_advantage.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f250.png \ No newline at end of file diff --git a/plugins/emoji/public/images/imp.png b/plugins/emoji/public/images/imp.png deleted file mode 120000 index 0c979fc1c8..0000000000 --- a/plugins/emoji/public/images/imp.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f47f.png \ No newline at end of file diff --git a/plugins/emoji/public/images/inbox_tray.png b/plugins/emoji/public/images/inbox_tray.png deleted file mode 120000 index 1f6523edcc..0000000000 --- a/plugins/emoji/public/images/inbox_tray.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4e5.png \ No newline at end of file diff --git a/plugins/emoji/public/images/incoming_envelope.png b/plugins/emoji/public/images/incoming_envelope.png deleted file mode 120000 index 8ec90adce6..0000000000 --- a/plugins/emoji/public/images/incoming_envelope.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4e8.png \ No newline at end of file diff --git a/plugins/emoji/public/images/information_desk_person.png b/plugins/emoji/public/images/information_desk_person.png deleted file mode 120000 index 5b7edeb0a7..0000000000 --- a/plugins/emoji/public/images/information_desk_person.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f481.png \ No newline at end of file diff --git a/plugins/emoji/public/images/information_source.png b/plugins/emoji/public/images/information_source.png deleted file mode 120000 index b25c3cbd76..0000000000 --- a/plugins/emoji/public/images/information_source.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2139.png \ No newline at end of file diff --git a/plugins/emoji/public/images/innocent.png b/plugins/emoji/public/images/innocent.png deleted file mode 120000 index 87c9e8c58f..0000000000 --- a/plugins/emoji/public/images/innocent.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f607.png \ No newline at end of file diff --git a/plugins/emoji/public/images/interrobang.png b/plugins/emoji/public/images/interrobang.png deleted file mode 120000 index ac24b74153..0000000000 --- a/plugins/emoji/public/images/interrobang.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2049.png \ No newline at end of file diff --git a/plugins/emoji/public/images/iphone.png b/plugins/emoji/public/images/iphone.png deleted file mode 120000 index 30673d2775..0000000000 --- a/plugins/emoji/public/images/iphone.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4f1.png \ No newline at end of file diff --git a/plugins/emoji/public/images/it.png b/plugins/emoji/public/images/it.png deleted file mode 120000 index 6534d31cd0..0000000000 --- a/plugins/emoji/public/images/it.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f1ee-1f1f9.png \ No newline at end of file diff --git a/plugins/emoji/public/images/izakaya_lantern.png b/plugins/emoji/public/images/izakaya_lantern.png deleted file mode 120000 index a00faa3541..0000000000 --- a/plugins/emoji/public/images/izakaya_lantern.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3ee.png \ No newline at end of file diff --git a/plugins/emoji/public/images/jack_o_lantern.png b/plugins/emoji/public/images/jack_o_lantern.png deleted file mode 120000 index a457fa6077..0000000000 --- a/plugins/emoji/public/images/jack_o_lantern.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f383.png \ No newline at end of file diff --git a/plugins/emoji/public/images/japan.png b/plugins/emoji/public/images/japan.png deleted file mode 120000 index 4587899c6a..0000000000 --- a/plugins/emoji/public/images/japan.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f5fe.png \ No newline at end of file diff --git a/plugins/emoji/public/images/japanese_castle.png b/plugins/emoji/public/images/japanese_castle.png deleted file mode 120000 index b6465b5ca1..0000000000 --- a/plugins/emoji/public/images/japanese_castle.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3ef.png \ No newline at end of file diff --git a/plugins/emoji/public/images/japanese_goblin.png b/plugins/emoji/public/images/japanese_goblin.png deleted file mode 120000 index 74c5aaced5..0000000000 --- a/plugins/emoji/public/images/japanese_goblin.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f47a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/japanese_ogre.png b/plugins/emoji/public/images/japanese_ogre.png deleted file mode 120000 index 1a6082f418..0000000000 --- a/plugins/emoji/public/images/japanese_ogre.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f479.png \ No newline at end of file diff --git a/plugins/emoji/public/images/jeans.png b/plugins/emoji/public/images/jeans.png deleted file mode 120000 index cec43bf66b..0000000000 --- a/plugins/emoji/public/images/jeans.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f456.png \ No newline at end of file diff --git a/plugins/emoji/public/images/joy.png b/plugins/emoji/public/images/joy.png deleted file mode 120000 index e5a84c0200..0000000000 --- a/plugins/emoji/public/images/joy.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f602.png \ No newline at end of file diff --git a/plugins/emoji/public/images/joy_cat.png b/plugins/emoji/public/images/joy_cat.png deleted file mode 120000 index 86a6a3fa47..0000000000 --- a/plugins/emoji/public/images/joy_cat.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f639.png \ No newline at end of file diff --git a/plugins/emoji/public/images/jp.png b/plugins/emoji/public/images/jp.png deleted file mode 120000 index a006fa1186..0000000000 --- a/plugins/emoji/public/images/jp.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f1ef-1f1f5.png \ No newline at end of file diff --git a/plugins/emoji/public/images/key.png b/plugins/emoji/public/images/key.png deleted file mode 120000 index 8a6eec737e..0000000000 --- a/plugins/emoji/public/images/key.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f511.png \ No newline at end of file diff --git a/plugins/emoji/public/images/keycap_ten.png b/plugins/emoji/public/images/keycap_ten.png deleted file mode 120000 index d1ed03a818..0000000000 --- a/plugins/emoji/public/images/keycap_ten.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f51f.png \ No newline at end of file diff --git a/plugins/emoji/public/images/kimono.png b/plugins/emoji/public/images/kimono.png deleted file mode 120000 index 50261f1f05..0000000000 --- a/plugins/emoji/public/images/kimono.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f458.png \ No newline at end of file diff --git a/plugins/emoji/public/images/kiss.png b/plugins/emoji/public/images/kiss.png deleted file mode 120000 index d3b97cba96..0000000000 --- a/plugins/emoji/public/images/kiss.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f48b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/kissing.png b/plugins/emoji/public/images/kissing.png deleted file mode 120000 index 661f9a2881..0000000000 --- a/plugins/emoji/public/images/kissing.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f617.png \ No newline at end of file diff --git a/plugins/emoji/public/images/kissing_cat.png b/plugins/emoji/public/images/kissing_cat.png deleted file mode 120000 index 38f289f1da..0000000000 --- a/plugins/emoji/public/images/kissing_cat.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f63d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/kissing_closed_eyes.png b/plugins/emoji/public/images/kissing_closed_eyes.png deleted file mode 120000 index 18a8bfde3d..0000000000 --- a/plugins/emoji/public/images/kissing_closed_eyes.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f61a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/kissing_heart.png b/plugins/emoji/public/images/kissing_heart.png deleted file mode 120000 index 3d66dd0904..0000000000 --- a/plugins/emoji/public/images/kissing_heart.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f618.png \ No newline at end of file diff --git a/plugins/emoji/public/images/kissing_smiling_eyes.png b/plugins/emoji/public/images/kissing_smiling_eyes.png deleted file mode 120000 index 5a55bd834e..0000000000 --- a/plugins/emoji/public/images/kissing_smiling_eyes.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f619.png \ No newline at end of file diff --git a/plugins/emoji/public/images/koala.png b/plugins/emoji/public/images/koala.png deleted file mode 120000 index 5919ebfcdd..0000000000 --- a/plugins/emoji/public/images/koala.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f428.png \ No newline at end of file diff --git a/plugins/emoji/public/images/koko.png b/plugins/emoji/public/images/koko.png deleted file mode 120000 index 4216c07f92..0000000000 --- a/plugins/emoji/public/images/koko.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f201.png \ No newline at end of file diff --git a/plugins/emoji/public/images/kr.png b/plugins/emoji/public/images/kr.png deleted file mode 120000 index be8cdb3db3..0000000000 --- a/plugins/emoji/public/images/kr.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f1f0-1f1f7.png \ No newline at end of file diff --git a/plugins/emoji/public/images/lantern.png b/plugins/emoji/public/images/lantern.png deleted file mode 120000 index a00faa3541..0000000000 --- a/plugins/emoji/public/images/lantern.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3ee.png \ No newline at end of file diff --git a/plugins/emoji/public/images/large_blue_circle.png b/plugins/emoji/public/images/large_blue_circle.png deleted file mode 120000 index d40ba77c02..0000000000 --- a/plugins/emoji/public/images/large_blue_circle.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f535.png \ No newline at end of file diff --git a/plugins/emoji/public/images/large_blue_diamond.png b/plugins/emoji/public/images/large_blue_diamond.png deleted file mode 120000 index ef7a174ef4..0000000000 --- a/plugins/emoji/public/images/large_blue_diamond.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f537.png \ No newline at end of file diff --git a/plugins/emoji/public/images/large_orange_diamond.png b/plugins/emoji/public/images/large_orange_diamond.png deleted file mode 120000 index 199a588f00..0000000000 --- a/plugins/emoji/public/images/large_orange_diamond.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f536.png \ No newline at end of file diff --git a/plugins/emoji/public/images/last_quarter_moon.png b/plugins/emoji/public/images/last_quarter_moon.png deleted file mode 120000 index ef89f66032..0000000000 --- a/plugins/emoji/public/images/last_quarter_moon.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f317.png \ No newline at end of file diff --git a/plugins/emoji/public/images/last_quarter_moon_with_face.png b/plugins/emoji/public/images/last_quarter_moon_with_face.png deleted file mode 120000 index e1ef1cf41f..0000000000 --- a/plugins/emoji/public/images/last_quarter_moon_with_face.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f31c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/laughing.png b/plugins/emoji/public/images/laughing.png deleted file mode 120000 index 7332e39078..0000000000 --- a/plugins/emoji/public/images/laughing.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f606.png \ No newline at end of file diff --git a/plugins/emoji/public/images/leaves.png b/plugins/emoji/public/images/leaves.png deleted file mode 120000 index 19492463bd..0000000000 --- a/plugins/emoji/public/images/leaves.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f343.png \ No newline at end of file diff --git a/plugins/emoji/public/images/ledger.png b/plugins/emoji/public/images/ledger.png deleted file mode 120000 index 72bd6c33c7..0000000000 --- a/plugins/emoji/public/images/ledger.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4d2.png \ No newline at end of file diff --git a/plugins/emoji/public/images/left_luggage.png b/plugins/emoji/public/images/left_luggage.png deleted file mode 120000 index 7d6ca72d90..0000000000 --- a/plugins/emoji/public/images/left_luggage.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6c5.png \ No newline at end of file diff --git a/plugins/emoji/public/images/left_right_arrow.png b/plugins/emoji/public/images/left_right_arrow.png deleted file mode 120000 index 70d9adbf1c..0000000000 --- a/plugins/emoji/public/images/left_right_arrow.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2194.png \ No newline at end of file diff --git a/plugins/emoji/public/images/leftwards_arrow_with_hook.png b/plugins/emoji/public/images/leftwards_arrow_with_hook.png deleted file mode 120000 index 4bf1b424f1..0000000000 --- a/plugins/emoji/public/images/leftwards_arrow_with_hook.png +++ /dev/null @@ -1 +0,0 @@ -unicode/21a9.png \ No newline at end of file diff --git a/plugins/emoji/public/images/lemon.png b/plugins/emoji/public/images/lemon.png deleted file mode 120000 index 8f68b3e048..0000000000 --- a/plugins/emoji/public/images/lemon.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f34b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/leo.png b/plugins/emoji/public/images/leo.png deleted file mode 120000 index f699bf6d3c..0000000000 --- a/plugins/emoji/public/images/leo.png +++ /dev/null @@ -1 +0,0 @@ -unicode/264c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/leopard.png b/plugins/emoji/public/images/leopard.png deleted file mode 120000 index 415e3b4d17..0000000000 --- a/plugins/emoji/public/images/leopard.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f406.png \ No newline at end of file diff --git a/plugins/emoji/public/images/libra.png b/plugins/emoji/public/images/libra.png deleted file mode 120000 index 04bf667748..0000000000 --- a/plugins/emoji/public/images/libra.png +++ /dev/null @@ -1 +0,0 @@ -unicode/264e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/light_rail.png b/plugins/emoji/public/images/light_rail.png deleted file mode 120000 index 448409bc2d..0000000000 --- a/plugins/emoji/public/images/light_rail.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f688.png \ No newline at end of file diff --git a/plugins/emoji/public/images/link.png b/plugins/emoji/public/images/link.png deleted file mode 120000 index 4e82b7668c..0000000000 --- a/plugins/emoji/public/images/link.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f517.png \ No newline at end of file diff --git a/plugins/emoji/public/images/lips.png b/plugins/emoji/public/images/lips.png deleted file mode 120000 index 7109205352..0000000000 --- a/plugins/emoji/public/images/lips.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f444.png \ No newline at end of file diff --git a/plugins/emoji/public/images/lipstick.png b/plugins/emoji/public/images/lipstick.png deleted file mode 120000 index 588bc77901..0000000000 --- a/plugins/emoji/public/images/lipstick.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f484.png \ No newline at end of file diff --git a/plugins/emoji/public/images/lock.png b/plugins/emoji/public/images/lock.png deleted file mode 120000 index 6e433a52a4..0000000000 --- a/plugins/emoji/public/images/lock.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f512.png \ No newline at end of file diff --git a/plugins/emoji/public/images/lock_with_ink_pen.png b/plugins/emoji/public/images/lock_with_ink_pen.png deleted file mode 120000 index 823ba53a28..0000000000 --- a/plugins/emoji/public/images/lock_with_ink_pen.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f50f.png \ No newline at end of file diff --git a/plugins/emoji/public/images/lollipop.png b/plugins/emoji/public/images/lollipop.png deleted file mode 120000 index ea983b520c..0000000000 --- a/plugins/emoji/public/images/lollipop.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f36d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/loop.png b/plugins/emoji/public/images/loop.png deleted file mode 120000 index cb0c2b131e..0000000000 --- a/plugins/emoji/public/images/loop.png +++ /dev/null @@ -1 +0,0 @@ -unicode/27bf.png \ No newline at end of file diff --git a/plugins/emoji/public/images/loudspeaker.png b/plugins/emoji/public/images/loudspeaker.png deleted file mode 120000 index ca5f5232e3..0000000000 --- a/plugins/emoji/public/images/loudspeaker.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4e2.png \ No newline at end of file diff --git a/plugins/emoji/public/images/love_hotel.png b/plugins/emoji/public/images/love_hotel.png deleted file mode 120000 index b608db140d..0000000000 --- a/plugins/emoji/public/images/love_hotel.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3e9.png \ No newline at end of file diff --git a/plugins/emoji/public/images/love_letter.png b/plugins/emoji/public/images/love_letter.png deleted file mode 120000 index bbdee84084..0000000000 --- a/plugins/emoji/public/images/love_letter.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f48c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/low_brightness.png b/plugins/emoji/public/images/low_brightness.png deleted file mode 120000 index 6a90c2e8ce..0000000000 --- a/plugins/emoji/public/images/low_brightness.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f505.png \ No newline at end of file diff --git a/plugins/emoji/public/images/m.png b/plugins/emoji/public/images/m.png deleted file mode 120000 index 1423f80362..0000000000 --- a/plugins/emoji/public/images/m.png +++ /dev/null @@ -1 +0,0 @@ -unicode/24c2.png \ No newline at end of file diff --git a/plugins/emoji/public/images/mag.png b/plugins/emoji/public/images/mag.png deleted file mode 120000 index 70b0cab887..0000000000 --- a/plugins/emoji/public/images/mag.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f50d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/mag_right.png b/plugins/emoji/public/images/mag_right.png deleted file mode 120000 index 81eb76fb06..0000000000 --- a/plugins/emoji/public/images/mag_right.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f50e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/mahjong.png b/plugins/emoji/public/images/mahjong.png deleted file mode 120000 index 8632fba11c..0000000000 --- a/plugins/emoji/public/images/mahjong.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f004.png \ No newline at end of file diff --git a/plugins/emoji/public/images/mailbox.png b/plugins/emoji/public/images/mailbox.png deleted file mode 120000 index 7022c84b53..0000000000 --- a/plugins/emoji/public/images/mailbox.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4eb.png \ No newline at end of file diff --git a/plugins/emoji/public/images/mailbox_closed.png b/plugins/emoji/public/images/mailbox_closed.png deleted file mode 120000 index 678bf987f4..0000000000 --- a/plugins/emoji/public/images/mailbox_closed.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4ea.png \ No newline at end of file diff --git a/plugins/emoji/public/images/mailbox_with_mail.png b/plugins/emoji/public/images/mailbox_with_mail.png deleted file mode 120000 index 77ecb292d0..0000000000 --- a/plugins/emoji/public/images/mailbox_with_mail.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4ec.png \ No newline at end of file diff --git a/plugins/emoji/public/images/mailbox_with_no_mail.png b/plugins/emoji/public/images/mailbox_with_no_mail.png deleted file mode 120000 index 4801a95afc..0000000000 --- a/plugins/emoji/public/images/mailbox_with_no_mail.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4ed.png \ No newline at end of file diff --git a/plugins/emoji/public/images/man.png b/plugins/emoji/public/images/man.png deleted file mode 120000 index 4fca922aa1..0000000000 --- a/plugins/emoji/public/images/man.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f468.png \ No newline at end of file diff --git a/plugins/emoji/public/images/man_with_gua_pi_mao.png b/plugins/emoji/public/images/man_with_gua_pi_mao.png deleted file mode 120000 index c98dd580eb..0000000000 --- a/plugins/emoji/public/images/man_with_gua_pi_mao.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f472.png \ No newline at end of file diff --git a/plugins/emoji/public/images/man_with_turban.png b/plugins/emoji/public/images/man_with_turban.png deleted file mode 120000 index 3ef16c61fd..0000000000 --- a/plugins/emoji/public/images/man_with_turban.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f473.png \ No newline at end of file diff --git a/plugins/emoji/public/images/mans_shoe.png b/plugins/emoji/public/images/mans_shoe.png deleted file mode 120000 index cb54be7b1d..0000000000 --- a/plugins/emoji/public/images/mans_shoe.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f45e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/maple_leaf.png b/plugins/emoji/public/images/maple_leaf.png deleted file mode 120000 index c04325af97..0000000000 --- a/plugins/emoji/public/images/maple_leaf.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f341.png \ No newline at end of file diff --git a/plugins/emoji/public/images/markov.png b/plugins/emoji/public/images/markov.png deleted file mode 100644 index d90ff2f3dd..0000000000 Binary files a/plugins/emoji/public/images/markov.png and /dev/null differ diff --git a/plugins/emoji/public/images/mask.png b/plugins/emoji/public/images/mask.png deleted file mode 120000 index 9d7051388f..0000000000 --- a/plugins/emoji/public/images/mask.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f637.png \ No newline at end of file diff --git a/plugins/emoji/public/images/massage.png b/plugins/emoji/public/images/massage.png deleted file mode 120000 index c312a63477..0000000000 --- a/plugins/emoji/public/images/massage.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f486.png \ No newline at end of file diff --git a/plugins/emoji/public/images/meat_on_bone.png b/plugins/emoji/public/images/meat_on_bone.png deleted file mode 120000 index acc34def9a..0000000000 --- a/plugins/emoji/public/images/meat_on_bone.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f356.png \ No newline at end of file diff --git a/plugins/emoji/public/images/mega.png b/plugins/emoji/public/images/mega.png deleted file mode 120000 index 713cbe57f0..0000000000 --- a/plugins/emoji/public/images/mega.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4e3.png \ No newline at end of file diff --git a/plugins/emoji/public/images/melon.png b/plugins/emoji/public/images/melon.png deleted file mode 120000 index 6682e14e15..0000000000 --- a/plugins/emoji/public/images/melon.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f348.png \ No newline at end of file diff --git a/plugins/emoji/public/images/memo.png b/plugins/emoji/public/images/memo.png deleted file mode 120000 index 86cf768615..0000000000 --- a/plugins/emoji/public/images/memo.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4dd.png \ No newline at end of file diff --git a/plugins/emoji/public/images/mens.png b/plugins/emoji/public/images/mens.png deleted file mode 120000 index 7d0a3f3913..0000000000 --- a/plugins/emoji/public/images/mens.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6b9.png \ No newline at end of file diff --git a/plugins/emoji/public/images/metal.png b/plugins/emoji/public/images/metal.png deleted file mode 100755 index 94f1fda224..0000000000 Binary files a/plugins/emoji/public/images/metal.png and /dev/null differ diff --git a/plugins/emoji/public/images/metro.png b/plugins/emoji/public/images/metro.png deleted file mode 120000 index 6ffc8b5f70..0000000000 --- a/plugins/emoji/public/images/metro.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f687.png \ No newline at end of file diff --git a/plugins/emoji/public/images/microphone.png b/plugins/emoji/public/images/microphone.png deleted file mode 120000 index f9f9ee1aef..0000000000 --- a/plugins/emoji/public/images/microphone.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3a4.png \ No newline at end of file diff --git a/plugins/emoji/public/images/microscope.png b/plugins/emoji/public/images/microscope.png deleted file mode 120000 index a41c40cdcb..0000000000 --- a/plugins/emoji/public/images/microscope.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f52c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/milky_way.png b/plugins/emoji/public/images/milky_way.png deleted file mode 120000 index 4ae45e6713..0000000000 --- a/plugins/emoji/public/images/milky_way.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f30c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/minibus.png b/plugins/emoji/public/images/minibus.png deleted file mode 120000 index dda7931b80..0000000000 --- a/plugins/emoji/public/images/minibus.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f690.png \ No newline at end of file diff --git a/plugins/emoji/public/images/minidisc.png b/plugins/emoji/public/images/minidisc.png deleted file mode 120000 index a09f4f9dde..0000000000 --- a/plugins/emoji/public/images/minidisc.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4bd.png \ No newline at end of file diff --git a/plugins/emoji/public/images/mobile_phone_off.png b/plugins/emoji/public/images/mobile_phone_off.png deleted file mode 120000 index c38b96953e..0000000000 --- a/plugins/emoji/public/images/mobile_phone_off.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4f4.png \ No newline at end of file diff --git a/plugins/emoji/public/images/money_with_wings.png b/plugins/emoji/public/images/money_with_wings.png deleted file mode 120000 index d1c8c76237..0000000000 --- a/plugins/emoji/public/images/money_with_wings.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4b8.png \ No newline at end of file diff --git a/plugins/emoji/public/images/moneybag.png b/plugins/emoji/public/images/moneybag.png deleted file mode 120000 index 8015c910c2..0000000000 --- a/plugins/emoji/public/images/moneybag.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4b0.png \ No newline at end of file diff --git a/plugins/emoji/public/images/monkey.png b/plugins/emoji/public/images/monkey.png deleted file mode 120000 index 172f99bc7f..0000000000 --- a/plugins/emoji/public/images/monkey.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f412.png \ No newline at end of file diff --git a/plugins/emoji/public/images/monkey_face.png b/plugins/emoji/public/images/monkey_face.png deleted file mode 120000 index 6c12bfd986..0000000000 --- a/plugins/emoji/public/images/monkey_face.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f435.png \ No newline at end of file diff --git a/plugins/emoji/public/images/monorail.png b/plugins/emoji/public/images/monorail.png deleted file mode 120000 index af2fababc7..0000000000 --- a/plugins/emoji/public/images/monorail.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f69d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/moon.png b/plugins/emoji/public/images/moon.png deleted file mode 120000 index 1c589610f6..0000000000 --- a/plugins/emoji/public/images/moon.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f314.png \ No newline at end of file diff --git a/plugins/emoji/public/images/mortar_board.png b/plugins/emoji/public/images/mortar_board.png deleted file mode 120000 index 6388096e00..0000000000 --- a/plugins/emoji/public/images/mortar_board.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f393.png \ No newline at end of file diff --git a/plugins/emoji/public/images/mount_fuji.png b/plugins/emoji/public/images/mount_fuji.png deleted file mode 120000 index e6d48e70e4..0000000000 --- a/plugins/emoji/public/images/mount_fuji.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f5fb.png \ No newline at end of file diff --git a/plugins/emoji/public/images/mountain_bicyclist.png b/plugins/emoji/public/images/mountain_bicyclist.png deleted file mode 120000 index 8ac95b66c5..0000000000 --- a/plugins/emoji/public/images/mountain_bicyclist.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6b5.png \ No newline at end of file diff --git a/plugins/emoji/public/images/mountain_cableway.png b/plugins/emoji/public/images/mountain_cableway.png deleted file mode 120000 index fad5d102f5..0000000000 --- a/plugins/emoji/public/images/mountain_cableway.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6a0.png \ No newline at end of file diff --git a/plugins/emoji/public/images/mountain_railway.png b/plugins/emoji/public/images/mountain_railway.png deleted file mode 120000 index 9eb2ff6a76..0000000000 --- a/plugins/emoji/public/images/mountain_railway.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f69e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/mouse.png b/plugins/emoji/public/images/mouse.png deleted file mode 120000 index d3fb6ff981..0000000000 --- a/plugins/emoji/public/images/mouse.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f42d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/mouse2.png b/plugins/emoji/public/images/mouse2.png deleted file mode 120000 index 6d0e846938..0000000000 --- a/plugins/emoji/public/images/mouse2.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f401.png \ No newline at end of file diff --git a/plugins/emoji/public/images/movie_camera.png b/plugins/emoji/public/images/movie_camera.png deleted file mode 120000 index 931edf69fc..0000000000 --- a/plugins/emoji/public/images/movie_camera.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3a5.png \ No newline at end of file diff --git a/plugins/emoji/public/images/moyai.png b/plugins/emoji/public/images/moyai.png deleted file mode 120000 index 0e6686365a..0000000000 --- a/plugins/emoji/public/images/moyai.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f5ff.png \ No newline at end of file diff --git a/plugins/emoji/public/images/muscle.png b/plugins/emoji/public/images/muscle.png deleted file mode 120000 index 83a632d4ad..0000000000 --- a/plugins/emoji/public/images/muscle.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4aa.png \ No newline at end of file diff --git a/plugins/emoji/public/images/mushroom.png b/plugins/emoji/public/images/mushroom.png deleted file mode 120000 index cb8f696c7d..0000000000 --- a/plugins/emoji/public/images/mushroom.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f344.png \ No newline at end of file diff --git a/plugins/emoji/public/images/musical_keyboard.png b/plugins/emoji/public/images/musical_keyboard.png deleted file mode 120000 index 0607cc6805..0000000000 --- a/plugins/emoji/public/images/musical_keyboard.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3b9.png \ No newline at end of file diff --git a/plugins/emoji/public/images/musical_note.png b/plugins/emoji/public/images/musical_note.png deleted file mode 120000 index 0e3f7e8937..0000000000 --- a/plugins/emoji/public/images/musical_note.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3b5.png \ No newline at end of file diff --git a/plugins/emoji/public/images/musical_score.png b/plugins/emoji/public/images/musical_score.png deleted file mode 120000 index 14e0b39fe2..0000000000 --- a/plugins/emoji/public/images/musical_score.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3bc.png \ No newline at end of file diff --git a/plugins/emoji/public/images/mute.png b/plugins/emoji/public/images/mute.png deleted file mode 120000 index 3d6fbe2a36..0000000000 --- a/plugins/emoji/public/images/mute.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f507.png \ No newline at end of file diff --git a/plugins/emoji/public/images/nail_care.png b/plugins/emoji/public/images/nail_care.png deleted file mode 120000 index 3616fc5ec3..0000000000 --- a/plugins/emoji/public/images/nail_care.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f485.png \ No newline at end of file diff --git a/plugins/emoji/public/images/name_badge.png b/plugins/emoji/public/images/name_badge.png deleted file mode 120000 index a0f9446728..0000000000 --- a/plugins/emoji/public/images/name_badge.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4db.png \ No newline at end of file diff --git a/plugins/emoji/public/images/neckbeard.png b/plugins/emoji/public/images/neckbeard.png deleted file mode 100755 index 6158152e1d..0000000000 Binary files a/plugins/emoji/public/images/neckbeard.png and /dev/null differ diff --git a/plugins/emoji/public/images/necktie.png b/plugins/emoji/public/images/necktie.png deleted file mode 120000 index 485b2dbe91..0000000000 --- a/plugins/emoji/public/images/necktie.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f454.png \ No newline at end of file diff --git a/plugins/emoji/public/images/negative_squared_cross_mark.png b/plugins/emoji/public/images/negative_squared_cross_mark.png deleted file mode 120000 index 584c2abc03..0000000000 --- a/plugins/emoji/public/images/negative_squared_cross_mark.png +++ /dev/null @@ -1 +0,0 @@ -unicode/274e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/neutral_face.png b/plugins/emoji/public/images/neutral_face.png deleted file mode 120000 index 0c8163cd5d..0000000000 --- a/plugins/emoji/public/images/neutral_face.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f610.png \ No newline at end of file diff --git a/plugins/emoji/public/images/new.png b/plugins/emoji/public/images/new.png deleted file mode 120000 index 53f7e4db6e..0000000000 --- a/plugins/emoji/public/images/new.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f195.png \ No newline at end of file diff --git a/plugins/emoji/public/images/new_moon.png b/plugins/emoji/public/images/new_moon.png deleted file mode 120000 index 3a3fa145d1..0000000000 --- a/plugins/emoji/public/images/new_moon.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f311.png \ No newline at end of file diff --git a/plugins/emoji/public/images/new_moon_with_face.png b/plugins/emoji/public/images/new_moon_with_face.png deleted file mode 120000 index 4cf7b98372..0000000000 --- a/plugins/emoji/public/images/new_moon_with_face.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f31a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/newspaper.png b/plugins/emoji/public/images/newspaper.png deleted file mode 120000 index c293be595a..0000000000 --- a/plugins/emoji/public/images/newspaper.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4f0.png \ No newline at end of file diff --git a/plugins/emoji/public/images/ng.png b/plugins/emoji/public/images/ng.png deleted file mode 120000 index a056925356..0000000000 --- a/plugins/emoji/public/images/ng.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f196.png \ No newline at end of file diff --git a/plugins/emoji/public/images/nine.png b/plugins/emoji/public/images/nine.png deleted file mode 120000 index 98bd6bcd4f..0000000000 --- a/plugins/emoji/public/images/nine.png +++ /dev/null @@ -1 +0,0 @@ -unicode/0039.png \ No newline at end of file diff --git a/plugins/emoji/public/images/no_bell.png b/plugins/emoji/public/images/no_bell.png deleted file mode 120000 index 639986ef4b..0000000000 --- a/plugins/emoji/public/images/no_bell.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f515.png \ No newline at end of file diff --git a/plugins/emoji/public/images/no_bicycles.png b/plugins/emoji/public/images/no_bicycles.png deleted file mode 120000 index 2611e3d7c2..0000000000 --- a/plugins/emoji/public/images/no_bicycles.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6b3.png \ No newline at end of file diff --git a/plugins/emoji/public/images/no_entry.png b/plugins/emoji/public/images/no_entry.png deleted file mode 120000 index bd28b894da..0000000000 --- a/plugins/emoji/public/images/no_entry.png +++ /dev/null @@ -1 +0,0 @@ -unicode/26d4.png \ No newline at end of file diff --git a/plugins/emoji/public/images/no_entry_sign.png b/plugins/emoji/public/images/no_entry_sign.png deleted file mode 120000 index b3d86696ce..0000000000 --- a/plugins/emoji/public/images/no_entry_sign.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6ab.png \ No newline at end of file diff --git a/plugins/emoji/public/images/no_good.png b/plugins/emoji/public/images/no_good.png deleted file mode 120000 index 88562a4e52..0000000000 --- a/plugins/emoji/public/images/no_good.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f645.png \ No newline at end of file diff --git a/plugins/emoji/public/images/no_mobile_phones.png b/plugins/emoji/public/images/no_mobile_phones.png deleted file mode 120000 index 97d727dab2..0000000000 --- a/plugins/emoji/public/images/no_mobile_phones.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4f5.png \ No newline at end of file diff --git a/plugins/emoji/public/images/no_mouth.png b/plugins/emoji/public/images/no_mouth.png deleted file mode 120000 index 99800d2db0..0000000000 --- a/plugins/emoji/public/images/no_mouth.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f636.png \ No newline at end of file diff --git a/plugins/emoji/public/images/no_pedestrians.png b/plugins/emoji/public/images/no_pedestrians.png deleted file mode 120000 index dd66e6bb70..0000000000 --- a/plugins/emoji/public/images/no_pedestrians.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6b7.png \ No newline at end of file diff --git a/plugins/emoji/public/images/no_smoking.png b/plugins/emoji/public/images/no_smoking.png deleted file mode 120000 index abaee93811..0000000000 --- a/plugins/emoji/public/images/no_smoking.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6ad.png \ No newline at end of file diff --git a/plugins/emoji/public/images/non-potable_water.png b/plugins/emoji/public/images/non-potable_water.png deleted file mode 120000 index 5afedf60d4..0000000000 --- a/plugins/emoji/public/images/non-potable_water.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6b1.png \ No newline at end of file diff --git a/plugins/emoji/public/images/nose.png b/plugins/emoji/public/images/nose.png deleted file mode 120000 index cc5a54e20c..0000000000 --- a/plugins/emoji/public/images/nose.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f443.png \ No newline at end of file diff --git a/plugins/emoji/public/images/notebook.png b/plugins/emoji/public/images/notebook.png deleted file mode 120000 index 5be93b3e44..0000000000 --- a/plugins/emoji/public/images/notebook.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4d3.png \ No newline at end of file diff --git a/plugins/emoji/public/images/notebook_with_decorative_cover.png b/plugins/emoji/public/images/notebook_with_decorative_cover.png deleted file mode 120000 index 3aac1dd6a3..0000000000 --- a/plugins/emoji/public/images/notebook_with_decorative_cover.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4d4.png \ No newline at end of file diff --git a/plugins/emoji/public/images/notes.png b/plugins/emoji/public/images/notes.png deleted file mode 120000 index 224b64f1bf..0000000000 --- a/plugins/emoji/public/images/notes.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3b6.png \ No newline at end of file diff --git a/plugins/emoji/public/images/nut_and_bolt.png b/plugins/emoji/public/images/nut_and_bolt.png deleted file mode 120000 index 7530f4e231..0000000000 --- a/plugins/emoji/public/images/nut_and_bolt.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f529.png \ No newline at end of file diff --git a/plugins/emoji/public/images/o.png b/plugins/emoji/public/images/o.png deleted file mode 120000 index b7a481b329..0000000000 --- a/plugins/emoji/public/images/o.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2b55.png \ No newline at end of file diff --git a/plugins/emoji/public/images/o2.png b/plugins/emoji/public/images/o2.png deleted file mode 120000 index abc55e2ba0..0000000000 --- a/plugins/emoji/public/images/o2.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f17e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/ocean.png b/plugins/emoji/public/images/ocean.png deleted file mode 120000 index 148f04ea40..0000000000 --- a/plugins/emoji/public/images/ocean.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f30a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/octocat.png b/plugins/emoji/public/images/octocat.png deleted file mode 100755 index 361f68224d..0000000000 Binary files a/plugins/emoji/public/images/octocat.png and /dev/null differ diff --git a/plugins/emoji/public/images/octopus.png b/plugins/emoji/public/images/octopus.png deleted file mode 120000 index 8538bdf06c..0000000000 --- a/plugins/emoji/public/images/octopus.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f419.png \ No newline at end of file diff --git a/plugins/emoji/public/images/oden.png b/plugins/emoji/public/images/oden.png deleted file mode 120000 index b661ff2f64..0000000000 --- a/plugins/emoji/public/images/oden.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f362.png \ No newline at end of file diff --git a/plugins/emoji/public/images/office.png b/plugins/emoji/public/images/office.png deleted file mode 120000 index 43213334b7..0000000000 --- a/plugins/emoji/public/images/office.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3e2.png \ No newline at end of file diff --git a/plugins/emoji/public/images/ok.png b/plugins/emoji/public/images/ok.png deleted file mode 120000 index e44e71a7b9..0000000000 --- a/plugins/emoji/public/images/ok.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f197.png \ No newline at end of file diff --git a/plugins/emoji/public/images/ok_hand.png b/plugins/emoji/public/images/ok_hand.png deleted file mode 120000 index cbcef36ea1..0000000000 --- a/plugins/emoji/public/images/ok_hand.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f44c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/ok_woman.png b/plugins/emoji/public/images/ok_woman.png deleted file mode 120000 index b17c458a4f..0000000000 --- a/plugins/emoji/public/images/ok_woman.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f646.png \ No newline at end of file diff --git a/plugins/emoji/public/images/older_man.png b/plugins/emoji/public/images/older_man.png deleted file mode 120000 index 75f2f552ab..0000000000 --- a/plugins/emoji/public/images/older_man.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f474.png \ No newline at end of file diff --git a/plugins/emoji/public/images/older_woman.png b/plugins/emoji/public/images/older_woman.png deleted file mode 120000 index 78ead72191..0000000000 --- a/plugins/emoji/public/images/older_woman.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f475.png \ No newline at end of file diff --git a/plugins/emoji/public/images/on.png b/plugins/emoji/public/images/on.png deleted file mode 120000 index f16f702e35..0000000000 --- a/plugins/emoji/public/images/on.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f51b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/oncoming_automobile.png b/plugins/emoji/public/images/oncoming_automobile.png deleted file mode 120000 index 9c65b63602..0000000000 --- a/plugins/emoji/public/images/oncoming_automobile.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f698.png \ No newline at end of file diff --git a/plugins/emoji/public/images/oncoming_bus.png b/plugins/emoji/public/images/oncoming_bus.png deleted file mode 120000 index 6c9981c7fe..0000000000 --- a/plugins/emoji/public/images/oncoming_bus.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f68d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/oncoming_police_car.png b/plugins/emoji/public/images/oncoming_police_car.png deleted file mode 120000 index 6fbae18580..0000000000 --- a/plugins/emoji/public/images/oncoming_police_car.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f694.png \ No newline at end of file diff --git a/plugins/emoji/public/images/oncoming_taxi.png b/plugins/emoji/public/images/oncoming_taxi.png deleted file mode 120000 index d56b95af63..0000000000 --- a/plugins/emoji/public/images/oncoming_taxi.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f696.png \ No newline at end of file diff --git a/plugins/emoji/public/images/one.png b/plugins/emoji/public/images/one.png deleted file mode 120000 index a3301c8928..0000000000 --- a/plugins/emoji/public/images/one.png +++ /dev/null @@ -1 +0,0 @@ -unicode/0031.png \ No newline at end of file diff --git a/plugins/emoji/public/images/open_book.png b/plugins/emoji/public/images/open_book.png deleted file mode 120000 index c95e97943c..0000000000 --- a/plugins/emoji/public/images/open_book.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4d6.png \ No newline at end of file diff --git a/plugins/emoji/public/images/open_file_folder.png b/plugins/emoji/public/images/open_file_folder.png deleted file mode 120000 index 64c9f9b801..0000000000 --- a/plugins/emoji/public/images/open_file_folder.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4c2.png \ No newline at end of file diff --git a/plugins/emoji/public/images/open_hands.png b/plugins/emoji/public/images/open_hands.png deleted file mode 120000 index dd19ce87a0..0000000000 --- a/plugins/emoji/public/images/open_hands.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f450.png \ No newline at end of file diff --git a/plugins/emoji/public/images/open_mouth.png b/plugins/emoji/public/images/open_mouth.png deleted file mode 120000 index faf9722b82..0000000000 --- a/plugins/emoji/public/images/open_mouth.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f62e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/ophiuchus.png b/plugins/emoji/public/images/ophiuchus.png deleted file mode 120000 index 1c080c45ba..0000000000 --- a/plugins/emoji/public/images/ophiuchus.png +++ /dev/null @@ -1 +0,0 @@ -unicode/26ce.png \ No newline at end of file diff --git a/plugins/emoji/public/images/orange_book.png b/plugins/emoji/public/images/orange_book.png deleted file mode 120000 index 056958e9a9..0000000000 --- a/plugins/emoji/public/images/orange_book.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4d9.png \ No newline at end of file diff --git a/plugins/emoji/public/images/outbox_tray.png b/plugins/emoji/public/images/outbox_tray.png deleted file mode 120000 index 02e496e900..0000000000 --- a/plugins/emoji/public/images/outbox_tray.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4e4.png \ No newline at end of file diff --git a/plugins/emoji/public/images/ox.png b/plugins/emoji/public/images/ox.png deleted file mode 120000 index 51a3aa6662..0000000000 --- a/plugins/emoji/public/images/ox.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f402.png \ No newline at end of file diff --git a/plugins/emoji/public/images/package.png b/plugins/emoji/public/images/package.png deleted file mode 120000 index e017037118..0000000000 --- a/plugins/emoji/public/images/package.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4e6.png \ No newline at end of file diff --git a/plugins/emoji/public/images/page_facing_up.png b/plugins/emoji/public/images/page_facing_up.png deleted file mode 120000 index 4163893650..0000000000 --- a/plugins/emoji/public/images/page_facing_up.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4c4.png \ No newline at end of file diff --git a/plugins/emoji/public/images/page_with_curl.png b/plugins/emoji/public/images/page_with_curl.png deleted file mode 120000 index 5ca7256099..0000000000 --- a/plugins/emoji/public/images/page_with_curl.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4c3.png \ No newline at end of file diff --git a/plugins/emoji/public/images/pager.png b/plugins/emoji/public/images/pager.png deleted file mode 120000 index aa013af26c..0000000000 --- a/plugins/emoji/public/images/pager.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4df.png \ No newline at end of file diff --git a/plugins/emoji/public/images/palm_tree.png b/plugins/emoji/public/images/palm_tree.png deleted file mode 120000 index 8025f633d0..0000000000 --- a/plugins/emoji/public/images/palm_tree.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f334.png \ No newline at end of file diff --git a/plugins/emoji/public/images/panda_face.png b/plugins/emoji/public/images/panda_face.png deleted file mode 120000 index 54581cc7a9..0000000000 --- a/plugins/emoji/public/images/panda_face.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f43c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/paperclip.png b/plugins/emoji/public/images/paperclip.png deleted file mode 120000 index c189521a20..0000000000 --- a/plugins/emoji/public/images/paperclip.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4ce.png \ No newline at end of file diff --git a/plugins/emoji/public/images/parking.png b/plugins/emoji/public/images/parking.png deleted file mode 120000 index beeeaff5df..0000000000 --- a/plugins/emoji/public/images/parking.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f17f.png \ No newline at end of file diff --git a/plugins/emoji/public/images/part_alternation_mark.png b/plugins/emoji/public/images/part_alternation_mark.png deleted file mode 120000 index 97e7244cd7..0000000000 --- a/plugins/emoji/public/images/part_alternation_mark.png +++ /dev/null @@ -1 +0,0 @@ -unicode/303d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/partly_sunny.png b/plugins/emoji/public/images/partly_sunny.png deleted file mode 120000 index aae87e0fb4..0000000000 --- a/plugins/emoji/public/images/partly_sunny.png +++ /dev/null @@ -1 +0,0 @@ -unicode/26c5.png \ No newline at end of file diff --git a/plugins/emoji/public/images/passport_control.png b/plugins/emoji/public/images/passport_control.png deleted file mode 120000 index c14ffc60ca..0000000000 --- a/plugins/emoji/public/images/passport_control.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6c2.png \ No newline at end of file diff --git a/plugins/emoji/public/images/paw_prints.png b/plugins/emoji/public/images/paw_prints.png deleted file mode 120000 index bbb76f06be..0000000000 --- a/plugins/emoji/public/images/paw_prints.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f43e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/peach.png b/plugins/emoji/public/images/peach.png deleted file mode 120000 index c6932993a6..0000000000 --- a/plugins/emoji/public/images/peach.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f351.png \ No newline at end of file diff --git a/plugins/emoji/public/images/pear.png b/plugins/emoji/public/images/pear.png deleted file mode 120000 index 973048cc6a..0000000000 --- a/plugins/emoji/public/images/pear.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f350.png \ No newline at end of file diff --git a/plugins/emoji/public/images/pencil.png b/plugins/emoji/public/images/pencil.png deleted file mode 120000 index 86cf768615..0000000000 --- a/plugins/emoji/public/images/pencil.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4dd.png \ No newline at end of file diff --git a/plugins/emoji/public/images/pencil2.png b/plugins/emoji/public/images/pencil2.png deleted file mode 120000 index a32c1634d1..0000000000 --- a/plugins/emoji/public/images/pencil2.png +++ /dev/null @@ -1 +0,0 @@ -unicode/270f.png \ No newline at end of file diff --git a/plugins/emoji/public/images/penguin.png b/plugins/emoji/public/images/penguin.png deleted file mode 120000 index 1d705fb24d..0000000000 --- a/plugins/emoji/public/images/penguin.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f427.png \ No newline at end of file diff --git a/plugins/emoji/public/images/pensive.png b/plugins/emoji/public/images/pensive.png deleted file mode 120000 index 65dc755517..0000000000 --- a/plugins/emoji/public/images/pensive.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f614.png \ No newline at end of file diff --git a/plugins/emoji/public/images/performing_arts.png b/plugins/emoji/public/images/performing_arts.png deleted file mode 120000 index a1ec9abcfd..0000000000 --- a/plugins/emoji/public/images/performing_arts.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3ad.png \ No newline at end of file diff --git a/plugins/emoji/public/images/persevere.png b/plugins/emoji/public/images/persevere.png deleted file mode 120000 index ff9960484c..0000000000 --- a/plugins/emoji/public/images/persevere.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f623.png \ No newline at end of file diff --git a/plugins/emoji/public/images/person_frowning.png b/plugins/emoji/public/images/person_frowning.png deleted file mode 120000 index dba4617b84..0000000000 --- a/plugins/emoji/public/images/person_frowning.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f64d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/person_with_blond_hair.png b/plugins/emoji/public/images/person_with_blond_hair.png deleted file mode 120000 index 20d9b2624f..0000000000 --- a/plugins/emoji/public/images/person_with_blond_hair.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f471.png \ No newline at end of file diff --git a/plugins/emoji/public/images/person_with_pouting_face.png b/plugins/emoji/public/images/person_with_pouting_face.png deleted file mode 120000 index c59a4b2688..0000000000 --- a/plugins/emoji/public/images/person_with_pouting_face.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f64e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/phone.png b/plugins/emoji/public/images/phone.png deleted file mode 120000 index 66f023aaaa..0000000000 --- a/plugins/emoji/public/images/phone.png +++ /dev/null @@ -1 +0,0 @@ -unicode/260e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/pig.png b/plugins/emoji/public/images/pig.png deleted file mode 120000 index 1168b408d9..0000000000 --- a/plugins/emoji/public/images/pig.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f437.png \ No newline at end of file diff --git a/plugins/emoji/public/images/pig2.png b/plugins/emoji/public/images/pig2.png deleted file mode 120000 index b8f3054e33..0000000000 --- a/plugins/emoji/public/images/pig2.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f416.png \ No newline at end of file diff --git a/plugins/emoji/public/images/pig_nose.png b/plugins/emoji/public/images/pig_nose.png deleted file mode 120000 index 67a14dd630..0000000000 --- a/plugins/emoji/public/images/pig_nose.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f43d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/pill.png b/plugins/emoji/public/images/pill.png deleted file mode 120000 index 39055ebe57..0000000000 --- a/plugins/emoji/public/images/pill.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f48a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/pineapple.png b/plugins/emoji/public/images/pineapple.png deleted file mode 120000 index b51c51c6c3..0000000000 --- a/plugins/emoji/public/images/pineapple.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f34d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/pisces.png b/plugins/emoji/public/images/pisces.png deleted file mode 120000 index 4f9bcf4f81..0000000000 --- a/plugins/emoji/public/images/pisces.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2653.png \ No newline at end of file diff --git a/plugins/emoji/public/images/pizza.png b/plugins/emoji/public/images/pizza.png deleted file mode 120000 index 0fc4d8d1a7..0000000000 --- a/plugins/emoji/public/images/pizza.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f355.png \ No newline at end of file diff --git a/plugins/emoji/public/images/point_down.png b/plugins/emoji/public/images/point_down.png deleted file mode 120000 index 841155d40c..0000000000 --- a/plugins/emoji/public/images/point_down.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f447.png \ No newline at end of file diff --git a/plugins/emoji/public/images/point_left.png b/plugins/emoji/public/images/point_left.png deleted file mode 120000 index 79d5ab40be..0000000000 --- a/plugins/emoji/public/images/point_left.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f448.png \ No newline at end of file diff --git a/plugins/emoji/public/images/point_right.png b/plugins/emoji/public/images/point_right.png deleted file mode 120000 index c94f84fe84..0000000000 --- a/plugins/emoji/public/images/point_right.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f449.png \ No newline at end of file diff --git a/plugins/emoji/public/images/point_up.png b/plugins/emoji/public/images/point_up.png deleted file mode 120000 index a25e2e1d23..0000000000 --- a/plugins/emoji/public/images/point_up.png +++ /dev/null @@ -1 +0,0 @@ -unicode/261d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/point_up_2.png b/plugins/emoji/public/images/point_up_2.png deleted file mode 120000 index c3589e1ed3..0000000000 --- a/plugins/emoji/public/images/point_up_2.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f446.png \ No newline at end of file diff --git a/plugins/emoji/public/images/police_car.png b/plugins/emoji/public/images/police_car.png deleted file mode 120000 index 6ecd04aa30..0000000000 --- a/plugins/emoji/public/images/police_car.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f693.png \ No newline at end of file diff --git a/plugins/emoji/public/images/poodle.png b/plugins/emoji/public/images/poodle.png deleted file mode 120000 index 8bb4520ac0..0000000000 --- a/plugins/emoji/public/images/poodle.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f429.png \ No newline at end of file diff --git a/plugins/emoji/public/images/poop.png b/plugins/emoji/public/images/poop.png deleted file mode 120000 index c8c5a772b2..0000000000 --- a/plugins/emoji/public/images/poop.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4a9.png \ No newline at end of file diff --git a/plugins/emoji/public/images/post_office.png b/plugins/emoji/public/images/post_office.png deleted file mode 120000 index b4f8f20a75..0000000000 --- a/plugins/emoji/public/images/post_office.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3e3.png \ No newline at end of file diff --git a/plugins/emoji/public/images/postal_horn.png b/plugins/emoji/public/images/postal_horn.png deleted file mode 120000 index ade3f6cc14..0000000000 --- a/plugins/emoji/public/images/postal_horn.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4ef.png \ No newline at end of file diff --git a/plugins/emoji/public/images/postbox.png b/plugins/emoji/public/images/postbox.png deleted file mode 120000 index 86bcd458c0..0000000000 --- a/plugins/emoji/public/images/postbox.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4ee.png \ No newline at end of file diff --git a/plugins/emoji/public/images/potable_water.png b/plugins/emoji/public/images/potable_water.png deleted file mode 120000 index dab0e117fb..0000000000 --- a/plugins/emoji/public/images/potable_water.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6b0.png \ No newline at end of file diff --git a/plugins/emoji/public/images/pouch.png b/plugins/emoji/public/images/pouch.png deleted file mode 120000 index 0fdd953a19..0000000000 --- a/plugins/emoji/public/images/pouch.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f45d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/poultry_leg.png b/plugins/emoji/public/images/poultry_leg.png deleted file mode 120000 index 7e3d37f108..0000000000 --- a/plugins/emoji/public/images/poultry_leg.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f357.png \ No newline at end of file diff --git a/plugins/emoji/public/images/pound.png b/plugins/emoji/public/images/pound.png deleted file mode 120000 index 74d086bb9d..0000000000 --- a/plugins/emoji/public/images/pound.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4b7.png \ No newline at end of file diff --git a/plugins/emoji/public/images/pouting_cat.png b/plugins/emoji/public/images/pouting_cat.png deleted file mode 120000 index 9f7aff80a0..0000000000 --- a/plugins/emoji/public/images/pouting_cat.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f63e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/pray.png b/plugins/emoji/public/images/pray.png deleted file mode 120000 index 8147900a9c..0000000000 --- a/plugins/emoji/public/images/pray.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f64f.png \ No newline at end of file diff --git a/plugins/emoji/public/images/princess.png b/plugins/emoji/public/images/princess.png deleted file mode 120000 index 652d0925fe..0000000000 --- a/plugins/emoji/public/images/princess.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f478.png \ No newline at end of file diff --git a/plugins/emoji/public/images/punch.png b/plugins/emoji/public/images/punch.png deleted file mode 120000 index 2423b707f2..0000000000 --- a/plugins/emoji/public/images/punch.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f44a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/purple_heart.png b/plugins/emoji/public/images/purple_heart.png deleted file mode 120000 index d48fc7f7a3..0000000000 --- a/plugins/emoji/public/images/purple_heart.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f49c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/purse.png b/plugins/emoji/public/images/purse.png deleted file mode 120000 index edb70ffc8c..0000000000 --- a/plugins/emoji/public/images/purse.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f45b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/pushpin.png b/plugins/emoji/public/images/pushpin.png deleted file mode 120000 index 9a99e83c2e..0000000000 --- a/plugins/emoji/public/images/pushpin.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4cc.png \ No newline at end of file diff --git a/plugins/emoji/public/images/put_litter_in_its_place.png b/plugins/emoji/public/images/put_litter_in_its_place.png deleted file mode 120000 index 45a9ed4984..0000000000 --- a/plugins/emoji/public/images/put_litter_in_its_place.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6ae.png \ No newline at end of file diff --git a/plugins/emoji/public/images/question.png b/plugins/emoji/public/images/question.png deleted file mode 120000 index a88af087b6..0000000000 --- a/plugins/emoji/public/images/question.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2753.png \ No newline at end of file diff --git a/plugins/emoji/public/images/rabbit.png b/plugins/emoji/public/images/rabbit.png deleted file mode 120000 index 9d6e712c5e..0000000000 --- a/plugins/emoji/public/images/rabbit.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f430.png \ No newline at end of file diff --git a/plugins/emoji/public/images/rabbit2.png b/plugins/emoji/public/images/rabbit2.png deleted file mode 120000 index 6abf31f2fa..0000000000 --- a/plugins/emoji/public/images/rabbit2.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f407.png \ No newline at end of file diff --git a/plugins/emoji/public/images/racehorse.png b/plugins/emoji/public/images/racehorse.png deleted file mode 120000 index 4aa454bdc1..0000000000 --- a/plugins/emoji/public/images/racehorse.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f40e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/radio.png b/plugins/emoji/public/images/radio.png deleted file mode 120000 index 10610ef401..0000000000 --- a/plugins/emoji/public/images/radio.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4fb.png \ No newline at end of file diff --git a/plugins/emoji/public/images/radio_button.png b/plugins/emoji/public/images/radio_button.png deleted file mode 120000 index fb91e155c5..0000000000 --- a/plugins/emoji/public/images/radio_button.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f518.png \ No newline at end of file diff --git a/plugins/emoji/public/images/rage.png b/plugins/emoji/public/images/rage.png deleted file mode 120000 index ba281221bd..0000000000 --- a/plugins/emoji/public/images/rage.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f621.png \ No newline at end of file diff --git a/plugins/emoji/public/images/rage1.png b/plugins/emoji/public/images/rage1.png deleted file mode 100755 index f0c4ece775..0000000000 Binary files a/plugins/emoji/public/images/rage1.png and /dev/null differ diff --git a/plugins/emoji/public/images/rage2.png b/plugins/emoji/public/images/rage2.png deleted file mode 100755 index f792e063b4..0000000000 Binary files a/plugins/emoji/public/images/rage2.png and /dev/null differ diff --git a/plugins/emoji/public/images/rage3.png b/plugins/emoji/public/images/rage3.png deleted file mode 100755 index 58764cbcb3..0000000000 Binary files a/plugins/emoji/public/images/rage3.png and /dev/null differ diff --git a/plugins/emoji/public/images/rage4.png b/plugins/emoji/public/images/rage4.png deleted file mode 100755 index c726c94a29..0000000000 Binary files a/plugins/emoji/public/images/rage4.png and /dev/null differ diff --git a/plugins/emoji/public/images/railway_car.png b/plugins/emoji/public/images/railway_car.png deleted file mode 120000 index 856483debc..0000000000 --- a/plugins/emoji/public/images/railway_car.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f683.png \ No newline at end of file diff --git a/plugins/emoji/public/images/rainbow.png b/plugins/emoji/public/images/rainbow.png deleted file mode 120000 index 1a6b41464a..0000000000 --- a/plugins/emoji/public/images/rainbow.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f308.png \ No newline at end of file diff --git a/plugins/emoji/public/images/raised_hand.png b/plugins/emoji/public/images/raised_hand.png deleted file mode 120000 index da39fb0cba..0000000000 --- a/plugins/emoji/public/images/raised_hand.png +++ /dev/null @@ -1 +0,0 @@ -unicode/270b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/raised_hands.png b/plugins/emoji/public/images/raised_hands.png deleted file mode 120000 index 3bac5029e8..0000000000 --- a/plugins/emoji/public/images/raised_hands.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f64c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/raising_hand.png b/plugins/emoji/public/images/raising_hand.png deleted file mode 120000 index 9ec42be095..0000000000 --- a/plugins/emoji/public/images/raising_hand.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f64b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/ram.png b/plugins/emoji/public/images/ram.png deleted file mode 120000 index f1be38c0b3..0000000000 --- a/plugins/emoji/public/images/ram.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f40f.png \ No newline at end of file diff --git a/plugins/emoji/public/images/ramen.png b/plugins/emoji/public/images/ramen.png deleted file mode 120000 index 62e320ac46..0000000000 --- a/plugins/emoji/public/images/ramen.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f35c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/rat.png b/plugins/emoji/public/images/rat.png deleted file mode 120000 index 800b1e0f58..0000000000 --- a/plugins/emoji/public/images/rat.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f400.png \ No newline at end of file diff --git a/plugins/emoji/public/images/recycle.png b/plugins/emoji/public/images/recycle.png deleted file mode 120000 index 54db0b3cdd..0000000000 --- a/plugins/emoji/public/images/recycle.png +++ /dev/null @@ -1 +0,0 @@ -unicode/267b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/red_car.png b/plugins/emoji/public/images/red_car.png deleted file mode 120000 index 5c765c0c78..0000000000 --- a/plugins/emoji/public/images/red_car.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f697.png \ No newline at end of file diff --git a/plugins/emoji/public/images/red_circle.png b/plugins/emoji/public/images/red_circle.png deleted file mode 120000 index 1d117854ed..0000000000 --- a/plugins/emoji/public/images/red_circle.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f534.png \ No newline at end of file diff --git a/plugins/emoji/public/images/registered.png b/plugins/emoji/public/images/registered.png deleted file mode 120000 index 572704d8d9..0000000000 --- a/plugins/emoji/public/images/registered.png +++ /dev/null @@ -1 +0,0 @@ -unicode/00ae.png \ No newline at end of file diff --git a/plugins/emoji/public/images/relaxed.png b/plugins/emoji/public/images/relaxed.png deleted file mode 120000 index 0d6faed0cc..0000000000 --- a/plugins/emoji/public/images/relaxed.png +++ /dev/null @@ -1 +0,0 @@ -unicode/263a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/relieved.png b/plugins/emoji/public/images/relieved.png deleted file mode 120000 index eff71dca1e..0000000000 --- a/plugins/emoji/public/images/relieved.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f60c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/repeat.png b/plugins/emoji/public/images/repeat.png deleted file mode 120000 index b27d6969ab..0000000000 --- a/plugins/emoji/public/images/repeat.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f501.png \ No newline at end of file diff --git a/plugins/emoji/public/images/repeat_one.png b/plugins/emoji/public/images/repeat_one.png deleted file mode 120000 index 22541207a2..0000000000 --- a/plugins/emoji/public/images/repeat_one.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f502.png \ No newline at end of file diff --git a/plugins/emoji/public/images/restroom.png b/plugins/emoji/public/images/restroom.png deleted file mode 120000 index d36f59f72d..0000000000 --- a/plugins/emoji/public/images/restroom.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6bb.png \ No newline at end of file diff --git a/plugins/emoji/public/images/revolving_hearts.png b/plugins/emoji/public/images/revolving_hearts.png deleted file mode 120000 index 081b988d4a..0000000000 --- a/plugins/emoji/public/images/revolving_hearts.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f49e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/rewind.png b/plugins/emoji/public/images/rewind.png deleted file mode 120000 index d6fe513e0c..0000000000 --- a/plugins/emoji/public/images/rewind.png +++ /dev/null @@ -1 +0,0 @@ -unicode/23ea.png \ No newline at end of file diff --git a/plugins/emoji/public/images/ribbon.png b/plugins/emoji/public/images/ribbon.png deleted file mode 120000 index 391cb4a4ef..0000000000 --- a/plugins/emoji/public/images/ribbon.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f380.png \ No newline at end of file diff --git a/plugins/emoji/public/images/rice.png b/plugins/emoji/public/images/rice.png deleted file mode 120000 index b780fb5f1f..0000000000 --- a/plugins/emoji/public/images/rice.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f35a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/rice_ball.png b/plugins/emoji/public/images/rice_ball.png deleted file mode 120000 index 2a7653a0d5..0000000000 --- a/plugins/emoji/public/images/rice_ball.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f359.png \ No newline at end of file diff --git a/plugins/emoji/public/images/rice_cracker.png b/plugins/emoji/public/images/rice_cracker.png deleted file mode 120000 index 0967178292..0000000000 --- a/plugins/emoji/public/images/rice_cracker.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f358.png \ No newline at end of file diff --git a/plugins/emoji/public/images/rice_scene.png b/plugins/emoji/public/images/rice_scene.png deleted file mode 120000 index 3a4c66d9fc..0000000000 --- a/plugins/emoji/public/images/rice_scene.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f391.png \ No newline at end of file diff --git a/plugins/emoji/public/images/ring.png b/plugins/emoji/public/images/ring.png deleted file mode 120000 index d7939e188d..0000000000 --- a/plugins/emoji/public/images/ring.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f48d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/rocket.png b/plugins/emoji/public/images/rocket.png deleted file mode 120000 index a00f435375..0000000000 --- a/plugins/emoji/public/images/rocket.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f680.png \ No newline at end of file diff --git a/plugins/emoji/public/images/roller_coaster.png b/plugins/emoji/public/images/roller_coaster.png deleted file mode 120000 index 52322e0a85..0000000000 --- a/plugins/emoji/public/images/roller_coaster.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3a2.png \ No newline at end of file diff --git a/plugins/emoji/public/images/rooster.png b/plugins/emoji/public/images/rooster.png deleted file mode 120000 index 68d4162887..0000000000 --- a/plugins/emoji/public/images/rooster.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f413.png \ No newline at end of file diff --git a/plugins/emoji/public/images/rose.png b/plugins/emoji/public/images/rose.png deleted file mode 120000 index a5b12117ac..0000000000 --- a/plugins/emoji/public/images/rose.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f339.png \ No newline at end of file diff --git a/plugins/emoji/public/images/rotating_light.png b/plugins/emoji/public/images/rotating_light.png deleted file mode 120000 index 3df341839f..0000000000 --- a/plugins/emoji/public/images/rotating_light.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6a8.png \ No newline at end of file diff --git a/plugins/emoji/public/images/round_pushpin.png b/plugins/emoji/public/images/round_pushpin.png deleted file mode 120000 index bf066c15b8..0000000000 --- a/plugins/emoji/public/images/round_pushpin.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4cd.png \ No newline at end of file diff --git a/plugins/emoji/public/images/rowboat.png b/plugins/emoji/public/images/rowboat.png deleted file mode 120000 index 6a1d209ef9..0000000000 --- a/plugins/emoji/public/images/rowboat.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6a3.png \ No newline at end of file diff --git a/plugins/emoji/public/images/ru.png b/plugins/emoji/public/images/ru.png deleted file mode 120000 index f8127305c8..0000000000 --- a/plugins/emoji/public/images/ru.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f1f7-1f1fa.png \ No newline at end of file diff --git a/plugins/emoji/public/images/rugby_football.png b/plugins/emoji/public/images/rugby_football.png deleted file mode 120000 index 4036943094..0000000000 --- a/plugins/emoji/public/images/rugby_football.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3c9.png \ No newline at end of file diff --git a/plugins/emoji/public/images/runner.png b/plugins/emoji/public/images/runner.png deleted file mode 120000 index 31876ed57d..0000000000 --- a/plugins/emoji/public/images/runner.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3c3.png \ No newline at end of file diff --git a/plugins/emoji/public/images/running.png b/plugins/emoji/public/images/running.png deleted file mode 120000 index 31876ed57d..0000000000 --- a/plugins/emoji/public/images/running.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3c3.png \ No newline at end of file diff --git a/plugins/emoji/public/images/running_shirt_with_sash.png b/plugins/emoji/public/images/running_shirt_with_sash.png deleted file mode 120000 index ed68ed7fd0..0000000000 --- a/plugins/emoji/public/images/running_shirt_with_sash.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3bd.png \ No newline at end of file diff --git a/plugins/emoji/public/images/sa.png b/plugins/emoji/public/images/sa.png deleted file mode 120000 index 2aea26241a..0000000000 --- a/plugins/emoji/public/images/sa.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f202.png \ No newline at end of file diff --git a/plugins/emoji/public/images/sagittarius.png b/plugins/emoji/public/images/sagittarius.png deleted file mode 120000 index 32a4664e75..0000000000 --- a/plugins/emoji/public/images/sagittarius.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2650.png \ No newline at end of file diff --git a/plugins/emoji/public/images/sailboat.png b/plugins/emoji/public/images/sailboat.png deleted file mode 120000 index e134ebd6c2..0000000000 --- a/plugins/emoji/public/images/sailboat.png +++ /dev/null @@ -1 +0,0 @@ -unicode/26f5.png \ No newline at end of file diff --git a/plugins/emoji/public/images/sake.png b/plugins/emoji/public/images/sake.png deleted file mode 120000 index b5ecf47b18..0000000000 --- a/plugins/emoji/public/images/sake.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f376.png \ No newline at end of file diff --git a/plugins/emoji/public/images/sandal.png b/plugins/emoji/public/images/sandal.png deleted file mode 120000 index 055679cd47..0000000000 --- a/plugins/emoji/public/images/sandal.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f461.png \ No newline at end of file diff --git a/plugins/emoji/public/images/santa.png b/plugins/emoji/public/images/santa.png deleted file mode 120000 index 46dbff73b6..0000000000 --- a/plugins/emoji/public/images/santa.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f385.png \ No newline at end of file diff --git a/plugins/emoji/public/images/satellite.png b/plugins/emoji/public/images/satellite.png deleted file mode 120000 index ac1972e1ed..0000000000 --- a/plugins/emoji/public/images/satellite.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4e1.png \ No newline at end of file diff --git a/plugins/emoji/public/images/satisfied.png b/plugins/emoji/public/images/satisfied.png deleted file mode 120000 index 7332e39078..0000000000 --- a/plugins/emoji/public/images/satisfied.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f606.png \ No newline at end of file diff --git a/plugins/emoji/public/images/saxophone.png b/plugins/emoji/public/images/saxophone.png deleted file mode 120000 index 17e34d3ad9..0000000000 --- a/plugins/emoji/public/images/saxophone.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3b7.png \ No newline at end of file diff --git a/plugins/emoji/public/images/school.png b/plugins/emoji/public/images/school.png deleted file mode 120000 index 2556e5de1a..0000000000 --- a/plugins/emoji/public/images/school.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3eb.png \ No newline at end of file diff --git a/plugins/emoji/public/images/school_satchel.png b/plugins/emoji/public/images/school_satchel.png deleted file mode 120000 index e759bebaec..0000000000 --- a/plugins/emoji/public/images/school_satchel.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f392.png \ No newline at end of file diff --git a/plugins/emoji/public/images/scissors.png b/plugins/emoji/public/images/scissors.png deleted file mode 120000 index d0926ff167..0000000000 --- a/plugins/emoji/public/images/scissors.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2702.png \ No newline at end of file diff --git a/plugins/emoji/public/images/scorpius.png b/plugins/emoji/public/images/scorpius.png deleted file mode 120000 index 3bc6ce3d78..0000000000 --- a/plugins/emoji/public/images/scorpius.png +++ /dev/null @@ -1 +0,0 @@ -unicode/264f.png \ No newline at end of file diff --git a/plugins/emoji/public/images/scream.png b/plugins/emoji/public/images/scream.png deleted file mode 120000 index f58990499e..0000000000 --- a/plugins/emoji/public/images/scream.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f631.png \ No newline at end of file diff --git a/plugins/emoji/public/images/scream_cat.png b/plugins/emoji/public/images/scream_cat.png deleted file mode 120000 index 819f2ccc88..0000000000 --- a/plugins/emoji/public/images/scream_cat.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f640.png \ No newline at end of file diff --git a/plugins/emoji/public/images/scroll.png b/plugins/emoji/public/images/scroll.png deleted file mode 120000 index 4934e3c551..0000000000 --- a/plugins/emoji/public/images/scroll.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4dc.png \ No newline at end of file diff --git a/plugins/emoji/public/images/seat.png b/plugins/emoji/public/images/seat.png deleted file mode 120000 index d3a3691abb..0000000000 --- a/plugins/emoji/public/images/seat.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4ba.png \ No newline at end of file diff --git a/plugins/emoji/public/images/secret.png b/plugins/emoji/public/images/secret.png deleted file mode 120000 index 3d51edf34b..0000000000 --- a/plugins/emoji/public/images/secret.png +++ /dev/null @@ -1 +0,0 @@ -unicode/3299.png \ No newline at end of file diff --git a/plugins/emoji/public/images/see_no_evil.png b/plugins/emoji/public/images/see_no_evil.png deleted file mode 120000 index c26c37613a..0000000000 --- a/plugins/emoji/public/images/see_no_evil.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f648.png \ No newline at end of file diff --git a/plugins/emoji/public/images/seedling.png b/plugins/emoji/public/images/seedling.png deleted file mode 120000 index 2f5b12a4dc..0000000000 --- a/plugins/emoji/public/images/seedling.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f331.png \ No newline at end of file diff --git a/plugins/emoji/public/images/seven.png b/plugins/emoji/public/images/seven.png deleted file mode 120000 index 05e58f0740..0000000000 --- a/plugins/emoji/public/images/seven.png +++ /dev/null @@ -1 +0,0 @@ -unicode/0037.png \ No newline at end of file diff --git a/plugins/emoji/public/images/shaved_ice.png b/plugins/emoji/public/images/shaved_ice.png deleted file mode 120000 index 7821ea98d0..0000000000 --- a/plugins/emoji/public/images/shaved_ice.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f367.png \ No newline at end of file diff --git a/plugins/emoji/public/images/sheep.png b/plugins/emoji/public/images/sheep.png deleted file mode 120000 index a4b8d9ad0c..0000000000 --- a/plugins/emoji/public/images/sheep.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f411.png \ No newline at end of file diff --git a/plugins/emoji/public/images/shell.png b/plugins/emoji/public/images/shell.png deleted file mode 120000 index 4d7bd4d36e..0000000000 --- a/plugins/emoji/public/images/shell.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f41a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/ship.png b/plugins/emoji/public/images/ship.png deleted file mode 120000 index cfc1dabd2c..0000000000 --- a/plugins/emoji/public/images/ship.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6a2.png \ No newline at end of file diff --git a/plugins/emoji/public/images/shipit.png b/plugins/emoji/public/images/shipit.png deleted file mode 100755 index a58a47f62f..0000000000 Binary files a/plugins/emoji/public/images/shipit.png and /dev/null differ diff --git a/plugins/emoji/public/images/shirt.png b/plugins/emoji/public/images/shirt.png deleted file mode 120000 index 205cc1b9c1..0000000000 --- a/plugins/emoji/public/images/shirt.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f455.png \ No newline at end of file diff --git a/plugins/emoji/public/images/shit.png b/plugins/emoji/public/images/shit.png deleted file mode 120000 index c8c5a772b2..0000000000 --- a/plugins/emoji/public/images/shit.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4a9.png \ No newline at end of file diff --git a/plugins/emoji/public/images/shoe.png b/plugins/emoji/public/images/shoe.png deleted file mode 120000 index cb54be7b1d..0000000000 --- a/plugins/emoji/public/images/shoe.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f45e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/shower.png b/plugins/emoji/public/images/shower.png deleted file mode 120000 index 0d8bdc0d31..0000000000 --- a/plugins/emoji/public/images/shower.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6bf.png \ No newline at end of file diff --git a/plugins/emoji/public/images/signal_strength.png b/plugins/emoji/public/images/signal_strength.png deleted file mode 120000 index 624174c45c..0000000000 --- a/plugins/emoji/public/images/signal_strength.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4f6.png \ No newline at end of file diff --git a/plugins/emoji/public/images/six.png b/plugins/emoji/public/images/six.png deleted file mode 120000 index cbbfc282d9..0000000000 --- a/plugins/emoji/public/images/six.png +++ /dev/null @@ -1 +0,0 @@ -unicode/0036.png \ No newline at end of file diff --git a/plugins/emoji/public/images/six_pointed_star.png b/plugins/emoji/public/images/six_pointed_star.png deleted file mode 120000 index 37915ad505..0000000000 --- a/plugins/emoji/public/images/six_pointed_star.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f52f.png \ No newline at end of file diff --git a/plugins/emoji/public/images/ski.png b/plugins/emoji/public/images/ski.png deleted file mode 120000 index f0c31115ac..0000000000 --- a/plugins/emoji/public/images/ski.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3bf.png \ No newline at end of file diff --git a/plugins/emoji/public/images/skull.png b/plugins/emoji/public/images/skull.png deleted file mode 120000 index 86bcc55a69..0000000000 --- a/plugins/emoji/public/images/skull.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f480.png \ No newline at end of file diff --git a/plugins/emoji/public/images/sleeping.png b/plugins/emoji/public/images/sleeping.png deleted file mode 120000 index afe6ef800a..0000000000 --- a/plugins/emoji/public/images/sleeping.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f634.png \ No newline at end of file diff --git a/plugins/emoji/public/images/sleepy.png b/plugins/emoji/public/images/sleepy.png deleted file mode 120000 index 4f23760b3b..0000000000 --- a/plugins/emoji/public/images/sleepy.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f62a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/slot_machine.png b/plugins/emoji/public/images/slot_machine.png deleted file mode 120000 index 24080bb7c0..0000000000 --- a/plugins/emoji/public/images/slot_machine.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3b0.png \ No newline at end of file diff --git a/plugins/emoji/public/images/small_blue_diamond.png b/plugins/emoji/public/images/small_blue_diamond.png deleted file mode 120000 index 95b4b9fc6d..0000000000 --- a/plugins/emoji/public/images/small_blue_diamond.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f539.png \ No newline at end of file diff --git a/plugins/emoji/public/images/small_orange_diamond.png b/plugins/emoji/public/images/small_orange_diamond.png deleted file mode 120000 index e85dbee309..0000000000 --- a/plugins/emoji/public/images/small_orange_diamond.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f538.png \ No newline at end of file diff --git a/plugins/emoji/public/images/small_red_triangle.png b/plugins/emoji/public/images/small_red_triangle.png deleted file mode 120000 index 53ab4364fe..0000000000 --- a/plugins/emoji/public/images/small_red_triangle.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f53a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/small_red_triangle_down.png b/plugins/emoji/public/images/small_red_triangle_down.png deleted file mode 120000 index 62231574e0..0000000000 --- a/plugins/emoji/public/images/small_red_triangle_down.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f53b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/smile.png b/plugins/emoji/public/images/smile.png deleted file mode 120000 index 6e88ed0688..0000000000 --- a/plugins/emoji/public/images/smile.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f604.png \ No newline at end of file diff --git a/plugins/emoji/public/images/smile_cat.png b/plugins/emoji/public/images/smile_cat.png deleted file mode 120000 index d0fc083b08..0000000000 --- a/plugins/emoji/public/images/smile_cat.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f638.png \ No newline at end of file diff --git a/plugins/emoji/public/images/smiley.png b/plugins/emoji/public/images/smiley.png deleted file mode 120000 index cd824362b9..0000000000 --- a/plugins/emoji/public/images/smiley.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f603.png \ No newline at end of file diff --git a/plugins/emoji/public/images/smiley_cat.png b/plugins/emoji/public/images/smiley_cat.png deleted file mode 120000 index bbb10516d9..0000000000 --- a/plugins/emoji/public/images/smiley_cat.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f63a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/smiling_imp.png b/plugins/emoji/public/images/smiling_imp.png deleted file mode 120000 index 6327d19dbe..0000000000 --- a/plugins/emoji/public/images/smiling_imp.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f608.png \ No newline at end of file diff --git a/plugins/emoji/public/images/smirk.png b/plugins/emoji/public/images/smirk.png deleted file mode 120000 index c8527f9616..0000000000 --- a/plugins/emoji/public/images/smirk.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f60f.png \ No newline at end of file diff --git a/plugins/emoji/public/images/smirk_cat.png b/plugins/emoji/public/images/smirk_cat.png deleted file mode 120000 index e95f6580d8..0000000000 --- a/plugins/emoji/public/images/smirk_cat.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f63c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/smoking.png b/plugins/emoji/public/images/smoking.png deleted file mode 120000 index 37b8ea6776..0000000000 --- a/plugins/emoji/public/images/smoking.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6ac.png \ No newline at end of file diff --git a/plugins/emoji/public/images/snail.png b/plugins/emoji/public/images/snail.png deleted file mode 120000 index a1f472956b..0000000000 --- a/plugins/emoji/public/images/snail.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f40c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/snake.png b/plugins/emoji/public/images/snake.png deleted file mode 120000 index 5cbb33b8f9..0000000000 --- a/plugins/emoji/public/images/snake.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f40d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/snowboarder.png b/plugins/emoji/public/images/snowboarder.png deleted file mode 120000 index 6fabb940ea..0000000000 --- a/plugins/emoji/public/images/snowboarder.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3c2.png \ No newline at end of file diff --git a/plugins/emoji/public/images/snowflake.png b/plugins/emoji/public/images/snowflake.png deleted file mode 120000 index 0bd4292cb2..0000000000 --- a/plugins/emoji/public/images/snowflake.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2744.png \ No newline at end of file diff --git a/plugins/emoji/public/images/snowman.png b/plugins/emoji/public/images/snowman.png deleted file mode 120000 index fd41bb720e..0000000000 --- a/plugins/emoji/public/images/snowman.png +++ /dev/null @@ -1 +0,0 @@ -unicode/26c4.png \ No newline at end of file diff --git a/plugins/emoji/public/images/sob.png b/plugins/emoji/public/images/sob.png deleted file mode 120000 index 93cd7336ab..0000000000 --- a/plugins/emoji/public/images/sob.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f62d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/soccer.png b/plugins/emoji/public/images/soccer.png deleted file mode 120000 index 3912f45b09..0000000000 --- a/plugins/emoji/public/images/soccer.png +++ /dev/null @@ -1 +0,0 @@ -unicode/26bd.png \ No newline at end of file diff --git a/plugins/emoji/public/images/soon.png b/plugins/emoji/public/images/soon.png deleted file mode 120000 index 1da86c8f67..0000000000 --- a/plugins/emoji/public/images/soon.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f51c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/sos.png b/plugins/emoji/public/images/sos.png deleted file mode 120000 index 5b52ee755f..0000000000 --- a/plugins/emoji/public/images/sos.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f198.png \ No newline at end of file diff --git a/plugins/emoji/public/images/sound.png b/plugins/emoji/public/images/sound.png deleted file mode 120000 index ae84808364..0000000000 --- a/plugins/emoji/public/images/sound.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f509.png \ No newline at end of file diff --git a/plugins/emoji/public/images/space_invader.png b/plugins/emoji/public/images/space_invader.png deleted file mode 120000 index 95098aee70..0000000000 --- a/plugins/emoji/public/images/space_invader.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f47e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/spades.png b/plugins/emoji/public/images/spades.png deleted file mode 120000 index 561cea045e..0000000000 --- a/plugins/emoji/public/images/spades.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2660.png \ No newline at end of file diff --git a/plugins/emoji/public/images/spaghetti.png b/plugins/emoji/public/images/spaghetti.png deleted file mode 120000 index 8be51b22e1..0000000000 --- a/plugins/emoji/public/images/spaghetti.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f35d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/sparkle.png b/plugins/emoji/public/images/sparkle.png deleted file mode 120000 index a80be6f2c7..0000000000 --- a/plugins/emoji/public/images/sparkle.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2747.png \ No newline at end of file diff --git a/plugins/emoji/public/images/sparkler.png b/plugins/emoji/public/images/sparkler.png deleted file mode 120000 index d74142a41f..0000000000 --- a/plugins/emoji/public/images/sparkler.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f387.png \ No newline at end of file diff --git a/plugins/emoji/public/images/sparkles.png b/plugins/emoji/public/images/sparkles.png deleted file mode 120000 index d35a70592b..0000000000 --- a/plugins/emoji/public/images/sparkles.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2728.png \ No newline at end of file diff --git a/plugins/emoji/public/images/sparkling_heart.png b/plugins/emoji/public/images/sparkling_heart.png deleted file mode 120000 index e9eba4e15d..0000000000 --- a/plugins/emoji/public/images/sparkling_heart.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f496.png \ No newline at end of file diff --git a/plugins/emoji/public/images/speak_no_evil.png b/plugins/emoji/public/images/speak_no_evil.png deleted file mode 120000 index c64efd7fcc..0000000000 --- a/plugins/emoji/public/images/speak_no_evil.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f64a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/speaker.png b/plugins/emoji/public/images/speaker.png deleted file mode 120000 index 4fc46c2c4a..0000000000 --- a/plugins/emoji/public/images/speaker.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f50a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/speech_balloon.png b/plugins/emoji/public/images/speech_balloon.png deleted file mode 120000 index 978d8aeaee..0000000000 --- a/plugins/emoji/public/images/speech_balloon.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4ac.png \ No newline at end of file diff --git a/plugins/emoji/public/images/speedboat.png b/plugins/emoji/public/images/speedboat.png deleted file mode 120000 index a96e6aea2e..0000000000 --- a/plugins/emoji/public/images/speedboat.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6a4.png \ No newline at end of file diff --git a/plugins/emoji/public/images/squirrel.png b/plugins/emoji/public/images/squirrel.png deleted file mode 100755 index a58a47f62f..0000000000 Binary files a/plugins/emoji/public/images/squirrel.png and /dev/null differ diff --git a/plugins/emoji/public/images/star.png b/plugins/emoji/public/images/star.png deleted file mode 120000 index 2e34a9cee6..0000000000 --- a/plugins/emoji/public/images/star.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2b50.png \ No newline at end of file diff --git a/plugins/emoji/public/images/star2.png b/plugins/emoji/public/images/star2.png deleted file mode 120000 index 77788513ec..0000000000 --- a/plugins/emoji/public/images/star2.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f31f.png \ No newline at end of file diff --git a/plugins/emoji/public/images/stars.png b/plugins/emoji/public/images/stars.png deleted file mode 120000 index 8a30b37eb4..0000000000 --- a/plugins/emoji/public/images/stars.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f303.png \ No newline at end of file diff --git a/plugins/emoji/public/images/station.png b/plugins/emoji/public/images/station.png deleted file mode 120000 index 2d6d3d0f07..0000000000 --- a/plugins/emoji/public/images/station.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f689.png \ No newline at end of file diff --git a/plugins/emoji/public/images/statue_of_liberty.png b/plugins/emoji/public/images/statue_of_liberty.png deleted file mode 120000 index 4fe98ac34d..0000000000 --- a/plugins/emoji/public/images/statue_of_liberty.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f5fd.png \ No newline at end of file diff --git a/plugins/emoji/public/images/steam_locomotive.png b/plugins/emoji/public/images/steam_locomotive.png deleted file mode 120000 index b7e90514fc..0000000000 --- a/plugins/emoji/public/images/steam_locomotive.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f682.png \ No newline at end of file diff --git a/plugins/emoji/public/images/stew.png b/plugins/emoji/public/images/stew.png deleted file mode 120000 index ecb6aa2c12..0000000000 --- a/plugins/emoji/public/images/stew.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f372.png \ No newline at end of file diff --git a/plugins/emoji/public/images/straight_ruler.png b/plugins/emoji/public/images/straight_ruler.png deleted file mode 120000 index fffdaa0c90..0000000000 --- a/plugins/emoji/public/images/straight_ruler.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4cf.png \ No newline at end of file diff --git a/plugins/emoji/public/images/strawberry.png b/plugins/emoji/public/images/strawberry.png deleted file mode 120000 index 3028b59a19..0000000000 --- a/plugins/emoji/public/images/strawberry.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f353.png \ No newline at end of file diff --git a/plugins/emoji/public/images/stuck_out_tongue.png b/plugins/emoji/public/images/stuck_out_tongue.png deleted file mode 120000 index f17b1c16bd..0000000000 --- a/plugins/emoji/public/images/stuck_out_tongue.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f61b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/stuck_out_tongue_closed_eyes.png b/plugins/emoji/public/images/stuck_out_tongue_closed_eyes.png deleted file mode 120000 index 60199fedda..0000000000 --- a/plugins/emoji/public/images/stuck_out_tongue_closed_eyes.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f61d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/stuck_out_tongue_winking_eye.png b/plugins/emoji/public/images/stuck_out_tongue_winking_eye.png deleted file mode 120000 index 9a51c02b5d..0000000000 --- a/plugins/emoji/public/images/stuck_out_tongue_winking_eye.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f61c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/sun_with_face.png b/plugins/emoji/public/images/sun_with_face.png deleted file mode 120000 index 9f3ca0790b..0000000000 --- a/plugins/emoji/public/images/sun_with_face.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f31e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/sunflower.png b/plugins/emoji/public/images/sunflower.png deleted file mode 120000 index 0a1bad8a83..0000000000 --- a/plugins/emoji/public/images/sunflower.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f33b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/sunglasses.png b/plugins/emoji/public/images/sunglasses.png deleted file mode 120000 index d5789358c0..0000000000 --- a/plugins/emoji/public/images/sunglasses.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f60e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/sunny.png b/plugins/emoji/public/images/sunny.png deleted file mode 120000 index ec2ee45f2f..0000000000 --- a/plugins/emoji/public/images/sunny.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2600.png \ No newline at end of file diff --git a/plugins/emoji/public/images/sunrise.png b/plugins/emoji/public/images/sunrise.png deleted file mode 120000 index 400a2bde20..0000000000 --- a/plugins/emoji/public/images/sunrise.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f305.png \ No newline at end of file diff --git a/plugins/emoji/public/images/sunrise_over_mountains.png b/plugins/emoji/public/images/sunrise_over_mountains.png deleted file mode 120000 index cfd9dc8b47..0000000000 --- a/plugins/emoji/public/images/sunrise_over_mountains.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f304.png \ No newline at end of file diff --git a/plugins/emoji/public/images/surfer.png b/plugins/emoji/public/images/surfer.png deleted file mode 120000 index 812296d05d..0000000000 --- a/plugins/emoji/public/images/surfer.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3c4.png \ No newline at end of file diff --git a/plugins/emoji/public/images/sushi.png b/plugins/emoji/public/images/sushi.png deleted file mode 120000 index f4a4d994b1..0000000000 --- a/plugins/emoji/public/images/sushi.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f363.png \ No newline at end of file diff --git a/plugins/emoji/public/images/suspect.png b/plugins/emoji/public/images/suspect.png deleted file mode 100755 index f9e38afefd..0000000000 Binary files a/plugins/emoji/public/images/suspect.png and /dev/null differ diff --git a/plugins/emoji/public/images/suspension_railway.png b/plugins/emoji/public/images/suspension_railway.png deleted file mode 120000 index 6d6b3ea3a6..0000000000 --- a/plugins/emoji/public/images/suspension_railway.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f69f.png \ No newline at end of file diff --git a/plugins/emoji/public/images/sweat.png b/plugins/emoji/public/images/sweat.png deleted file mode 120000 index 16b63a3bd4..0000000000 --- a/plugins/emoji/public/images/sweat.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f613.png \ No newline at end of file diff --git a/plugins/emoji/public/images/sweat_drops.png b/plugins/emoji/public/images/sweat_drops.png deleted file mode 120000 index 0b76ebb734..0000000000 --- a/plugins/emoji/public/images/sweat_drops.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4a6.png \ No newline at end of file diff --git a/plugins/emoji/public/images/sweat_smile.png b/plugins/emoji/public/images/sweat_smile.png deleted file mode 120000 index 2999db3563..0000000000 --- a/plugins/emoji/public/images/sweat_smile.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f605.png \ No newline at end of file diff --git a/plugins/emoji/public/images/sweet_potato.png b/plugins/emoji/public/images/sweet_potato.png deleted file mode 120000 index 1e62e2d0dc..0000000000 --- a/plugins/emoji/public/images/sweet_potato.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f360.png \ No newline at end of file diff --git a/plugins/emoji/public/images/swimmer.png b/plugins/emoji/public/images/swimmer.png deleted file mode 120000 index 430b4820bc..0000000000 --- a/plugins/emoji/public/images/swimmer.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3ca.png \ No newline at end of file diff --git a/plugins/emoji/public/images/symbols.png b/plugins/emoji/public/images/symbols.png deleted file mode 120000 index c63eb5bb6c..0000000000 --- a/plugins/emoji/public/images/symbols.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f523.png \ No newline at end of file diff --git a/plugins/emoji/public/images/syringe.png b/plugins/emoji/public/images/syringe.png deleted file mode 120000 index 5f914ff0c4..0000000000 --- a/plugins/emoji/public/images/syringe.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f489.png \ No newline at end of file diff --git a/plugins/emoji/public/images/tada.png b/plugins/emoji/public/images/tada.png deleted file mode 120000 index ee2fe8c989..0000000000 --- a/plugins/emoji/public/images/tada.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f389.png \ No newline at end of file diff --git a/plugins/emoji/public/images/tanabata_tree.png b/plugins/emoji/public/images/tanabata_tree.png deleted file mode 120000 index e4c849b141..0000000000 --- a/plugins/emoji/public/images/tanabata_tree.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f38b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/tangerine.png b/plugins/emoji/public/images/tangerine.png deleted file mode 120000 index de74a4b6c4..0000000000 --- a/plugins/emoji/public/images/tangerine.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f34a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/taurus.png b/plugins/emoji/public/images/taurus.png deleted file mode 120000 index b8663881c9..0000000000 --- a/plugins/emoji/public/images/taurus.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2649.png \ No newline at end of file diff --git a/plugins/emoji/public/images/taxi.png b/plugins/emoji/public/images/taxi.png deleted file mode 120000 index 3bbb53f0f7..0000000000 --- a/plugins/emoji/public/images/taxi.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f695.png \ No newline at end of file diff --git a/plugins/emoji/public/images/tea.png b/plugins/emoji/public/images/tea.png deleted file mode 120000 index 285c08d139..0000000000 --- a/plugins/emoji/public/images/tea.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f375.png \ No newline at end of file diff --git a/plugins/emoji/public/images/telephone.png b/plugins/emoji/public/images/telephone.png deleted file mode 120000 index 66f023aaaa..0000000000 --- a/plugins/emoji/public/images/telephone.png +++ /dev/null @@ -1 +0,0 @@ -unicode/260e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/telephone_receiver.png b/plugins/emoji/public/images/telephone_receiver.png deleted file mode 120000 index ad7d836714..0000000000 --- a/plugins/emoji/public/images/telephone_receiver.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4de.png \ No newline at end of file diff --git a/plugins/emoji/public/images/telescope.png b/plugins/emoji/public/images/telescope.png deleted file mode 120000 index 504b45368c..0000000000 --- a/plugins/emoji/public/images/telescope.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f52d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/tennis.png b/plugins/emoji/public/images/tennis.png deleted file mode 120000 index 1f23a29e13..0000000000 --- a/plugins/emoji/public/images/tennis.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3be.png \ No newline at end of file diff --git a/plugins/emoji/public/images/tent.png b/plugins/emoji/public/images/tent.png deleted file mode 120000 index df4f2e38f3..0000000000 --- a/plugins/emoji/public/images/tent.png +++ /dev/null @@ -1 +0,0 @@ -unicode/26fa.png \ No newline at end of file diff --git a/plugins/emoji/public/images/thought_balloon.png b/plugins/emoji/public/images/thought_balloon.png deleted file mode 120000 index 7999771a48..0000000000 --- a/plugins/emoji/public/images/thought_balloon.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4ad.png \ No newline at end of file diff --git a/plugins/emoji/public/images/three.png b/plugins/emoji/public/images/three.png deleted file mode 120000 index c2ae234524..0000000000 --- a/plugins/emoji/public/images/three.png +++ /dev/null @@ -1 +0,0 @@ -unicode/0033.png \ No newline at end of file diff --git a/plugins/emoji/public/images/thumbsdown.png b/plugins/emoji/public/images/thumbsdown.png deleted file mode 120000 index 5b63ed9312..0000000000 --- a/plugins/emoji/public/images/thumbsdown.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f44e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/thumbsup.png b/plugins/emoji/public/images/thumbsup.png deleted file mode 120000 index 5db29d6679..0000000000 --- a/plugins/emoji/public/images/thumbsup.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f44d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/ticket.png b/plugins/emoji/public/images/ticket.png deleted file mode 120000 index 6e58827815..0000000000 --- a/plugins/emoji/public/images/ticket.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3ab.png \ No newline at end of file diff --git a/plugins/emoji/public/images/tiger.png b/plugins/emoji/public/images/tiger.png deleted file mode 120000 index b3b9b348b2..0000000000 --- a/plugins/emoji/public/images/tiger.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f42f.png \ No newline at end of file diff --git a/plugins/emoji/public/images/tiger2.png b/plugins/emoji/public/images/tiger2.png deleted file mode 120000 index 475fc74c42..0000000000 --- a/plugins/emoji/public/images/tiger2.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f405.png \ No newline at end of file diff --git a/plugins/emoji/public/images/tired_face.png b/plugins/emoji/public/images/tired_face.png deleted file mode 120000 index ccc7a1150e..0000000000 --- a/plugins/emoji/public/images/tired_face.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f62b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/tm.png b/plugins/emoji/public/images/tm.png deleted file mode 120000 index 47559a3104..0000000000 --- a/plugins/emoji/public/images/tm.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2122.png \ No newline at end of file diff --git a/plugins/emoji/public/images/toilet.png b/plugins/emoji/public/images/toilet.png deleted file mode 120000 index ed1dcb961a..0000000000 --- a/plugins/emoji/public/images/toilet.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6bd.png \ No newline at end of file diff --git a/plugins/emoji/public/images/tokyo_tower.png b/plugins/emoji/public/images/tokyo_tower.png deleted file mode 120000 index c1580aaeb8..0000000000 --- a/plugins/emoji/public/images/tokyo_tower.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f5fc.png \ No newline at end of file diff --git a/plugins/emoji/public/images/tomato.png b/plugins/emoji/public/images/tomato.png deleted file mode 120000 index 334a0f8a5e..0000000000 --- a/plugins/emoji/public/images/tomato.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f345.png \ No newline at end of file diff --git a/plugins/emoji/public/images/tongue.png b/plugins/emoji/public/images/tongue.png deleted file mode 120000 index ef62232804..0000000000 --- a/plugins/emoji/public/images/tongue.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f445.png \ No newline at end of file diff --git a/plugins/emoji/public/images/top.png b/plugins/emoji/public/images/top.png deleted file mode 120000 index 4a21bd707c..0000000000 --- a/plugins/emoji/public/images/top.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f51d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/tophat.png b/plugins/emoji/public/images/tophat.png deleted file mode 120000 index aea6695749..0000000000 --- a/plugins/emoji/public/images/tophat.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3a9.png \ No newline at end of file diff --git a/plugins/emoji/public/images/tractor.png b/plugins/emoji/public/images/tractor.png deleted file mode 120000 index 5ab84e76df..0000000000 --- a/plugins/emoji/public/images/tractor.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f69c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/traffic_light.png b/plugins/emoji/public/images/traffic_light.png deleted file mode 120000 index 28bd5f4ea3..0000000000 --- a/plugins/emoji/public/images/traffic_light.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6a5.png \ No newline at end of file diff --git a/plugins/emoji/public/images/train.png b/plugins/emoji/public/images/train.png deleted file mode 120000 index 856483debc..0000000000 --- a/plugins/emoji/public/images/train.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f683.png \ No newline at end of file diff --git a/plugins/emoji/public/images/train2.png b/plugins/emoji/public/images/train2.png deleted file mode 120000 index 12438cf504..0000000000 --- a/plugins/emoji/public/images/train2.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f686.png \ No newline at end of file diff --git a/plugins/emoji/public/images/tram.png b/plugins/emoji/public/images/tram.png deleted file mode 120000 index b27dea5daf..0000000000 --- a/plugins/emoji/public/images/tram.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f68a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/triangular_flag_on_post.png b/plugins/emoji/public/images/triangular_flag_on_post.png deleted file mode 120000 index e73bd8b8d6..0000000000 --- a/plugins/emoji/public/images/triangular_flag_on_post.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6a9.png \ No newline at end of file diff --git a/plugins/emoji/public/images/triangular_ruler.png b/plugins/emoji/public/images/triangular_ruler.png deleted file mode 120000 index 3d9485127a..0000000000 --- a/plugins/emoji/public/images/triangular_ruler.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4d0.png \ No newline at end of file diff --git a/plugins/emoji/public/images/trident.png b/plugins/emoji/public/images/trident.png deleted file mode 120000 index 13864beaea..0000000000 --- a/plugins/emoji/public/images/trident.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f531.png \ No newline at end of file diff --git a/plugins/emoji/public/images/triumph.png b/plugins/emoji/public/images/triumph.png deleted file mode 120000 index 9b8ec14130..0000000000 --- a/plugins/emoji/public/images/triumph.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f624.png \ No newline at end of file diff --git a/plugins/emoji/public/images/trolleybus.png b/plugins/emoji/public/images/trolleybus.png deleted file mode 120000 index 7748498e70..0000000000 --- a/plugins/emoji/public/images/trolleybus.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f68e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/trollface.png b/plugins/emoji/public/images/trollface.png deleted file mode 100755 index 5f54d28ea6..0000000000 Binary files a/plugins/emoji/public/images/trollface.png and /dev/null differ diff --git a/plugins/emoji/public/images/trophy.png b/plugins/emoji/public/images/trophy.png deleted file mode 120000 index 67253716de..0000000000 --- a/plugins/emoji/public/images/trophy.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3c6.png \ No newline at end of file diff --git a/plugins/emoji/public/images/tropical_drink.png b/plugins/emoji/public/images/tropical_drink.png deleted file mode 120000 index a15a3cd87f..0000000000 --- a/plugins/emoji/public/images/tropical_drink.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f379.png \ No newline at end of file diff --git a/plugins/emoji/public/images/tropical_fish.png b/plugins/emoji/public/images/tropical_fish.png deleted file mode 120000 index 87a4c0f59d..0000000000 --- a/plugins/emoji/public/images/tropical_fish.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f420.png \ No newline at end of file diff --git a/plugins/emoji/public/images/truck.png b/plugins/emoji/public/images/truck.png deleted file mode 120000 index 0ba116f210..0000000000 --- a/plugins/emoji/public/images/truck.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f69a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/trumpet.png b/plugins/emoji/public/images/trumpet.png deleted file mode 120000 index ef9af1c260..0000000000 --- a/plugins/emoji/public/images/trumpet.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3ba.png \ No newline at end of file diff --git a/plugins/emoji/public/images/tshirt.png b/plugins/emoji/public/images/tshirt.png deleted file mode 120000 index 205cc1b9c1..0000000000 --- a/plugins/emoji/public/images/tshirt.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f455.png \ No newline at end of file diff --git a/plugins/emoji/public/images/tulip.png b/plugins/emoji/public/images/tulip.png deleted file mode 120000 index 1cc45dba70..0000000000 --- a/plugins/emoji/public/images/tulip.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f337.png \ No newline at end of file diff --git a/plugins/emoji/public/images/turtle.png b/plugins/emoji/public/images/turtle.png deleted file mode 120000 index d1e091a766..0000000000 --- a/plugins/emoji/public/images/turtle.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f422.png \ No newline at end of file diff --git a/plugins/emoji/public/images/tv.png b/plugins/emoji/public/images/tv.png deleted file mode 120000 index 0da5f83e30..0000000000 --- a/plugins/emoji/public/images/tv.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4fa.png \ No newline at end of file diff --git a/plugins/emoji/public/images/twisted_rightwards_arrows.png b/plugins/emoji/public/images/twisted_rightwards_arrows.png deleted file mode 120000 index 87ccda233e..0000000000 --- a/plugins/emoji/public/images/twisted_rightwards_arrows.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f500.png \ No newline at end of file diff --git a/plugins/emoji/public/images/two.png b/plugins/emoji/public/images/two.png deleted file mode 120000 index 53d81a6857..0000000000 --- a/plugins/emoji/public/images/two.png +++ /dev/null @@ -1 +0,0 @@ -unicode/0032.png \ No newline at end of file diff --git a/plugins/emoji/public/images/two_hearts.png b/plugins/emoji/public/images/two_hearts.png deleted file mode 120000 index c51e30afa1..0000000000 --- a/plugins/emoji/public/images/two_hearts.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f495.png \ No newline at end of file diff --git a/plugins/emoji/public/images/two_men_holding_hands.png b/plugins/emoji/public/images/two_men_holding_hands.png deleted file mode 120000 index b46ebc125a..0000000000 --- a/plugins/emoji/public/images/two_men_holding_hands.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f46c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/two_women_holding_hands.png b/plugins/emoji/public/images/two_women_holding_hands.png deleted file mode 120000 index 3385e6aab4..0000000000 --- a/plugins/emoji/public/images/two_women_holding_hands.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f46d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/u5272.png b/plugins/emoji/public/images/u5272.png deleted file mode 120000 index 1f5347dbf2..0000000000 --- a/plugins/emoji/public/images/u5272.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f239.png \ No newline at end of file diff --git a/plugins/emoji/public/images/u5408.png b/plugins/emoji/public/images/u5408.png deleted file mode 120000 index 1e6f46f351..0000000000 --- a/plugins/emoji/public/images/u5408.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f234.png \ No newline at end of file diff --git a/plugins/emoji/public/images/u55b6.png b/plugins/emoji/public/images/u55b6.png deleted file mode 120000 index 5eb2272844..0000000000 --- a/plugins/emoji/public/images/u55b6.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f23a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/u6307.png b/plugins/emoji/public/images/u6307.png deleted file mode 120000 index 22e815a3a8..0000000000 --- a/plugins/emoji/public/images/u6307.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f22f.png \ No newline at end of file diff --git a/plugins/emoji/public/images/u6708.png b/plugins/emoji/public/images/u6708.png deleted file mode 120000 index b1ef4805d7..0000000000 --- a/plugins/emoji/public/images/u6708.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f237.png \ No newline at end of file diff --git a/plugins/emoji/public/images/u6709.png b/plugins/emoji/public/images/u6709.png deleted file mode 120000 index 95217ff965..0000000000 --- a/plugins/emoji/public/images/u6709.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f236.png \ No newline at end of file diff --git a/plugins/emoji/public/images/u6e80.png b/plugins/emoji/public/images/u6e80.png deleted file mode 120000 index 033f6a9c15..0000000000 --- a/plugins/emoji/public/images/u6e80.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f235.png \ No newline at end of file diff --git a/plugins/emoji/public/images/u7121.png b/plugins/emoji/public/images/u7121.png deleted file mode 120000 index d03b88e343..0000000000 --- a/plugins/emoji/public/images/u7121.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f21a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/u7533.png b/plugins/emoji/public/images/u7533.png deleted file mode 120000 index c587a5840e..0000000000 --- a/plugins/emoji/public/images/u7533.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f238.png \ No newline at end of file diff --git a/plugins/emoji/public/images/u7981.png b/plugins/emoji/public/images/u7981.png deleted file mode 120000 index ffe13e4991..0000000000 --- a/plugins/emoji/public/images/u7981.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f232.png \ No newline at end of file diff --git a/plugins/emoji/public/images/u7a7a.png b/plugins/emoji/public/images/u7a7a.png deleted file mode 120000 index 51d3b1b9fc..0000000000 --- a/plugins/emoji/public/images/u7a7a.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f233.png \ No newline at end of file diff --git a/plugins/emoji/public/images/uk.png b/plugins/emoji/public/images/uk.png deleted file mode 120000 index 68cbaff002..0000000000 --- a/plugins/emoji/public/images/uk.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f1ec-1f1e7.png \ No newline at end of file diff --git a/plugins/emoji/public/images/umbrella.png b/plugins/emoji/public/images/umbrella.png deleted file mode 120000 index 9546363ef2..0000000000 --- a/plugins/emoji/public/images/umbrella.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2614.png \ No newline at end of file diff --git a/plugins/emoji/public/images/unamused.png b/plugins/emoji/public/images/unamused.png deleted file mode 120000 index cd62ee34d6..0000000000 --- a/plugins/emoji/public/images/unamused.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f612.png \ No newline at end of file diff --git a/plugins/emoji/public/images/underage.png b/plugins/emoji/public/images/underage.png deleted file mode 120000 index a43951e39a..0000000000 --- a/plugins/emoji/public/images/underage.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f51e.png \ No newline at end of file diff --git a/plugins/emoji/public/images/unicode/0023-20E3.png b/plugins/emoji/public/images/unicode/0023-20E3.png deleted file mode 100644 index 2c363cc05f..0000000000 Binary files a/plugins/emoji/public/images/unicode/0023-20E3.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/0023.png b/plugins/emoji/public/images/unicode/0023.png deleted file mode 100755 index a8cd17f52f..0000000000 Binary files a/plugins/emoji/public/images/unicode/0023.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/0030-20E3.png b/plugins/emoji/public/images/unicode/0030-20E3.png deleted file mode 100644 index 0d2be8a35b..0000000000 Binary files a/plugins/emoji/public/images/unicode/0030-20E3.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/0030.png b/plugins/emoji/public/images/unicode/0030.png deleted file mode 100755 index 2ad743df0c..0000000000 Binary files a/plugins/emoji/public/images/unicode/0030.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/0031.png b/plugins/emoji/public/images/unicode/0031.png deleted file mode 100755 index d792b49ef5..0000000000 Binary files a/plugins/emoji/public/images/unicode/0031.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/0032.png b/plugins/emoji/public/images/unicode/0032.png deleted file mode 100755 index 3da772c626..0000000000 Binary files a/plugins/emoji/public/images/unicode/0032.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/0033.png b/plugins/emoji/public/images/unicode/0033.png deleted file mode 100755 index 643a596a68..0000000000 Binary files a/plugins/emoji/public/images/unicode/0033.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/0034.png b/plugins/emoji/public/images/unicode/0034.png deleted file mode 100755 index 3e83ab5aec..0000000000 Binary files a/plugins/emoji/public/images/unicode/0034.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/0035-20E3.png b/plugins/emoji/public/images/unicode/0035-20E3.png deleted file mode 100644 index efce2f1c0d..0000000000 Binary files a/plugins/emoji/public/images/unicode/0035-20E3.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/0035.png b/plugins/emoji/public/images/unicode/0035.png deleted file mode 100755 index 05dfa5385b..0000000000 Binary files a/plugins/emoji/public/images/unicode/0035.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/0036-20E3.png b/plugins/emoji/public/images/unicode/0036-20E3.png deleted file mode 100644 index b9ffcc7b7e..0000000000 Binary files a/plugins/emoji/public/images/unicode/0036-20E3.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/0036.png b/plugins/emoji/public/images/unicode/0036.png deleted file mode 100755 index 78bb810f90..0000000000 Binary files a/plugins/emoji/public/images/unicode/0036.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/0037-20E3.png b/plugins/emoji/public/images/unicode/0037-20E3.png deleted file mode 100644 index 3aa2907b17..0000000000 Binary files a/plugins/emoji/public/images/unicode/0037-20E3.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/0037.png b/plugins/emoji/public/images/unicode/0037.png deleted file mode 100755 index 3a5f8a2e63..0000000000 Binary files a/plugins/emoji/public/images/unicode/0037.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/0038-20E3.png b/plugins/emoji/public/images/unicode/0038-20E3.png deleted file mode 100644 index 03c97b5cac..0000000000 Binary files a/plugins/emoji/public/images/unicode/0038-20E3.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/0038.png b/plugins/emoji/public/images/unicode/0038.png deleted file mode 100755 index 6ddacf2285..0000000000 Binary files a/plugins/emoji/public/images/unicode/0038.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/0039-20E3.png b/plugins/emoji/public/images/unicode/0039-20E3.png deleted file mode 100644 index bcda17b359..0000000000 Binary files a/plugins/emoji/public/images/unicode/0039-20E3.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/0039.png b/plugins/emoji/public/images/unicode/0039.png deleted file mode 100755 index 92c3831fda..0000000000 Binary files a/plugins/emoji/public/images/unicode/0039.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/00ae.png b/plugins/emoji/public/images/unicode/00ae.png deleted file mode 100755 index 23f009731f..0000000000 Binary files a/plugins/emoji/public/images/unicode/00ae.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1F320.png b/plugins/emoji/public/images/unicode/1F320.png deleted file mode 100644 index adf1e79637..0000000000 Binary files a/plugins/emoji/public/images/unicode/1F320.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1F508.png b/plugins/emoji/public/images/unicode/1F508.png deleted file mode 100644 index b6fae7340d..0000000000 Binary files a/plugins/emoji/public/images/unicode/1F508.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1F68B.png b/plugins/emoji/public/images/unicode/1F68B.png deleted file mode 100644 index e7bebbf093..0000000000 Binary files a/plugins/emoji/public/images/unicode/1F68B.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f0cf.png b/plugins/emoji/public/images/unicode/1f0cf.png deleted file mode 100755 index 172dbacf1b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f0cf.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f170.png b/plugins/emoji/public/images/unicode/1f170.png deleted file mode 100755 index 08a7db4ad8..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f170.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f171.png b/plugins/emoji/public/images/unicode/1f171.png deleted file mode 100755 index 5928372770..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f171.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f17e.png b/plugins/emoji/public/images/unicode/1f17e.png deleted file mode 100755 index b4469d486d..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f17e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f18e.png b/plugins/emoji/public/images/unicode/1f18e.png deleted file mode 100755 index 649385c6d8..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f18e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f191.png b/plugins/emoji/public/images/unicode/1f191.png deleted file mode 100755 index 321be7dc62..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f191.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f192.png b/plugins/emoji/public/images/unicode/1f192.png deleted file mode 100755 index 9505a7b822..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f192.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f195.png b/plugins/emoji/public/images/unicode/1f195.png deleted file mode 100755 index e8b9a73b9d..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f195.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f196.png b/plugins/emoji/public/images/unicode/1f196.png deleted file mode 100755 index 58835cea51..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f196.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f197.png b/plugins/emoji/public/images/unicode/1f197.png deleted file mode 100755 index ffe0cfca6d..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f197.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f198.png b/plugins/emoji/public/images/unicode/1f198.png deleted file mode 100755 index 2eb1562487..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f198.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f199.png b/plugins/emoji/public/images/unicode/1f199.png deleted file mode 100755 index ac3cf55385..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f199.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f19a.png b/plugins/emoji/public/images/unicode/1f19a.png deleted file mode 100755 index 0d3329b1eb..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f19a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f1e8-1f1f3.png b/plugins/emoji/public/images/unicode/1f1e8-1f1f3.png deleted file mode 100755 index 0d6c10a9bd..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f1e8-1f1f3.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f1e9-1f1ea.png b/plugins/emoji/public/images/unicode/1f1e9-1f1ea.png deleted file mode 100755 index b6e3d38893..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f1e9-1f1ea.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f1ea-1f1f8.png b/plugins/emoji/public/images/unicode/1f1ea-1f1f8.png deleted file mode 100755 index 899f7c139e..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f1ea-1f1f8.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f1eb-1f1f7.png b/plugins/emoji/public/images/unicode/1f1eb-1f1f7.png deleted file mode 100755 index 77ee310884..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f1eb-1f1f7.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f1ec-1f1e7.png b/plugins/emoji/public/images/unicode/1f1ec-1f1e7.png deleted file mode 100755 index d6e2e7ac75..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f1ec-1f1e7.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f1ee-1f1f9.png b/plugins/emoji/public/images/unicode/1f1ee-1f1f9.png deleted file mode 100755 index a416bcca1c..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f1ee-1f1f9.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f1ef-1f1f5.png b/plugins/emoji/public/images/unicode/1f1ef-1f1f5.png deleted file mode 100755 index 449f6cc38d..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f1ef-1f1f5.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f1f0-1f1f7.png b/plugins/emoji/public/images/unicode/1f1f0-1f1f7.png deleted file mode 100755 index 9814d03210..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f1f0-1f1f7.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f1f7-1f1fa.png b/plugins/emoji/public/images/unicode/1f1f7-1f1fa.png deleted file mode 100755 index 302cdf5481..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f1f7-1f1fa.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f1fa-1f1f8.png b/plugins/emoji/public/images/unicode/1f1fa-1f1f8.png deleted file mode 100755 index a9908f7067..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f1fa-1f1f8.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f201.png b/plugins/emoji/public/images/unicode/1f201.png deleted file mode 100755 index 11dac3a88e..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f201.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f202.png b/plugins/emoji/public/images/unicode/1f202.png deleted file mode 100755 index cd55a4fb81..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f202.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f21a.png b/plugins/emoji/public/images/unicode/1f21a.png deleted file mode 100755 index 5de0124e88..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f21a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f22f.png b/plugins/emoji/public/images/unicode/1f22f.png deleted file mode 100755 index 0b351e196f..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f22f.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f232.png b/plugins/emoji/public/images/unicode/1f232.png deleted file mode 100755 index 190b3555df..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f232.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f233.png b/plugins/emoji/public/images/unicode/1f233.png deleted file mode 100755 index 2534d1e243..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f233.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f234.png b/plugins/emoji/public/images/unicode/1f234.png deleted file mode 100755 index 6fcce1ff3e..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f234.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f235.png b/plugins/emoji/public/images/unicode/1f235.png deleted file mode 100755 index 05f3eb1db7..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f235.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f236.png b/plugins/emoji/public/images/unicode/1f236.png deleted file mode 100755 index 9f0140ff79..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f236.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f237.png b/plugins/emoji/public/images/unicode/1f237.png deleted file mode 100755 index accc1f19dd..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f237.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f250.png b/plugins/emoji/public/images/unicode/1f250.png deleted file mode 100755 index 4a648587c0..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f250.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f251.png b/plugins/emoji/public/images/unicode/1f251.png deleted file mode 100755 index 973a6d9e44..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f251.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f300.png b/plugins/emoji/public/images/unicode/1f300.png deleted file mode 100755 index 2915b98e5d..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f300.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f301.png b/plugins/emoji/public/images/unicode/1f301.png deleted file mode 100755 index 8c16bdac3c..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f301.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f302.png b/plugins/emoji/public/images/unicode/1f302.png deleted file mode 100755 index 1548f6ab97..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f302.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f303.png b/plugins/emoji/public/images/unicode/1f303.png deleted file mode 100755 index e809f11a83..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f303.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f304.png b/plugins/emoji/public/images/unicode/1f304.png deleted file mode 100755 index 47dcb34933..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f304.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f305.png b/plugins/emoji/public/images/unicode/1f305.png deleted file mode 100755 index 23950d50aa..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f305.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f306.png b/plugins/emoji/public/images/unicode/1f306.png deleted file mode 100755 index a6db039f89..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f306.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f307.png b/plugins/emoji/public/images/unicode/1f307.png deleted file mode 100755 index fa34d60c5b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f307.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f308.png b/plugins/emoji/public/images/unicode/1f308.png deleted file mode 100755 index 9ea38b5751..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f308.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f309.png b/plugins/emoji/public/images/unicode/1f309.png deleted file mode 100755 index 647ce7ada4..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f309.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f30a.png b/plugins/emoji/public/images/unicode/1f30a.png deleted file mode 100755 index 58f36df71f..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f30a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f30b.png b/plugins/emoji/public/images/unicode/1f30b.png deleted file mode 100755 index 76d544f220..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f30b.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f30c.png b/plugins/emoji/public/images/unicode/1f30c.png deleted file mode 100755 index e40847d7f5..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f30c.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f30d.png b/plugins/emoji/public/images/unicode/1f30d.png deleted file mode 100755 index 5702af7c00..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f30d.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f30e.png b/plugins/emoji/public/images/unicode/1f30e.png deleted file mode 100755 index 791cfd3d58..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f30e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f30f.png b/plugins/emoji/public/images/unicode/1f30f.png deleted file mode 100755 index cd9b8f7bf0..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f30f.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f310.png b/plugins/emoji/public/images/unicode/1f310.png deleted file mode 100755 index 1b2cd44be9..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f310.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f312.png b/plugins/emoji/public/images/unicode/1f312.png deleted file mode 100755 index 570a9c44c7..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f312.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f313.png b/plugins/emoji/public/images/unicode/1f313.png deleted file mode 100755 index 18482c8bea..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f313.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f314.png b/plugins/emoji/public/images/unicode/1f314.png deleted file mode 100755 index 28c41b9242..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f314.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f316.png b/plugins/emoji/public/images/unicode/1f316.png deleted file mode 100755 index 1b8a011703..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f316.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f317.png b/plugins/emoji/public/images/unicode/1f317.png deleted file mode 100755 index 2261d378fd..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f317.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f318.png b/plugins/emoji/public/images/unicode/1f318.png deleted file mode 100755 index 96549e32d9..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f318.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f319.png b/plugins/emoji/public/images/unicode/1f319.png deleted file mode 100755 index b503e79a46..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f319.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f31a.png b/plugins/emoji/public/images/unicode/1f31a.png deleted file mode 100755 index f67e9243b3..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f31a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f31c.png b/plugins/emoji/public/images/unicode/1f31c.png deleted file mode 100755 index 64b9bd588d..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f31c.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f31d.png b/plugins/emoji/public/images/unicode/1f31d.png deleted file mode 100755 index 00b24f05ff..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f31d.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f31e.png b/plugins/emoji/public/images/unicode/1f31e.png deleted file mode 100755 index 3bcf16cc00..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f31e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f31f.png b/plugins/emoji/public/images/unicode/1f31f.png deleted file mode 100755 index 88dd6ca856..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f31f.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f330.png b/plugins/emoji/public/images/unicode/1f330.png deleted file mode 100755 index 6d5e77b77e..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f330.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f331.png b/plugins/emoji/public/images/unicode/1f331.png deleted file mode 100755 index 59adaa16fa..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f331.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f332.png b/plugins/emoji/public/images/unicode/1f332.png deleted file mode 100755 index 7669d3e9c5..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f332.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f333.png b/plugins/emoji/public/images/unicode/1f333.png deleted file mode 100755 index 4c4a536f61..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f333.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f334.png b/plugins/emoji/public/images/unicode/1f334.png deleted file mode 100755 index b751784d29..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f334.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f335.png b/plugins/emoji/public/images/unicode/1f335.png deleted file mode 100755 index 15eaaaf7a0..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f335.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f337.png b/plugins/emoji/public/images/unicode/1f337.png deleted file mode 100755 index 17ecb0ff27..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f337.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f338.png b/plugins/emoji/public/images/unicode/1f338.png deleted file mode 100755 index 8952be896a..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f338.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f339.png b/plugins/emoji/public/images/unicode/1f339.png deleted file mode 100755 index e288096ae1..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f339.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f33a.png b/plugins/emoji/public/images/unicode/1f33a.png deleted file mode 100755 index 11239138a9..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f33a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f33b.png b/plugins/emoji/public/images/unicode/1f33b.png deleted file mode 100755 index 39e20f1a52..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f33b.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f33c.png b/plugins/emoji/public/images/unicode/1f33c.png deleted file mode 100755 index 9e8cd12221..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f33c.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f33d.png b/plugins/emoji/public/images/unicode/1f33d.png deleted file mode 100755 index 98740690e9..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f33d.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f33e.png b/plugins/emoji/public/images/unicode/1f33e.png deleted file mode 100755 index 97c6885d3e..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f33e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f33f.png b/plugins/emoji/public/images/unicode/1f33f.png deleted file mode 100755 index 356ee0754a..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f33f.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f340.png b/plugins/emoji/public/images/unicode/1f340.png deleted file mode 100755 index b5dcf4693d..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f340.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f341.png b/plugins/emoji/public/images/unicode/1f341.png deleted file mode 100755 index 3d5331cf3b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f341.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f342.png b/plugins/emoji/public/images/unicode/1f342.png deleted file mode 100755 index f0e8c5e2f8..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f342.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f343.png b/plugins/emoji/public/images/unicode/1f343.png deleted file mode 100755 index d72cb352b8..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f343.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f344.png b/plugins/emoji/public/images/unicode/1f344.png deleted file mode 100755 index 3cfdc9dce4..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f344.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f345.png b/plugins/emoji/public/images/unicode/1f345.png deleted file mode 100755 index 8515465655..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f345.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f346.png b/plugins/emoji/public/images/unicode/1f346.png deleted file mode 100755 index a87c8c8d59..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f346.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f347.png b/plugins/emoji/public/images/unicode/1f347.png deleted file mode 100755 index 7228af08d0..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f347.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f348.png b/plugins/emoji/public/images/unicode/1f348.png deleted file mode 100755 index 6b7cd131f6..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f348.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f349.png b/plugins/emoji/public/images/unicode/1f349.png deleted file mode 100755 index e132b6a176..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f349.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f34a.png b/plugins/emoji/public/images/unicode/1f34a.png deleted file mode 100755 index 6594b82c1e..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f34a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f34b.png b/plugins/emoji/public/images/unicode/1f34b.png deleted file mode 100755 index 0c466cdd02..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f34b.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f34c.png b/plugins/emoji/public/images/unicode/1f34c.png deleted file mode 100755 index 8e0a0f308b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f34c.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f34d.png b/plugins/emoji/public/images/unicode/1f34d.png deleted file mode 100755 index 2ca3dae28d..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f34d.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f34e.png b/plugins/emoji/public/images/unicode/1f34e.png deleted file mode 100755 index 354be3a76a..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f34e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f34f.png b/plugins/emoji/public/images/unicode/1f34f.png deleted file mode 100755 index bda0cca92c..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f34f.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f350.png b/plugins/emoji/public/images/unicode/1f350.png deleted file mode 100755 index 9129171f52..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f350.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f351.png b/plugins/emoji/public/images/unicode/1f351.png deleted file mode 100755 index bb2ab8c113..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f351.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f352.png b/plugins/emoji/public/images/unicode/1f352.png deleted file mode 100755 index fe61bde295..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f352.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f353.png b/plugins/emoji/public/images/unicode/1f353.png deleted file mode 100755 index 4a57691a45..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f353.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f354.png b/plugins/emoji/public/images/unicode/1f354.png deleted file mode 100755 index d9a552ee23..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f354.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f355.png b/plugins/emoji/public/images/unicode/1f355.png deleted file mode 100755 index 87eafcf32e..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f355.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f356.png b/plugins/emoji/public/images/unicode/1f356.png deleted file mode 100755 index a10df451e4..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f356.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f357.png b/plugins/emoji/public/images/unicode/1f357.png deleted file mode 100755 index 814b3bb2ac..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f357.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f358.png b/plugins/emoji/public/images/unicode/1f358.png deleted file mode 100755 index 6e91b4fdb7..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f358.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f359.png b/plugins/emoji/public/images/unicode/1f359.png deleted file mode 100755 index 9d5378a238..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f359.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f35a.png b/plugins/emoji/public/images/unicode/1f35a.png deleted file mode 100755 index 4d39ff9de2..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f35a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f35b.png b/plugins/emoji/public/images/unicode/1f35b.png deleted file mode 100755 index d05296b758..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f35b.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f35c.png b/plugins/emoji/public/images/unicode/1f35c.png deleted file mode 100755 index f204bac08f..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f35c.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f35d.png b/plugins/emoji/public/images/unicode/1f35d.png deleted file mode 100755 index c0df2d386b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f35d.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f35e.png b/plugins/emoji/public/images/unicode/1f35e.png deleted file mode 100755 index b21b305963..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f35e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f360.png b/plugins/emoji/public/images/unicode/1f360.png deleted file mode 100755 index 2c5f96f9bf..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f360.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f361.png b/plugins/emoji/public/images/unicode/1f361.png deleted file mode 100755 index f3240b3824..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f361.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f362.png b/plugins/emoji/public/images/unicode/1f362.png deleted file mode 100755 index 4436ede311..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f362.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f363.png b/plugins/emoji/public/images/unicode/1f363.png deleted file mode 100755 index 9554ff4b59..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f363.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f364.png b/plugins/emoji/public/images/unicode/1f364.png deleted file mode 100755 index 8ca7ce4f11..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f364.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f365.png b/plugins/emoji/public/images/unicode/1f365.png deleted file mode 100755 index daa35f8d4b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f365.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f366.png b/plugins/emoji/public/images/unicode/1f366.png deleted file mode 100755 index cf7ec7d750..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f366.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f367.png b/plugins/emoji/public/images/unicode/1f367.png deleted file mode 100755 index 7d5b46b002..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f367.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f368.png b/plugins/emoji/public/images/unicode/1f368.png deleted file mode 100755 index cb0160ca71..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f368.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f369.png b/plugins/emoji/public/images/unicode/1f369.png deleted file mode 100755 index 197caeb0ea..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f369.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f36a.png b/plugins/emoji/public/images/unicode/1f36a.png deleted file mode 100755 index f1438a7867..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f36a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f36b.png b/plugins/emoji/public/images/unicode/1f36b.png deleted file mode 100755 index 7626d50774..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f36b.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f36c.png b/plugins/emoji/public/images/unicode/1f36c.png deleted file mode 100755 index 9099fc8689..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f36c.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f36d.png b/plugins/emoji/public/images/unicode/1f36d.png deleted file mode 100755 index faf043e3ab..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f36d.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f36e.png b/plugins/emoji/public/images/unicode/1f36e.png deleted file mode 100755 index e980d3a2b0..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f36e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f36f.png b/plugins/emoji/public/images/unicode/1f36f.png deleted file mode 100755 index 29e6b96b26..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f36f.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f370.png b/plugins/emoji/public/images/unicode/1f370.png deleted file mode 100755 index 1873b5f902..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f370.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f371.png b/plugins/emoji/public/images/unicode/1f371.png deleted file mode 100755 index 25be0770e6..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f371.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f372.png b/plugins/emoji/public/images/unicode/1f372.png deleted file mode 100755 index c30c481b2d..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f372.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f373.png b/plugins/emoji/public/images/unicode/1f373.png deleted file mode 100755 index 5a7c0a776e..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f373.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f374.png b/plugins/emoji/public/images/unicode/1f374.png deleted file mode 100755 index 99b23d603c..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f374.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f375.png b/plugins/emoji/public/images/unicode/1f375.png deleted file mode 100755 index 7f5414ee29..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f375.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f376.png b/plugins/emoji/public/images/unicode/1f376.png deleted file mode 100755 index 5899536c87..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f376.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f377.png b/plugins/emoji/public/images/unicode/1f377.png deleted file mode 100755 index 2fcbed8abc..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f377.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f378.png b/plugins/emoji/public/images/unicode/1f378.png deleted file mode 100755 index 295bfd59ca..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f378.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f379.png b/plugins/emoji/public/images/unicode/1f379.png deleted file mode 100755 index bf649930a0..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f379.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f37a.png b/plugins/emoji/public/images/unicode/1f37a.png deleted file mode 100755 index 4afae65f86..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f37a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f37b.png b/plugins/emoji/public/images/unicode/1f37b.png deleted file mode 100755 index 7fd358c0f8..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f37b.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f37c.png b/plugins/emoji/public/images/unicode/1f37c.png deleted file mode 100755 index 2dcaa73ad6..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f37c.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f380.png b/plugins/emoji/public/images/unicode/1f380.png deleted file mode 100755 index 4b55b0fa76..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f380.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f381.png b/plugins/emoji/public/images/unicode/1f381.png deleted file mode 100755 index 306729a536..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f381.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f382.png b/plugins/emoji/public/images/unicode/1f382.png deleted file mode 100755 index a47b4d4a67..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f382.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f383.png b/plugins/emoji/public/images/unicode/1f383.png deleted file mode 100755 index da6edb50d9..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f383.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f384.png b/plugins/emoji/public/images/unicode/1f384.png deleted file mode 100755 index b0ac567324..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f384.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f385.png b/plugins/emoji/public/images/unicode/1f385.png deleted file mode 100755 index 9c92e39f08..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f385.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f386.png b/plugins/emoji/public/images/unicode/1f386.png deleted file mode 100755 index c659e81ec6..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f386.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f387.png b/plugins/emoji/public/images/unicode/1f387.png deleted file mode 100755 index 27c183c7da..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f387.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f388.png b/plugins/emoji/public/images/unicode/1f388.png deleted file mode 100755 index ea256272a4..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f388.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f389.png b/plugins/emoji/public/images/unicode/1f389.png deleted file mode 100755 index 8f395b7adf..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f389.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f38a.png b/plugins/emoji/public/images/unicode/1f38a.png deleted file mode 100755 index 5d02254e4f..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f38a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f38b.png b/plugins/emoji/public/images/unicode/1f38b.png deleted file mode 100755 index af5df39943..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f38b.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f38c.png b/plugins/emoji/public/images/unicode/1f38c.png deleted file mode 100755 index dc6a02104e..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f38c.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f38d.png b/plugins/emoji/public/images/unicode/1f38d.png deleted file mode 100755 index aacefef665..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f38d.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f38e.png b/plugins/emoji/public/images/unicode/1f38e.png deleted file mode 100755 index bb168988c5..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f38e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f38f.png b/plugins/emoji/public/images/unicode/1f38f.png deleted file mode 100755 index 3eda0700d7..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f38f.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f390.png b/plugins/emoji/public/images/unicode/1f390.png deleted file mode 100755 index 3ddc61434d..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f390.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f391.png b/plugins/emoji/public/images/unicode/1f391.png deleted file mode 100755 index 073b035fac..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f391.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f392.png b/plugins/emoji/public/images/unicode/1f392.png deleted file mode 100755 index 14c60d02e8..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f392.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f393.png b/plugins/emoji/public/images/unicode/1f393.png deleted file mode 100755 index 39ed772049..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f393.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3a0.png b/plugins/emoji/public/images/unicode/1f3a0.png deleted file mode 100755 index bead4fb62a..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3a0.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3a1.png b/plugins/emoji/public/images/unicode/1f3a1.png deleted file mode 100755 index 89fb6e4389..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3a1.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3a2.png b/plugins/emoji/public/images/unicode/1f3a2.png deleted file mode 100755 index a612ea2fdd..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3a2.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3a3.png b/plugins/emoji/public/images/unicode/1f3a3.png deleted file mode 100755 index 72476d501b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3a3.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3a4.png b/plugins/emoji/public/images/unicode/1f3a4.png deleted file mode 100755 index b514bd7c2e..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3a4.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3a5.png b/plugins/emoji/public/images/unicode/1f3a5.png deleted file mode 100755 index d0a046ee6b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3a5.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3a6.png b/plugins/emoji/public/images/unicode/1f3a6.png deleted file mode 100755 index 4bb70e5eae..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3a6.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3a7.png b/plugins/emoji/public/images/unicode/1f3a7.png deleted file mode 100755 index c808eeeae4..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3a7.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3a8.png b/plugins/emoji/public/images/unicode/1f3a8.png deleted file mode 100755 index 1261d69ebe..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3a8.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3aa.png b/plugins/emoji/public/images/unicode/1f3aa.png deleted file mode 100755 index 8d0fb74956..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3aa.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3ac.png b/plugins/emoji/public/images/unicode/1f3ac.png deleted file mode 100755 index 3d3dc344f0..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3ac.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3ad.png b/plugins/emoji/public/images/unicode/1f3ad.png deleted file mode 100755 index e354d77585..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3ad.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3ae.png b/plugins/emoji/public/images/unicode/1f3ae.png deleted file mode 100755 index c770c3bab6..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3ae.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3af.png b/plugins/emoji/public/images/unicode/1f3af.png deleted file mode 100755 index 7ac6b127bc..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3af.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3b0.png b/plugins/emoji/public/images/unicode/1f3b0.png deleted file mode 100755 index f352fe6bb8..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3b0.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3b1.png b/plugins/emoji/public/images/unicode/1f3b1.png deleted file mode 100755 index babfc059f8..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3b1.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3b2.png b/plugins/emoji/public/images/unicode/1f3b2.png deleted file mode 100755 index 8a2d5d6633..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3b2.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3b3.png b/plugins/emoji/public/images/unicode/1f3b3.png deleted file mode 100755 index 5647974a90..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3b3.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3b4.png b/plugins/emoji/public/images/unicode/1f3b4.png deleted file mode 100755 index 74ec80c0fc..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3b4.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3b5.png b/plugins/emoji/public/images/unicode/1f3b5.png deleted file mode 100755 index d5f8119633..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3b5.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3b6.png b/plugins/emoji/public/images/unicode/1f3b6.png deleted file mode 100755 index fded806ce0..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3b6.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3b7.png b/plugins/emoji/public/images/unicode/1f3b7.png deleted file mode 100755 index 51ead78453..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3b7.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3b8.png b/plugins/emoji/public/images/unicode/1f3b8.png deleted file mode 100755 index 8b3225d9ce..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3b8.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3ba.png b/plugins/emoji/public/images/unicode/1f3ba.png deleted file mode 100755 index b4570a6c40..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3ba.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3bb.png b/plugins/emoji/public/images/unicode/1f3bb.png deleted file mode 100755 index 46d0da0513..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3bb.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3bc.png b/plugins/emoji/public/images/unicode/1f3bc.png deleted file mode 100755 index 3ea3e17714..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3bc.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3bd.png b/plugins/emoji/public/images/unicode/1f3bd.png deleted file mode 100755 index f7d37651b5..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3bd.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3be.png b/plugins/emoji/public/images/unicode/1f3be.png deleted file mode 100755 index 600d0af99d..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3be.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3bf.png b/plugins/emoji/public/images/unicode/1f3bf.png deleted file mode 100755 index dc95f78678..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3bf.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3c0.png b/plugins/emoji/public/images/unicode/1f3c0.png deleted file mode 100755 index 726185ce7b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3c0.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3c1.png b/plugins/emoji/public/images/unicode/1f3c1.png deleted file mode 100755 index c20c51a08e..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3c1.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3c2.png b/plugins/emoji/public/images/unicode/1f3c2.png deleted file mode 100755 index 2eaa6cd565..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3c2.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3c3.png b/plugins/emoji/public/images/unicode/1f3c3.png deleted file mode 100755 index aaeb46612a..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3c3.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3c4.png b/plugins/emoji/public/images/unicode/1f3c4.png deleted file mode 100755 index a9b4f0fae2..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3c4.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3c6.png b/plugins/emoji/public/images/unicode/1f3c6.png deleted file mode 100755 index 8811f7b75d..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3c6.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3c7.png b/plugins/emoji/public/images/unicode/1f3c7.png deleted file mode 100755 index 78161397c8..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3c7.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3c8.png b/plugins/emoji/public/images/unicode/1f3c8.png deleted file mode 100755 index d6634c5243..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3c8.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3c9.png b/plugins/emoji/public/images/unicode/1f3c9.png deleted file mode 100755 index b91fd14c52..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3c9.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3ca.png b/plugins/emoji/public/images/unicode/1f3ca.png deleted file mode 100755 index 3de265f338..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3ca.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3e0.png b/plugins/emoji/public/images/unicode/1f3e0.png deleted file mode 100755 index b941a7d13b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3e0.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3e1.png b/plugins/emoji/public/images/unicode/1f3e1.png deleted file mode 100755 index eccc9ead94..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3e1.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3e2.png b/plugins/emoji/public/images/unicode/1f3e2.png deleted file mode 100755 index 1625234d54..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3e2.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3e4.png b/plugins/emoji/public/images/unicode/1f3e4.png deleted file mode 100755 index 788ca21cce..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3e4.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3e5.png b/plugins/emoji/public/images/unicode/1f3e5.png deleted file mode 100755 index 0af2d2f2a8..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3e5.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3e6.png b/plugins/emoji/public/images/unicode/1f3e6.png deleted file mode 100755 index aa9f3e49b0..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3e6.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3e7.png b/plugins/emoji/public/images/unicode/1f3e7.png deleted file mode 100755 index 884ca71228..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3e7.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3e8.png b/plugins/emoji/public/images/unicode/1f3e8.png deleted file mode 100755 index 60d20689d9..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3e8.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3e9.png b/plugins/emoji/public/images/unicode/1f3e9.png deleted file mode 100755 index e2c5343c81..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3e9.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3ea.png b/plugins/emoji/public/images/unicode/1f3ea.png deleted file mode 100755 index 0a4cfccbd1..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3ea.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3eb.png b/plugins/emoji/public/images/unicode/1f3eb.png deleted file mode 100755 index fd0fdeff6f..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3eb.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3ec.png b/plugins/emoji/public/images/unicode/1f3ec.png deleted file mode 100755 index 49441d88e1..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3ec.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3ed.png b/plugins/emoji/public/images/unicode/1f3ed.png deleted file mode 100755 index bc34f21d36..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3ed.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3ee.png b/plugins/emoji/public/images/unicode/1f3ee.png deleted file mode 100755 index 3ec1679f3b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3ee.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f3f0.png b/plugins/emoji/public/images/unicode/1f3f0.png deleted file mode 100755 index a68a754685..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f3f0.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f400.png b/plugins/emoji/public/images/unicode/1f400.png deleted file mode 100755 index f4c215fa2a..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f400.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f401.png b/plugins/emoji/public/images/unicode/1f401.png deleted file mode 100755 index a9df277829..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f401.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f402.png b/plugins/emoji/public/images/unicode/1f402.png deleted file mode 100755 index 21c3ff1972..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f402.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f403.png b/plugins/emoji/public/images/unicode/1f403.png deleted file mode 100755 index e65b690ca5..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f403.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f404.png b/plugins/emoji/public/images/unicode/1f404.png deleted file mode 100755 index 462dad2d02..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f404.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f405.png b/plugins/emoji/public/images/unicode/1f405.png deleted file mode 100755 index 5f16efe203..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f405.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f406.png b/plugins/emoji/public/images/unicode/1f406.png deleted file mode 100755 index c6ac8a49a4..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f406.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f407.png b/plugins/emoji/public/images/unicode/1f407.png deleted file mode 100755 index 904ed34a78..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f407.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f408.png b/plugins/emoji/public/images/unicode/1f408.png deleted file mode 100755 index fda7ea0d76..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f408.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f409.png b/plugins/emoji/public/images/unicode/1f409.png deleted file mode 100755 index 7d8c29615b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f409.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f40a.png b/plugins/emoji/public/images/unicode/1f40a.png deleted file mode 100755 index 066baa1276..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f40a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f40b.png b/plugins/emoji/public/images/unicode/1f40b.png deleted file mode 100755 index 7536a776a1..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f40b.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f40c.png b/plugins/emoji/public/images/unicode/1f40c.png deleted file mode 100755 index c1cd4a08c6..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f40c.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f40d.png b/plugins/emoji/public/images/unicode/1f40d.png deleted file mode 100755 index fbf2ee783f..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f40d.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f40e.png b/plugins/emoji/public/images/unicode/1f40e.png deleted file mode 100755 index 6cf5341316..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f40e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f40f.png b/plugins/emoji/public/images/unicode/1f40f.png deleted file mode 100755 index 7a904f5627..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f40f.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f410.png b/plugins/emoji/public/images/unicode/1f410.png deleted file mode 100755 index af5e125438..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f410.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f411.png b/plugins/emoji/public/images/unicode/1f411.png deleted file mode 100755 index 4dc861537b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f411.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f412.png b/plugins/emoji/public/images/unicode/1f412.png deleted file mode 100755 index dc459c5c6c..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f412.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f413.png b/plugins/emoji/public/images/unicode/1f413.png deleted file mode 100755 index 6ec7cebd32..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f413.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f414.png b/plugins/emoji/public/images/unicode/1f414.png deleted file mode 100755 index 43aa1b851c..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f414.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f415.png b/plugins/emoji/public/images/unicode/1f415.png deleted file mode 100755 index 7ba0e3c887..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f415.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f416.png b/plugins/emoji/public/images/unicode/1f416.png deleted file mode 100755 index d38ec17096..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f416.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f417.png b/plugins/emoji/public/images/unicode/1f417.png deleted file mode 100755 index 6e97d86c46..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f417.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f418.png b/plugins/emoji/public/images/unicode/1f418.png deleted file mode 100755 index dd920221e5..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f418.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f419.png b/plugins/emoji/public/images/unicode/1f419.png deleted file mode 100755 index 4db233093d..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f419.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f41a.png b/plugins/emoji/public/images/unicode/1f41a.png deleted file mode 100755 index 2856f77c90..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f41a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f41b.png b/plugins/emoji/public/images/unicode/1f41b.png deleted file mode 100755 index 3bd9352b17..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f41b.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f41c.png b/plugins/emoji/public/images/unicode/1f41c.png deleted file mode 100755 index bee0d089e8..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f41c.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f41d.png b/plugins/emoji/public/images/unicode/1f41d.png deleted file mode 100755 index c80d7c8680..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f41d.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f41e.png b/plugins/emoji/public/images/unicode/1f41e.png deleted file mode 100755 index 70d013af59..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f41e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f41f.png b/plugins/emoji/public/images/unicode/1f41f.png deleted file mode 100755 index 087b0a8af6..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f41f.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f420.png b/plugins/emoji/public/images/unicode/1f420.png deleted file mode 100755 index 3977bff186..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f420.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f421.png b/plugins/emoji/public/images/unicode/1f421.png deleted file mode 100755 index 4e796b73fc..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f421.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f422.png b/plugins/emoji/public/images/unicode/1f422.png deleted file mode 100755 index 7bf400411a..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f422.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f423.png b/plugins/emoji/public/images/unicode/1f423.png deleted file mode 100755 index 41f747108f..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f423.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f424.png b/plugins/emoji/public/images/unicode/1f424.png deleted file mode 100755 index 729e516b96..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f424.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f425.png b/plugins/emoji/public/images/unicode/1f425.png deleted file mode 100755 index 6efb393bd7..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f425.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f426.png b/plugins/emoji/public/images/unicode/1f426.png deleted file mode 100755 index 2c2bfba605..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f426.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f427.png b/plugins/emoji/public/images/unicode/1f427.png deleted file mode 100755 index c52617f6c3..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f427.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f428.png b/plugins/emoji/public/images/unicode/1f428.png deleted file mode 100755 index 29193ced2b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f428.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f429.png b/plugins/emoji/public/images/unicode/1f429.png deleted file mode 100755 index 4f3466a185..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f429.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f42a.png b/plugins/emoji/public/images/unicode/1f42a.png deleted file mode 100755 index 6e4d66f14f..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f42a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f42b.png b/plugins/emoji/public/images/unicode/1f42b.png deleted file mode 100755 index f1810925f9..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f42b.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f42c.png b/plugins/emoji/public/images/unicode/1f42c.png deleted file mode 100755 index 9fd3771017..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f42c.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f42d.png b/plugins/emoji/public/images/unicode/1f42d.png deleted file mode 100755 index 876d772317..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f42d.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f42e.png b/plugins/emoji/public/images/unicode/1f42e.png deleted file mode 100755 index 6a3943b1fb..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f42e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f42f.png b/plugins/emoji/public/images/unicode/1f42f.png deleted file mode 100755 index 4f85d80d37..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f42f.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f430.png b/plugins/emoji/public/images/unicode/1f430.png deleted file mode 100755 index 6511276f05..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f430.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f431.png b/plugins/emoji/public/images/unicode/1f431.png deleted file mode 100755 index 90fc474eac..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f431.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f432.png b/plugins/emoji/public/images/unicode/1f432.png deleted file mode 100755 index 8bdeee8d18..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f432.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f433.png b/plugins/emoji/public/images/unicode/1f433.png deleted file mode 100755 index 8941f6a919..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f433.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f434.png b/plugins/emoji/public/images/unicode/1f434.png deleted file mode 100755 index b04fd2efeb..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f434.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f435.png b/plugins/emoji/public/images/unicode/1f435.png deleted file mode 100755 index 8389d1652c..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f435.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f436.png b/plugins/emoji/public/images/unicode/1f436.png deleted file mode 100755 index b325f1892d..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f436.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f437.png b/plugins/emoji/public/images/unicode/1f437.png deleted file mode 100755 index 6c0156cc52..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f437.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f438.png b/plugins/emoji/public/images/unicode/1f438.png deleted file mode 100755 index 520ef68685..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f438.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f439.png b/plugins/emoji/public/images/unicode/1f439.png deleted file mode 100755 index fbe4198eb8..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f439.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f43a.png b/plugins/emoji/public/images/unicode/1f43a.png deleted file mode 100755 index c572e9415c..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f43a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f43b.png b/plugins/emoji/public/images/unicode/1f43b.png deleted file mode 100755 index c7bbf6f489..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f43b.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f43c.png b/plugins/emoji/public/images/unicode/1f43c.png deleted file mode 100755 index 7b3e2ffd31..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f43c.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f43d.png b/plugins/emoji/public/images/unicode/1f43d.png deleted file mode 100755 index 4d7724348f..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f43d.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f43e.png b/plugins/emoji/public/images/unicode/1f43e.png deleted file mode 100755 index 18183aff7b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f43e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f440.png b/plugins/emoji/public/images/unicode/1f440.png deleted file mode 100755 index 9e95191f2e..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f440.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f442.png b/plugins/emoji/public/images/unicode/1f442.png deleted file mode 100755 index ebf4a1c966..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f442.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f443.png b/plugins/emoji/public/images/unicode/1f443.png deleted file mode 100755 index a59f9f9074..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f443.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f446.png b/plugins/emoji/public/images/unicode/1f446.png deleted file mode 100755 index ff8d79206c..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f446.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f447.png b/plugins/emoji/public/images/unicode/1f447.png deleted file mode 100755 index 7b082a5ce9..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f447.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f448.png b/plugins/emoji/public/images/unicode/1f448.png deleted file mode 100755 index 8fa3327ccc..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f448.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f449.png b/plugins/emoji/public/images/unicode/1f449.png deleted file mode 100755 index d6e11e21d6..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f449.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f44b.png b/plugins/emoji/public/images/unicode/1f44b.png deleted file mode 100755 index 5b2e5164c1..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f44b.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f44e.png b/plugins/emoji/public/images/unicode/1f44e.png deleted file mode 100755 index 66357f5b77..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f44e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f44f.png b/plugins/emoji/public/images/unicode/1f44f.png deleted file mode 100755 index 012b5e6e77..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f44f.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f450.png b/plugins/emoji/public/images/unicode/1f450.png deleted file mode 100755 index 8681793e54..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f450.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f451.png b/plugins/emoji/public/images/unicode/1f451.png deleted file mode 100755 index d53fc85913..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f451.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f452.png b/plugins/emoji/public/images/unicode/1f452.png deleted file mode 100755 index 09cba791f6..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f452.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f453.png b/plugins/emoji/public/images/unicode/1f453.png deleted file mode 100755 index 6a869e05c4..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f453.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f454.png b/plugins/emoji/public/images/unicode/1f454.png deleted file mode 100755 index bc4e0671e4..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f454.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f455.png b/plugins/emoji/public/images/unicode/1f455.png deleted file mode 100755 index 855ed6c732..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f455.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f456.png b/plugins/emoji/public/images/unicode/1f456.png deleted file mode 100755 index c0ebdf63d4..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f456.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f457.png b/plugins/emoji/public/images/unicode/1f457.png deleted file mode 100755 index 4c23125d56..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f457.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f458.png b/plugins/emoji/public/images/unicode/1f458.png deleted file mode 100755 index d1f5f463bb..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f458.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f459.png b/plugins/emoji/public/images/unicode/1f459.png deleted file mode 100755 index 46193638d0..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f459.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f45a.png b/plugins/emoji/public/images/unicode/1f45a.png deleted file mode 100755 index 97a03f5a90..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f45a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f45b.png b/plugins/emoji/public/images/unicode/1f45b.png deleted file mode 100755 index 68dca81e88..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f45b.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f45c.png b/plugins/emoji/public/images/unicode/1f45c.png deleted file mode 100755 index e04dc1f2b5..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f45c.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f45d.png b/plugins/emoji/public/images/unicode/1f45d.png deleted file mode 100755 index 54e613a6ff..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f45d.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f45e.png b/plugins/emoji/public/images/unicode/1f45e.png deleted file mode 100755 index b30dfafec3..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f45e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f45f.png b/plugins/emoji/public/images/unicode/1f45f.png deleted file mode 100755 index 6f6445b76c..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f45f.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f460.png b/plugins/emoji/public/images/unicode/1f460.png deleted file mode 100755 index c06a69d667..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f460.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f461.png b/plugins/emoji/public/images/unicode/1f461.png deleted file mode 100755 index 5afbf02525..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f461.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f462.png b/plugins/emoji/public/images/unicode/1f462.png deleted file mode 100755 index 0aeda991e7..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f462.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f463.png b/plugins/emoji/public/images/unicode/1f463.png deleted file mode 100755 index 7607c4450a..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f463.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f465.png b/plugins/emoji/public/images/unicode/1f465.png deleted file mode 100755 index aaeeef3c94..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f465.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f466.png b/plugins/emoji/public/images/unicode/1f466.png deleted file mode 100755 index af9c4b6d4f..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f466.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f467.png b/plugins/emoji/public/images/unicode/1f467.png deleted file mode 100755 index 55f5d0c340..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f467.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f468.png b/plugins/emoji/public/images/unicode/1f468.png deleted file mode 100755 index 55c829c4a3..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f468.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f469.png b/plugins/emoji/public/images/unicode/1f469.png deleted file mode 100755 index fe9cc7dde5..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f469.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f46a.png b/plugins/emoji/public/images/unicode/1f46a.png deleted file mode 100755 index 056bb66db3..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f46a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f46b.png b/plugins/emoji/public/images/unicode/1f46b.png deleted file mode 100755 index f0b6d5fb34..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f46b.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f46c.png b/plugins/emoji/public/images/unicode/1f46c.png deleted file mode 100755 index 88acc0ba69..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f46c.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f46d.png b/plugins/emoji/public/images/unicode/1f46d.png deleted file mode 100755 index 4dd9b22bee..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f46d.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f46e.png b/plugins/emoji/public/images/unicode/1f46e.png deleted file mode 100755 index ee62d3480e..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f46e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f46f.png b/plugins/emoji/public/images/unicode/1f46f.png deleted file mode 100755 index a9918ef2e1..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f46f.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f470.png b/plugins/emoji/public/images/unicode/1f470.png deleted file mode 100755 index fb80bb8c59..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f470.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f471.png b/plugins/emoji/public/images/unicode/1f471.png deleted file mode 100755 index abf9f8a66d..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f471.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f472.png b/plugins/emoji/public/images/unicode/1f472.png deleted file mode 100755 index 564b4f2537..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f472.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f473.png b/plugins/emoji/public/images/unicode/1f473.png deleted file mode 100755 index f68bdd0be8..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f473.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f474.png b/plugins/emoji/public/images/unicode/1f474.png deleted file mode 100755 index e2b5d52407..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f474.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f475.png b/plugins/emoji/public/images/unicode/1f475.png deleted file mode 100755 index f220f46eff..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f475.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f476.png b/plugins/emoji/public/images/unicode/1f476.png deleted file mode 100755 index 92ae89d115..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f476.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f477.png b/plugins/emoji/public/images/unicode/1f477.png deleted file mode 100755 index f5384e369f..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f477.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f478.png b/plugins/emoji/public/images/unicode/1f478.png deleted file mode 100755 index 4a605eb39b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f478.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f479.png b/plugins/emoji/public/images/unicode/1f479.png deleted file mode 100755 index f76e040e9c..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f479.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f47a.png b/plugins/emoji/public/images/unicode/1f47a.png deleted file mode 100755 index 074e2a565b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f47a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f47b.png b/plugins/emoji/public/images/unicode/1f47b.png deleted file mode 100755 index 8e65a2e946..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f47b.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f47c.png b/plugins/emoji/public/images/unicode/1f47c.png deleted file mode 100755 index 9673a51e7b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f47c.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f47d.png b/plugins/emoji/public/images/unicode/1f47d.png deleted file mode 100755 index adf816085e..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f47d.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f47e.png b/plugins/emoji/public/images/unicode/1f47e.png deleted file mode 100755 index 9116414ed7..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f47e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f47f.png b/plugins/emoji/public/images/unicode/1f47f.png deleted file mode 100755 index 9dca38ce6b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f47f.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f480.png b/plugins/emoji/public/images/unicode/1f480.png deleted file mode 100755 index 37dc580516..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f480.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f481.png b/plugins/emoji/public/images/unicode/1f481.png deleted file mode 100755 index 8216e79944..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f481.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f482.png b/plugins/emoji/public/images/unicode/1f482.png deleted file mode 100755 index 2726a029b7..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f482.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f483.png b/plugins/emoji/public/images/unicode/1f483.png deleted file mode 100755 index 8c4ab977c7..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f483.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f484.png b/plugins/emoji/public/images/unicode/1f484.png deleted file mode 100755 index 986f4e8048..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f484.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f485.png b/plugins/emoji/public/images/unicode/1f485.png deleted file mode 100755 index 2182f09a46..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f485.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f486.png b/plugins/emoji/public/images/unicode/1f486.png deleted file mode 100755 index be9a042165..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f486.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f487.png b/plugins/emoji/public/images/unicode/1f487.png deleted file mode 100755 index 5c31b5006f..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f487.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f489.png b/plugins/emoji/public/images/unicode/1f489.png deleted file mode 100755 index facc07aa71..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f489.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f48a.png b/plugins/emoji/public/images/unicode/1f48a.png deleted file mode 100755 index 4e28263201..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f48a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f48b.png b/plugins/emoji/public/images/unicode/1f48b.png deleted file mode 100755 index f5d206de29..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f48b.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f48c.png b/plugins/emoji/public/images/unicode/1f48c.png deleted file mode 100755 index c505b30255..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f48c.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f48d.png b/plugins/emoji/public/images/unicode/1f48d.png deleted file mode 100755 index 1f6f682956..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f48d.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f48e.png b/plugins/emoji/public/images/unicode/1f48e.png deleted file mode 100755 index 2f293f10cb..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f48e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f48f.png b/plugins/emoji/public/images/unicode/1f48f.png deleted file mode 100755 index 68b6e688e7..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f48f.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f490.png b/plugins/emoji/public/images/unicode/1f490.png deleted file mode 100755 index fcc3c1ec75..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f490.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f491.png b/plugins/emoji/public/images/unicode/1f491.png deleted file mode 100755 index 431456bb45..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f491.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f492.png b/plugins/emoji/public/images/unicode/1f492.png deleted file mode 100755 index cd22aa41ad..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f492.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f493.png b/plugins/emoji/public/images/unicode/1f493.png deleted file mode 100755 index ec98b24c1b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f493.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f496.png b/plugins/emoji/public/images/unicode/1f496.png deleted file mode 100755 index 419bd8f5cf..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f496.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f497.png b/plugins/emoji/public/images/unicode/1f497.png deleted file mode 100755 index ce532234c1..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f497.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f498.png b/plugins/emoji/public/images/unicode/1f498.png deleted file mode 100755 index 23c1ef104f..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f498.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f49d.png b/plugins/emoji/public/images/unicode/1f49d.png deleted file mode 100755 index 59a4044bc5..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f49d.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f49e.png b/plugins/emoji/public/images/unicode/1f49e.png deleted file mode 100755 index 6b80fa5935..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f49e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4a0.png b/plugins/emoji/public/images/unicode/1f4a0.png deleted file mode 100755 index 05cf514bac..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4a0.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4a1.png b/plugins/emoji/public/images/unicode/1f4a1.png deleted file mode 100755 index 2f82960bd9..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4a1.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4a2.png b/plugins/emoji/public/images/unicode/1f4a2.png deleted file mode 100755 index 3b78fec84b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4a2.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4a3.png b/plugins/emoji/public/images/unicode/1f4a3.png deleted file mode 100755 index 869f835778..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4a3.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4a5.png b/plugins/emoji/public/images/unicode/1f4a5.png deleted file mode 100755 index 036a9485a9..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4a5.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4a6.png b/plugins/emoji/public/images/unicode/1f4a6.png deleted file mode 100755 index 090fabb722..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4a6.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4a7.png b/plugins/emoji/public/images/unicode/1f4a7.png deleted file mode 100755 index 8c140e2956..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4a7.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4a8.png b/plugins/emoji/public/images/unicode/1f4a8.png deleted file mode 100755 index 6b5fbbc08b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4a8.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4a9.png b/plugins/emoji/public/images/unicode/1f4a9.png deleted file mode 100755 index 92c9f16d58..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4a9.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4ab.png b/plugins/emoji/public/images/unicode/1f4ab.png deleted file mode 100755 index 7cb9616277..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4ab.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4ac.png b/plugins/emoji/public/images/unicode/1f4ac.png deleted file mode 100755 index 2ada0f598a..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4ac.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4b0.png b/plugins/emoji/public/images/unicode/1f4b0.png deleted file mode 100755 index 277438153a..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4b0.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4b1.png b/plugins/emoji/public/images/unicode/1f4b1.png deleted file mode 100755 index 7badcceab9..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4b1.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4b2.png b/plugins/emoji/public/images/unicode/1f4b2.png deleted file mode 100755 index 8c8a26ba8f..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4b2.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4b3.png b/plugins/emoji/public/images/unicode/1f4b3.png deleted file mode 100755 index 551cfcda02..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4b3.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4b4.png b/plugins/emoji/public/images/unicode/1f4b4.png deleted file mode 100755 index 328ed9238c..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4b4.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4b5.png b/plugins/emoji/public/images/unicode/1f4b5.png deleted file mode 100755 index 9596fe2f26..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4b5.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4b6.png b/plugins/emoji/public/images/unicode/1f4b6.png deleted file mode 100755 index 7daa5d7ee4..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4b6.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4b7.png b/plugins/emoji/public/images/unicode/1f4b7.png deleted file mode 100755 index 0ed732c40c..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4b7.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4b8.png b/plugins/emoji/public/images/unicode/1f4b8.png deleted file mode 100755 index b7b346a1ed..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4b8.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4b9.png b/plugins/emoji/public/images/unicode/1f4b9.png deleted file mode 100755 index a2f698f845..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4b9.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4ba.png b/plugins/emoji/public/images/unicode/1f4ba.png deleted file mode 100755 index bdb6feb2d1..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4ba.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4bb.png b/plugins/emoji/public/images/unicode/1f4bb.png deleted file mode 100755 index 1a57589036..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4bb.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4bc.png b/plugins/emoji/public/images/unicode/1f4bc.png deleted file mode 100755 index 881b8468a0..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4bc.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4bf.png b/plugins/emoji/public/images/unicode/1f4bf.png deleted file mode 100755 index 9af52ab334..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4bf.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4c0.png b/plugins/emoji/public/images/unicode/1f4c0.png deleted file mode 100755 index 85bf357924..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4c0.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4c1.png b/plugins/emoji/public/images/unicode/1f4c1.png deleted file mode 100755 index ebb1e453d2..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4c1.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4c2.png b/plugins/emoji/public/images/unicode/1f4c2.png deleted file mode 100755 index f3e4589cba..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4c2.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4c4.png b/plugins/emoji/public/images/unicode/1f4c4.png deleted file mode 100755 index 9d8e7ff6be..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4c4.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4c5.png b/plugins/emoji/public/images/unicode/1f4c5.png deleted file mode 100755 index 5dd1d08004..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4c5.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4c6.png b/plugins/emoji/public/images/unicode/1f4c6.png deleted file mode 100755 index c16c147bff..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4c6.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4c7.png b/plugins/emoji/public/images/unicode/1f4c7.png deleted file mode 100755 index 57a686e3d3..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4c7.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4c8.png b/plugins/emoji/public/images/unicode/1f4c8.png deleted file mode 100755 index bb1fa4d3a6..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4c8.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4c9.png b/plugins/emoji/public/images/unicode/1f4c9.png deleted file mode 100755 index ba2d64df92..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4c9.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4cb.png b/plugins/emoji/public/images/unicode/1f4cb.png deleted file mode 100755 index 1eebd97f5d..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4cb.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4cc.png b/plugins/emoji/public/images/unicode/1f4cc.png deleted file mode 100755 index 899b9312e9..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4cc.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4cd.png b/plugins/emoji/public/images/unicode/1f4cd.png deleted file mode 100755 index ec4db5b251..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4cd.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4ce.png b/plugins/emoji/public/images/unicode/1f4ce.png deleted file mode 100755 index a12d7bdb04..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4ce.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4cf.png b/plugins/emoji/public/images/unicode/1f4cf.png deleted file mode 100755 index 3c31200f15..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4cf.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4d1.png b/plugins/emoji/public/images/unicode/1f4d1.png deleted file mode 100755 index 8e67e72e3e..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4d1.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4d2.png b/plugins/emoji/public/images/unicode/1f4d2.png deleted file mode 100755 index 741f57f47b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4d2.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4d3.png b/plugins/emoji/public/images/unicode/1f4d3.png deleted file mode 100755 index e273c8a1f1..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4d3.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4d4.png b/plugins/emoji/public/images/unicode/1f4d4.png deleted file mode 100755 index dd6bf116ab..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4d4.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4d5.png b/plugins/emoji/public/images/unicode/1f4d5.png deleted file mode 100755 index e68ecda0e1..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4d5.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4d6.png b/plugins/emoji/public/images/unicode/1f4d6.png deleted file mode 100755 index 2b4700f0aa..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4d6.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4d7.png b/plugins/emoji/public/images/unicode/1f4d7.png deleted file mode 100755 index 83a54c5c07..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4d7.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4d8.png b/plugins/emoji/public/images/unicode/1f4d8.png deleted file mode 100755 index 3fc41997dd..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4d8.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4d9.png b/plugins/emoji/public/images/unicode/1f4d9.png deleted file mode 100755 index 1d9782acb0..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4d9.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4da.png b/plugins/emoji/public/images/unicode/1f4da.png deleted file mode 100755 index 612a2983c6..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4da.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4db.png b/plugins/emoji/public/images/unicode/1f4db.png deleted file mode 100755 index 5edcc6e0e5..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4db.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4dc.png b/plugins/emoji/public/images/unicode/1f4dc.png deleted file mode 100755 index f73d9f0208..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4dc.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4dd.png b/plugins/emoji/public/images/unicode/1f4dd.png deleted file mode 100755 index 44baeeb987..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4dd.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4de.png b/plugins/emoji/public/images/unicode/1f4de.png deleted file mode 100755 index d791af9c6a..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4de.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4df.png b/plugins/emoji/public/images/unicode/1f4df.png deleted file mode 100755 index 0bd43d7175..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4df.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4e0.png b/plugins/emoji/public/images/unicode/1f4e0.png deleted file mode 100755 index b25f17ce56..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4e0.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4e1.png b/plugins/emoji/public/images/unicode/1f4e1.png deleted file mode 100755 index 1e2c0f0a91..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4e1.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4e2.png b/plugins/emoji/public/images/unicode/1f4e2.png deleted file mode 100755 index 8b0267f32d..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4e2.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4e3.png b/plugins/emoji/public/images/unicode/1f4e3.png deleted file mode 100755 index c1822a86bf..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4e3.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4e4.png b/plugins/emoji/public/images/unicode/1f4e4.png deleted file mode 100755 index 4ff4e10484..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4e4.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4e5.png b/plugins/emoji/public/images/unicode/1f4e5.png deleted file mode 100755 index 807013c30c..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4e5.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4e6.png b/plugins/emoji/public/images/unicode/1f4e6.png deleted file mode 100755 index 1a0bd75a97..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4e6.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4e7.png b/plugins/emoji/public/images/unicode/1f4e7.png deleted file mode 100755 index a1a2e59dbe..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4e7.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4e9.png b/plugins/emoji/public/images/unicode/1f4e9.png deleted file mode 100755 index 3b59cc15e2..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4e9.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4eb.png b/plugins/emoji/public/images/unicode/1f4eb.png deleted file mode 100755 index f551f0ec2f..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4eb.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4ec.png b/plugins/emoji/public/images/unicode/1f4ec.png deleted file mode 100755 index 71e8f1b9fa..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4ec.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4ef.png b/plugins/emoji/public/images/unicode/1f4ef.png deleted file mode 100755 index 9f0d88c858..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4ef.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4f0.png b/plugins/emoji/public/images/unicode/1f4f0.png deleted file mode 100755 index 7b3ecf319b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4f0.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4f2.png b/plugins/emoji/public/images/unicode/1f4f2.png deleted file mode 100755 index 8f570101fc..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4f2.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4f3.png b/plugins/emoji/public/images/unicode/1f4f3.png deleted file mode 100755 index 413ff1e6bd..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4f3.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4f4.png b/plugins/emoji/public/images/unicode/1f4f4.png deleted file mode 100755 index 217ce99306..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4f4.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4f6.png b/plugins/emoji/public/images/unicode/1f4f6.png deleted file mode 100755 index 81725cbdd5..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4f6.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4f7.png b/plugins/emoji/public/images/unicode/1f4f7.png deleted file mode 100755 index 72aee52626..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4f7.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4fa.png b/plugins/emoji/public/images/unicode/1f4fa.png deleted file mode 100755 index 3497d596d7..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4fa.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4fb.png b/plugins/emoji/public/images/unicode/1f4fb.png deleted file mode 100755 index 21b48b234d..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4fb.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f4fc.png b/plugins/emoji/public/images/unicode/1f4fc.png deleted file mode 100755 index d72ad5d0be..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f4fc.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f500.png b/plugins/emoji/public/images/unicode/1f500.png deleted file mode 100755 index 95e6617d75..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f500.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f501.png b/plugins/emoji/public/images/unicode/1f501.png deleted file mode 100755 index e8e42ee5a0..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f501.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f502.png b/plugins/emoji/public/images/unicode/1f502.png deleted file mode 100755 index 49469a3c55..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f502.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f504.png b/plugins/emoji/public/images/unicode/1f504.png deleted file mode 100755 index 6d16cb2d6b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f504.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f505.png b/plugins/emoji/public/images/unicode/1f505.png deleted file mode 100755 index 9cd751eca7..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f505.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f506.png b/plugins/emoji/public/images/unicode/1f506.png deleted file mode 100755 index 39156db0eb..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f506.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f507.png b/plugins/emoji/public/images/unicode/1f507.png deleted file mode 100755 index 4a3c453e55..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f507.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f509.png b/plugins/emoji/public/images/unicode/1f509.png deleted file mode 100755 index 183429003c..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f509.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f50a.png b/plugins/emoji/public/images/unicode/1f50a.png deleted file mode 100755 index 7a067df91f..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f50a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f50b.png b/plugins/emoji/public/images/unicode/1f50b.png deleted file mode 100755 index e3f3352d3a..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f50b.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f50d.png b/plugins/emoji/public/images/unicode/1f50d.png deleted file mode 100755 index d7eab28534..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f50d.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f50e.png b/plugins/emoji/public/images/unicode/1f50e.png deleted file mode 100755 index 9cee6c13d4..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f50e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f50f.png b/plugins/emoji/public/images/unicode/1f50f.png deleted file mode 100755 index 7df1046c94..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f50f.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f510.png b/plugins/emoji/public/images/unicode/1f510.png deleted file mode 100755 index 4c48e04cea..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f510.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f511.png b/plugins/emoji/public/images/unicode/1f511.png deleted file mode 100755 index 6b9e8c48ad..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f511.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f512.png b/plugins/emoji/public/images/unicode/1f512.png deleted file mode 100755 index 381f5f2280..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f512.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f513.png b/plugins/emoji/public/images/unicode/1f513.png deleted file mode 100755 index 186847b0f8..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f513.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f514.png b/plugins/emoji/public/images/unicode/1f514.png deleted file mode 100755 index 4b39a481c7..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f514.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f515.png b/plugins/emoji/public/images/unicode/1f515.png deleted file mode 100755 index c1384cdae3..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f515.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f516.png b/plugins/emoji/public/images/unicode/1f516.png deleted file mode 100755 index 606f49e193..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f516.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f517.png b/plugins/emoji/public/images/unicode/1f517.png deleted file mode 100755 index e30d40a9dd..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f517.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f519.png b/plugins/emoji/public/images/unicode/1f519.png deleted file mode 100755 index aa5be6a550..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f519.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f51a.png b/plugins/emoji/public/images/unicode/1f51a.png deleted file mode 100755 index 378d7e784f..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f51a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f51b.png b/plugins/emoji/public/images/unicode/1f51b.png deleted file mode 100755 index f6b86e3751..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f51b.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f51d.png b/plugins/emoji/public/images/unicode/1f51d.png deleted file mode 100755 index 242354b1ae..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f51d.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f51e.png b/plugins/emoji/public/images/unicode/1f51e.png deleted file mode 100755 index 7dc0d82588..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f51e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f51f.png b/plugins/emoji/public/images/unicode/1f51f.png deleted file mode 100755 index 7f01148b1e..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f51f.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f520.png b/plugins/emoji/public/images/unicode/1f520.png deleted file mode 100755 index 1ca9cd7d13..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f520.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f521.png b/plugins/emoji/public/images/unicode/1f521.png deleted file mode 100755 index 9e8c35b605..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f521.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f522.png b/plugins/emoji/public/images/unicode/1f522.png deleted file mode 100755 index 06656eee77..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f522.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f523.png b/plugins/emoji/public/images/unicode/1f523.png deleted file mode 100755 index b4c222b2f0..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f523.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f524.png b/plugins/emoji/public/images/unicode/1f524.png deleted file mode 100755 index 48a9f5cc39..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f524.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f525.png b/plugins/emoji/public/images/unicode/1f525.png deleted file mode 100755 index ac9a7debc7..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f525.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f526.png b/plugins/emoji/public/images/unicode/1f526.png deleted file mode 100755 index 964ef2b116..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f526.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f527.png b/plugins/emoji/public/images/unicode/1f527.png deleted file mode 100755 index c0a296b473..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f527.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f528.png b/plugins/emoji/public/images/unicode/1f528.png deleted file mode 100755 index eb3bb4e752..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f528.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f529.png b/plugins/emoji/public/images/unicode/1f529.png deleted file mode 100755 index 5cd1c8730c..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f529.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f52a.png b/plugins/emoji/public/images/unicode/1f52a.png deleted file mode 100755 index 9646552838..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f52a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f52b.png b/plugins/emoji/public/images/unicode/1f52b.png deleted file mode 100755 index c596962138..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f52b.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f52c.png b/plugins/emoji/public/images/unicode/1f52c.png deleted file mode 100755 index 812fa4822a..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f52c.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f52d.png b/plugins/emoji/public/images/unicode/1f52d.png deleted file mode 100755 index 8e390019f0..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f52d.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f52e.png b/plugins/emoji/public/images/unicode/1f52e.png deleted file mode 100755 index 2635056f61..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f52e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f52f.png b/plugins/emoji/public/images/unicode/1f52f.png deleted file mode 100755 index 34fd6c29a7..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f52f.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f530.png b/plugins/emoji/public/images/unicode/1f530.png deleted file mode 100755 index 65e64c3198..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f530.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f531.png b/plugins/emoji/public/images/unicode/1f531.png deleted file mode 100755 index 3acb811a67..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f531.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f532.png b/plugins/emoji/public/images/unicode/1f532.png deleted file mode 100755 index 89887951f3..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f532.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f534.png b/plugins/emoji/public/images/unicode/1f534.png deleted file mode 100755 index d88da99d02..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f534.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f535.png b/plugins/emoji/public/images/unicode/1f535.png deleted file mode 100755 index 7923de9150..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f535.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f536.png b/plugins/emoji/public/images/unicode/1f536.png deleted file mode 100755 index 42e1a8d8a9..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f536.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f537.png b/plugins/emoji/public/images/unicode/1f537.png deleted file mode 100755 index 02d5ca4166..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f537.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f538.png b/plugins/emoji/public/images/unicode/1f538.png deleted file mode 100755 index 326ed4e29a..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f538.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f539.png b/plugins/emoji/public/images/unicode/1f539.png deleted file mode 100755 index a6dfb5f505..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f539.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f53a.png b/plugins/emoji/public/images/unicode/1f53a.png deleted file mode 100755 index 0d55efc323..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f53a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f53b.png b/plugins/emoji/public/images/unicode/1f53b.png deleted file mode 100755 index 36084fb77a..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f53b.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f53c.png b/plugins/emoji/public/images/unicode/1f53c.png deleted file mode 100755 index b5cbe7f5ed..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f53c.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f53d.png b/plugins/emoji/public/images/unicode/1f53d.png deleted file mode 100755 index e33206d992..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f53d.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f550.png b/plugins/emoji/public/images/unicode/1f550.png deleted file mode 100755 index b0d2927550..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f550.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f551.png b/plugins/emoji/public/images/unicode/1f551.png deleted file mode 100755 index 1eb32162aa..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f551.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f552.png b/plugins/emoji/public/images/unicode/1f552.png deleted file mode 100755 index d96832264e..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f552.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f553.png b/plugins/emoji/public/images/unicode/1f553.png deleted file mode 100755 index b09afa71a1..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f553.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f554.png b/plugins/emoji/public/images/unicode/1f554.png deleted file mode 100755 index 0e9a5819ca..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f554.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f555.png b/plugins/emoji/public/images/unicode/1f555.png deleted file mode 100755 index 071fb3b1bf..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f555.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f556.png b/plugins/emoji/public/images/unicode/1f556.png deleted file mode 100755 index 964f50b72f..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f556.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f557.png b/plugins/emoji/public/images/unicode/1f557.png deleted file mode 100755 index 34c520f861..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f557.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f558.png b/plugins/emoji/public/images/unicode/1f558.png deleted file mode 100755 index 1c2659a257..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f558.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f559.png b/plugins/emoji/public/images/unicode/1f559.png deleted file mode 100755 index c7f482f2a1..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f559.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f55a.png b/plugins/emoji/public/images/unicode/1f55a.png deleted file mode 100755 index 60d4719be8..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f55a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f55b.png b/plugins/emoji/public/images/unicode/1f55b.png deleted file mode 100755 index f1cd38ef3b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f55b.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f55c.png b/plugins/emoji/public/images/unicode/1f55c.png deleted file mode 100755 index 73f194f0f1..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f55c.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f55d.png b/plugins/emoji/public/images/unicode/1f55d.png deleted file mode 100755 index b2f74d2286..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f55d.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f55e.png b/plugins/emoji/public/images/unicode/1f55e.png deleted file mode 100755 index dca8d6fd55..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f55e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f55f.png b/plugins/emoji/public/images/unicode/1f55f.png deleted file mode 100755 index 3153a57fb1..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f55f.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f560.png b/plugins/emoji/public/images/unicode/1f560.png deleted file mode 100755 index 5582f0db86..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f560.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f561.png b/plugins/emoji/public/images/unicode/1f561.png deleted file mode 100755 index 4bcc4a3289..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f561.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f562.png b/plugins/emoji/public/images/unicode/1f562.png deleted file mode 100755 index 4be60370da..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f562.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f563.png b/plugins/emoji/public/images/unicode/1f563.png deleted file mode 100755 index 03a738dfc9..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f563.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f564.png b/plugins/emoji/public/images/unicode/1f564.png deleted file mode 100755 index 123dc1292a..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f564.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f565.png b/plugins/emoji/public/images/unicode/1f565.png deleted file mode 100755 index 6053323357..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f565.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f566.png b/plugins/emoji/public/images/unicode/1f566.png deleted file mode 100755 index a793710c9d..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f566.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f567.png b/plugins/emoji/public/images/unicode/1f567.png deleted file mode 100755 index d62fa40135..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f567.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f5fc.png b/plugins/emoji/public/images/unicode/1f5fc.png deleted file mode 100755 index 2b3ce12b6f..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f5fc.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f5fd.png b/plugins/emoji/public/images/unicode/1f5fd.png deleted file mode 100755 index 63f559dc2b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f5fd.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f5fe.png b/plugins/emoji/public/images/unicode/1f5fe.png deleted file mode 100755 index cede339007..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f5fe.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f5ff.png b/plugins/emoji/public/images/unicode/1f5ff.png deleted file mode 100755 index 0f7d9f712e..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f5ff.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f600.png b/plugins/emoji/public/images/unicode/1f600.png deleted file mode 100755 index d54cc5d634..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f600.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f601.png b/plugins/emoji/public/images/unicode/1f601.png deleted file mode 100755 index b3e9facebe..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f601.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f602.png b/plugins/emoji/public/images/unicode/1f602.png deleted file mode 100755 index dc059f6640..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f602.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f603.png b/plugins/emoji/public/images/unicode/1f603.png deleted file mode 100755 index e8e499127b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f603.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f604.png b/plugins/emoji/public/images/unicode/1f604.png deleted file mode 100755 index 67b981a5f0..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f604.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f605.png b/plugins/emoji/public/images/unicode/1f605.png deleted file mode 100755 index 01c4a7e0f6..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f605.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f606.png b/plugins/emoji/public/images/unicode/1f606.png deleted file mode 100755 index 22f4d66ba4..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f606.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f607.png b/plugins/emoji/public/images/unicode/1f607.png deleted file mode 100755 index 23c1344ed4..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f607.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f608.png b/plugins/emoji/public/images/unicode/1f608.png deleted file mode 100755 index 25f5ebf52c..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f608.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f609.png b/plugins/emoji/public/images/unicode/1f609.png deleted file mode 100755 index e32171de6f..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f609.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f60a.png b/plugins/emoji/public/images/unicode/1f60a.png deleted file mode 100755 index f7ee8aaf83..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f60a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f60b.png b/plugins/emoji/public/images/unicode/1f60b.png deleted file mode 100755 index 73401eaff4..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f60b.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f60c.png b/plugins/emoji/public/images/unicode/1f60c.png deleted file mode 100755 index 192b63bbd2..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f60c.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f60d.png b/plugins/emoji/public/images/unicode/1f60d.png deleted file mode 100755 index 2840d575f5..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f60d.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f60e.png b/plugins/emoji/public/images/unicode/1f60e.png deleted file mode 100755 index 37e55e35b6..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f60e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f60f.png b/plugins/emoji/public/images/unicode/1f60f.png deleted file mode 100755 index 3b29fe7a8a..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f60f.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f610.png b/plugins/emoji/public/images/unicode/1f610.png deleted file mode 100755 index 099cf9ddd2..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f610.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f611.png b/plugins/emoji/public/images/unicode/1f611.png deleted file mode 100755 index dd99d38a4d..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f611.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f612.png b/plugins/emoji/public/images/unicode/1f612.png deleted file mode 100755 index 34b1084c35..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f612.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f613.png b/plugins/emoji/public/images/unicode/1f613.png deleted file mode 100755 index 137e65c577..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f613.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f614.png b/plugins/emoji/public/images/unicode/1f614.png deleted file mode 100755 index 4bfd94da2b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f614.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f615.png b/plugins/emoji/public/images/unicode/1f615.png deleted file mode 100755 index 17dd8ea86b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f615.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f616.png b/plugins/emoji/public/images/unicode/1f616.png deleted file mode 100755 index 096de58334..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f616.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f617.png b/plugins/emoji/public/images/unicode/1f617.png deleted file mode 100755 index 505db6a1f8..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f617.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f618.png b/plugins/emoji/public/images/unicode/1f618.png deleted file mode 100755 index d9adf4f629..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f618.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f619.png b/plugins/emoji/public/images/unicode/1f619.png deleted file mode 100755 index dce6582133..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f619.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f61a.png b/plugins/emoji/public/images/unicode/1f61a.png deleted file mode 100755 index 15fe5e6d26..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f61a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f61b.png b/plugins/emoji/public/images/unicode/1f61b.png deleted file mode 100755 index 6187b68164..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f61b.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f61c.png b/plugins/emoji/public/images/unicode/1f61c.png deleted file mode 100755 index bf6131728b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f61c.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f61d.png b/plugins/emoji/public/images/unicode/1f61d.png deleted file mode 100755 index 3f5c503398..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f61d.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f61e.png b/plugins/emoji/public/images/unicode/1f61e.png deleted file mode 100755 index 4b85dc60e8..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f61e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f61f.png b/plugins/emoji/public/images/unicode/1f61f.png deleted file mode 100755 index 1ef02913f3..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f61f.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f620.png b/plugins/emoji/public/images/unicode/1f620.png deleted file mode 100755 index 3029c96f15..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f620.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f621.png b/plugins/emoji/public/images/unicode/1f621.png deleted file mode 100755 index 7f863faed2..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f621.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f622.png b/plugins/emoji/public/images/unicode/1f622.png deleted file mode 100755 index 406d213e02..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f622.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f623.png b/plugins/emoji/public/images/unicode/1f623.png deleted file mode 100755 index 5ef9458b5c..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f623.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f624.png b/plugins/emoji/public/images/unicode/1f624.png deleted file mode 100755 index b919ff0296..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f624.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f625.png b/plugins/emoji/public/images/unicode/1f625.png deleted file mode 100755 index 2e82ead119..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f625.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f626.png b/plugins/emoji/public/images/unicode/1f626.png deleted file mode 100755 index 943de036de..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f626.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f627.png b/plugins/emoji/public/images/unicode/1f627.png deleted file mode 100755 index 3ac78e2fd5..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f627.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f628.png b/plugins/emoji/public/images/unicode/1f628.png deleted file mode 100755 index e2af99cf90..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f628.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f629.png b/plugins/emoji/public/images/unicode/1f629.png deleted file mode 100755 index 93f62abb21..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f629.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f62a.png b/plugins/emoji/public/images/unicode/1f62a.png deleted file mode 100755 index 901be3fe07..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f62a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f62b.png b/plugins/emoji/public/images/unicode/1f62b.png deleted file mode 100755 index 3f22ae16f9..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f62b.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f62c.png b/plugins/emoji/public/images/unicode/1f62c.png deleted file mode 100755 index cb1116b519..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f62c.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f62d.png b/plugins/emoji/public/images/unicode/1f62d.png deleted file mode 100755 index 108eb0bb19..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f62d.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f62e.png b/plugins/emoji/public/images/unicode/1f62e.png deleted file mode 100755 index 7801186139..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f62e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f62f.png b/plugins/emoji/public/images/unicode/1f62f.png deleted file mode 100755 index 60a82d4bb8..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f62f.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f630.png b/plugins/emoji/public/images/unicode/1f630.png deleted file mode 100755 index 4bb2b1b98d..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f630.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f631.png b/plugins/emoji/public/images/unicode/1f631.png deleted file mode 100755 index f327bc1f14..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f631.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f632.png b/plugins/emoji/public/images/unicode/1f632.png deleted file mode 100755 index 3c805da81e..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f632.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f633.png b/plugins/emoji/public/images/unicode/1f633.png deleted file mode 100755 index c084896f89..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f633.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f634.png b/plugins/emoji/public/images/unicode/1f634.png deleted file mode 100755 index d5d188abf6..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f634.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f635.png b/plugins/emoji/public/images/unicode/1f635.png deleted file mode 100755 index d1054a004e..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f635.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f636.png b/plugins/emoji/public/images/unicode/1f636.png deleted file mode 100755 index 69be7e48e4..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f636.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f637.png b/plugins/emoji/public/images/unicode/1f637.png deleted file mode 100755 index 51b3e83e8b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f637.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f638.png b/plugins/emoji/public/images/unicode/1f638.png deleted file mode 100755 index 1954f7422d..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f638.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f639.png b/plugins/emoji/public/images/unicode/1f639.png deleted file mode 100755 index 6116146a04..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f639.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f63a.png b/plugins/emoji/public/images/unicode/1f63a.png deleted file mode 100755 index 2672f015da..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f63a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f63b.png b/plugins/emoji/public/images/unicode/1f63b.png deleted file mode 100755 index 204446090f..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f63b.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f63c.png b/plugins/emoji/public/images/unicode/1f63c.png deleted file mode 100755 index 423b843cfe..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f63c.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f63d.png b/plugins/emoji/public/images/unicode/1f63d.png deleted file mode 100755 index 6ec5c12279..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f63d.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f63e.png b/plugins/emoji/public/images/unicode/1f63e.png deleted file mode 100755 index 7346cb2f8e..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f63e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f63f.png b/plugins/emoji/public/images/unicode/1f63f.png deleted file mode 100755 index 9e41bb4660..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f63f.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f640.png b/plugins/emoji/public/images/unicode/1f640.png deleted file mode 100755 index 566cc3e867..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f640.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f645.png b/plugins/emoji/public/images/unicode/1f645.png deleted file mode 100755 index b06a92b9f6..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f645.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f646.png b/plugins/emoji/public/images/unicode/1f646.png deleted file mode 100755 index 457fd8edf3..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f646.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f647.png b/plugins/emoji/public/images/unicode/1f647.png deleted file mode 100755 index 04f26c158c..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f647.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f648.png b/plugins/emoji/public/images/unicode/1f648.png deleted file mode 100755 index 946d1d68e7..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f648.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f649.png b/plugins/emoji/public/images/unicode/1f649.png deleted file mode 100755 index aa3087c306..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f649.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f64a.png b/plugins/emoji/public/images/unicode/1f64a.png deleted file mode 100755 index c40ecdd73d..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f64a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f64b.png b/plugins/emoji/public/images/unicode/1f64b.png deleted file mode 100755 index 82d86ba585..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f64b.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f64c.png b/plugins/emoji/public/images/unicode/1f64c.png deleted file mode 100755 index 928377c547..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f64c.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f64d.png b/plugins/emoji/public/images/unicode/1f64d.png deleted file mode 100755 index 06e045dddf..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f64d.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f64e.png b/plugins/emoji/public/images/unicode/1f64e.png deleted file mode 100755 index 2ce816f37a..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f64e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f64f.png b/plugins/emoji/public/images/unicode/1f64f.png deleted file mode 100755 index 2a9167e111..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f64f.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f680.png b/plugins/emoji/public/images/unicode/1f680.png deleted file mode 100755 index e94890166e..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f680.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f681.png b/plugins/emoji/public/images/unicode/1f681.png deleted file mode 100755 index 8407946c24..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f681.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f682.png b/plugins/emoji/public/images/unicode/1f682.png deleted file mode 100755 index be7216cdb8..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f682.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f683.png b/plugins/emoji/public/images/unicode/1f683.png deleted file mode 100755 index cf0c2014bd..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f683.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f684.png b/plugins/emoji/public/images/unicode/1f684.png deleted file mode 100755 index b300cba252..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f684.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f685.png b/plugins/emoji/public/images/unicode/1f685.png deleted file mode 100755 index d9f009c68a..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f685.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f686.png b/plugins/emoji/public/images/unicode/1f686.png deleted file mode 100755 index 8c877426f4..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f686.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f687.png b/plugins/emoji/public/images/unicode/1f687.png deleted file mode 100755 index 1c9f67e0b1..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f687.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f688.png b/plugins/emoji/public/images/unicode/1f688.png deleted file mode 100755 index c72c5c3808..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f688.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f689.png b/plugins/emoji/public/images/unicode/1f689.png deleted file mode 100755 index 139c9566cf..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f689.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f68a.png b/plugins/emoji/public/images/unicode/1f68a.png deleted file mode 100755 index 95a3d9e775..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f68a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f68c.png b/plugins/emoji/public/images/unicode/1f68c.png deleted file mode 100755 index 8bb5abfb36..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f68c.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f68d.png b/plugins/emoji/public/images/unicode/1f68d.png deleted file mode 100755 index fb0b96788b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f68d.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f68e.png b/plugins/emoji/public/images/unicode/1f68e.png deleted file mode 100755 index d8029476db..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f68e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f68f.png b/plugins/emoji/public/images/unicode/1f68f.png deleted file mode 100755 index 4b5e16bd96..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f68f.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f690.png b/plugins/emoji/public/images/unicode/1f690.png deleted file mode 100755 index fd167c5c06..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f690.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f691.png b/plugins/emoji/public/images/unicode/1f691.png deleted file mode 100755 index 4c3d4ff868..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f691.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f692.png b/plugins/emoji/public/images/unicode/1f692.png deleted file mode 100755 index b842bd2acd..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f692.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f693.png b/plugins/emoji/public/images/unicode/1f693.png deleted file mode 100755 index 9ec5adddfd..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f693.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f694.png b/plugins/emoji/public/images/unicode/1f694.png deleted file mode 100755 index a456dd0a68..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f694.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f695.png b/plugins/emoji/public/images/unicode/1f695.png deleted file mode 100755 index 0a14d43ac1..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f695.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f696.png b/plugins/emoji/public/images/unicode/1f696.png deleted file mode 100755 index 735ba2dd40..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f696.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f697.png b/plugins/emoji/public/images/unicode/1f697.png deleted file mode 100755 index 93f6fc4f32..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f697.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f698.png b/plugins/emoji/public/images/unicode/1f698.png deleted file mode 100755 index 86cffe76d2..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f698.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f699.png b/plugins/emoji/public/images/unicode/1f699.png deleted file mode 100755 index 10e1d6f23b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f699.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f69a.png b/plugins/emoji/public/images/unicode/1f69a.png deleted file mode 100755 index 9dea1366f9..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f69a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f69b.png b/plugins/emoji/public/images/unicode/1f69b.png deleted file mode 100755 index 243e10889f..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f69b.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f69c.png b/plugins/emoji/public/images/unicode/1f69c.png deleted file mode 100755 index f2ed5f46c9..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f69c.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f69d.png b/plugins/emoji/public/images/unicode/1f69d.png deleted file mode 100755 index e7b93ab1d2..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f69d.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f69e.png b/plugins/emoji/public/images/unicode/1f69e.png deleted file mode 100755 index 458adf478c..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f69e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f69f.png b/plugins/emoji/public/images/unicode/1f69f.png deleted file mode 100755 index 98fb17708c..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f69f.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6a0.png b/plugins/emoji/public/images/unicode/1f6a0.png deleted file mode 100755 index 14871c2c69..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6a0.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6a2.png b/plugins/emoji/public/images/unicode/1f6a2.png deleted file mode 100755 index 268481d452..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6a2.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6a3.png b/plugins/emoji/public/images/unicode/1f6a3.png deleted file mode 100755 index b89344a5e5..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6a3.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6a4.png b/plugins/emoji/public/images/unicode/1f6a4.png deleted file mode 100755 index 4a63850d3b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6a4.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6a5.png b/plugins/emoji/public/images/unicode/1f6a5.png deleted file mode 100755 index 23e8750939..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6a5.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6a6.png b/plugins/emoji/public/images/unicode/1f6a6.png deleted file mode 100755 index f5db02984a..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6a6.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6a7.png b/plugins/emoji/public/images/unicode/1f6a7.png deleted file mode 100755 index 4488708607..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6a7.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6a8.png b/plugins/emoji/public/images/unicode/1f6a8.png deleted file mode 100755 index 4e2e509919..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6a8.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6a9.png b/plugins/emoji/public/images/unicode/1f6a9.png deleted file mode 100755 index 1f0bd6b74a..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6a9.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6ab.png b/plugins/emoji/public/images/unicode/1f6ab.png deleted file mode 100755 index 18b2426f48..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6ab.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6ac.png b/plugins/emoji/public/images/unicode/1f6ac.png deleted file mode 100755 index 2a47fe2996..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6ac.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6ad.png b/plugins/emoji/public/images/unicode/1f6ad.png deleted file mode 100755 index 3b75e6ef2b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6ad.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6ae.png b/plugins/emoji/public/images/unicode/1f6ae.png deleted file mode 100755 index 0fed1fe3c6..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6ae.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6af.png b/plugins/emoji/public/images/unicode/1f6af.png deleted file mode 100755 index ea2dbdcf22..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6af.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6b0.png b/plugins/emoji/public/images/unicode/1f6b0.png deleted file mode 100755 index 614d28f33e..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6b0.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6b1.png b/plugins/emoji/public/images/unicode/1f6b1.png deleted file mode 100755 index e1cb3bb3e9..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6b1.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6b2.png b/plugins/emoji/public/images/unicode/1f6b2.png deleted file mode 100755 index 0ab71a54ee..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6b2.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6b3.png b/plugins/emoji/public/images/unicode/1f6b3.png deleted file mode 100755 index b64d6f7295..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6b3.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6b4.png b/plugins/emoji/public/images/unicode/1f6b4.png deleted file mode 100755 index 473bf5049d..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6b4.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6b5.png b/plugins/emoji/public/images/unicode/1f6b5.png deleted file mode 100755 index 5054a4ec86..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6b5.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6b6.png b/plugins/emoji/public/images/unicode/1f6b6.png deleted file mode 100755 index 6bace24b2a..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6b6.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6b7.png b/plugins/emoji/public/images/unicode/1f6b7.png deleted file mode 100755 index 4fb3f39fef..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6b7.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6b8.png b/plugins/emoji/public/images/unicode/1f6b8.png deleted file mode 100755 index 36711a7d7b..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6b8.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6b9.png b/plugins/emoji/public/images/unicode/1f6b9.png deleted file mode 100755 index cae3354910..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6b9.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6ba.png b/plugins/emoji/public/images/unicode/1f6ba.png deleted file mode 100755 index ae0d50ba3c..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6ba.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6bb.png b/plugins/emoji/public/images/unicode/1f6bb.png deleted file mode 100755 index 3ac7b418e9..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6bb.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6bc.png b/plugins/emoji/public/images/unicode/1f6bc.png deleted file mode 100755 index 81ec25c88f..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6bc.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6bd.png b/plugins/emoji/public/images/unicode/1f6bd.png deleted file mode 100755 index 9c0a6d197e..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6bd.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6be.png b/plugins/emoji/public/images/unicode/1f6be.png deleted file mode 100755 index 95718321d9..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6be.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6bf.png b/plugins/emoji/public/images/unicode/1f6bf.png deleted file mode 100755 index 38f94f1034..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6bf.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6c0.png b/plugins/emoji/public/images/unicode/1f6c0.png deleted file mode 100755 index 7a6344c11e..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6c0.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6c1.png b/plugins/emoji/public/images/unicode/1f6c1.png deleted file mode 100755 index 622e702dd0..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6c1.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6c2.png b/plugins/emoji/public/images/unicode/1f6c2.png deleted file mode 100755 index 58538b03c4..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6c2.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6c3.png b/plugins/emoji/public/images/unicode/1f6c3.png deleted file mode 100755 index d2e648e48e..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6c3.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6c4.png b/plugins/emoji/public/images/unicode/1f6c4.png deleted file mode 100755 index c66e228867..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6c4.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/1f6c5.png b/plugins/emoji/public/images/unicode/1f6c5.png deleted file mode 100755 index 1c5a1395fe..0000000000 Binary files a/plugins/emoji/public/images/unicode/1f6c5.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/203c.png b/plugins/emoji/public/images/unicode/203c.png deleted file mode 100755 index 10a705d28c..0000000000 Binary files a/plugins/emoji/public/images/unicode/203c.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2122.png b/plugins/emoji/public/images/unicode/2122.png deleted file mode 100755 index 6cdc9b46f0..0000000000 Binary files a/plugins/emoji/public/images/unicode/2122.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2194.png b/plugins/emoji/public/images/unicode/2194.png deleted file mode 100755 index f381fb4c3c..0000000000 Binary files a/plugins/emoji/public/images/unicode/2194.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2195.png b/plugins/emoji/public/images/unicode/2195.png deleted file mode 100755 index 7bac2233dc..0000000000 Binary files a/plugins/emoji/public/images/unicode/2195.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2196.png b/plugins/emoji/public/images/unicode/2196.png deleted file mode 100755 index cd65016fa5..0000000000 Binary files a/plugins/emoji/public/images/unicode/2196.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2197.png b/plugins/emoji/public/images/unicode/2197.png deleted file mode 100755 index a537033160..0000000000 Binary files a/plugins/emoji/public/images/unicode/2197.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2198.png b/plugins/emoji/public/images/unicode/2198.png deleted file mode 100755 index 6d30bf19c5..0000000000 Binary files a/plugins/emoji/public/images/unicode/2198.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2199.png b/plugins/emoji/public/images/unicode/2199.png deleted file mode 100755 index 145e4f49c6..0000000000 Binary files a/plugins/emoji/public/images/unicode/2199.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/21a9.png b/plugins/emoji/public/images/unicode/21a9.png deleted file mode 100755 index 72ac2b7bca..0000000000 Binary files a/plugins/emoji/public/images/unicode/21a9.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/21aa.png b/plugins/emoji/public/images/unicode/21aa.png deleted file mode 100755 index 2b192f7bc8..0000000000 Binary files a/plugins/emoji/public/images/unicode/21aa.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/231a.png b/plugins/emoji/public/images/unicode/231a.png deleted file mode 100755 index acfb744d90..0000000000 Binary files a/plugins/emoji/public/images/unicode/231a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/231b.png b/plugins/emoji/public/images/unicode/231b.png deleted file mode 100755 index c2fbd1a120..0000000000 Binary files a/plugins/emoji/public/images/unicode/231b.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/23e9.png b/plugins/emoji/public/images/unicode/23e9.png deleted file mode 100755 index 5f836e74fa..0000000000 Binary files a/plugins/emoji/public/images/unicode/23e9.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/23ea.png b/plugins/emoji/public/images/unicode/23ea.png deleted file mode 100755 index 610d3180da..0000000000 Binary files a/plugins/emoji/public/images/unicode/23ea.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/23eb.png b/plugins/emoji/public/images/unicode/23eb.png deleted file mode 100755 index df5ac05495..0000000000 Binary files a/plugins/emoji/public/images/unicode/23eb.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/23ec.png b/plugins/emoji/public/images/unicode/23ec.png deleted file mode 100755 index 13d916be70..0000000000 Binary files a/plugins/emoji/public/images/unicode/23ec.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/23f0.png b/plugins/emoji/public/images/unicode/23f0.png deleted file mode 100755 index 80f4d52f89..0000000000 Binary files a/plugins/emoji/public/images/unicode/23f0.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/23f3.png b/plugins/emoji/public/images/unicode/23f3.png deleted file mode 100755 index ac9d570386..0000000000 Binary files a/plugins/emoji/public/images/unicode/23f3.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/24c2.png b/plugins/emoji/public/images/unicode/24c2.png deleted file mode 100755 index c99a7ce58e..0000000000 Binary files a/plugins/emoji/public/images/unicode/24c2.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/25aa.png b/plugins/emoji/public/images/unicode/25aa.png deleted file mode 100755 index 72aefad44a..0000000000 Binary files a/plugins/emoji/public/images/unicode/25aa.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/25b6.png b/plugins/emoji/public/images/unicode/25b6.png deleted file mode 100755 index 4ca6d97773..0000000000 Binary files a/plugins/emoji/public/images/unicode/25b6.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/25c0.png b/plugins/emoji/public/images/unicode/25c0.png deleted file mode 100755 index 2714a8f3f4..0000000000 Binary files a/plugins/emoji/public/images/unicode/25c0.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2601.png b/plugins/emoji/public/images/unicode/2601.png deleted file mode 100755 index 715702898a..0000000000 Binary files a/plugins/emoji/public/images/unicode/2601.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/260e.png b/plugins/emoji/public/images/unicode/260e.png deleted file mode 100755 index 75186c2b9f..0000000000 Binary files a/plugins/emoji/public/images/unicode/260e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2611.png b/plugins/emoji/public/images/unicode/2611.png deleted file mode 100755 index 72ce56cbfd..0000000000 Binary files a/plugins/emoji/public/images/unicode/2611.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2614.png b/plugins/emoji/public/images/unicode/2614.png deleted file mode 100755 index 17dca5fd8e..0000000000 Binary files a/plugins/emoji/public/images/unicode/2614.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2615.png b/plugins/emoji/public/images/unicode/2615.png deleted file mode 100755 index c17a1f1603..0000000000 Binary files a/plugins/emoji/public/images/unicode/2615.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/263a.png b/plugins/emoji/public/images/unicode/263a.png deleted file mode 100755 index 73756d97c9..0000000000 Binary files a/plugins/emoji/public/images/unicode/263a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2648.png b/plugins/emoji/public/images/unicode/2648.png deleted file mode 100755 index 713de88570..0000000000 Binary files a/plugins/emoji/public/images/unicode/2648.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2649.png b/plugins/emoji/public/images/unicode/2649.png deleted file mode 100755 index 6ab0e47965..0000000000 Binary files a/plugins/emoji/public/images/unicode/2649.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/264a.png b/plugins/emoji/public/images/unicode/264a.png deleted file mode 100755 index b0cf1f7190..0000000000 Binary files a/plugins/emoji/public/images/unicode/264a.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/264b.png b/plugins/emoji/public/images/unicode/264b.png deleted file mode 100755 index 842088fadc..0000000000 Binary files a/plugins/emoji/public/images/unicode/264b.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/264c.png b/plugins/emoji/public/images/unicode/264c.png deleted file mode 100755 index 31bcac9f72..0000000000 Binary files a/plugins/emoji/public/images/unicode/264c.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/264d.png b/plugins/emoji/public/images/unicode/264d.png deleted file mode 100755 index 4f4daec1ea..0000000000 Binary files a/plugins/emoji/public/images/unicode/264d.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/264e.png b/plugins/emoji/public/images/unicode/264e.png deleted file mode 100755 index 2f9aebf9b8..0000000000 Binary files a/plugins/emoji/public/images/unicode/264e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/264f.png b/plugins/emoji/public/images/unicode/264f.png deleted file mode 100755 index 90e5b4912b..0000000000 Binary files a/plugins/emoji/public/images/unicode/264f.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2650.png b/plugins/emoji/public/images/unicode/2650.png deleted file mode 100755 index db487cb32a..0000000000 Binary files a/plugins/emoji/public/images/unicode/2650.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2651.png b/plugins/emoji/public/images/unicode/2651.png deleted file mode 100755 index 04376740d7..0000000000 Binary files a/plugins/emoji/public/images/unicode/2651.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2652.png b/plugins/emoji/public/images/unicode/2652.png deleted file mode 100755 index cdf5fcf950..0000000000 Binary files a/plugins/emoji/public/images/unicode/2652.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2653.png b/plugins/emoji/public/images/unicode/2653.png deleted file mode 100755 index 226a27e624..0000000000 Binary files a/plugins/emoji/public/images/unicode/2653.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2660.png b/plugins/emoji/public/images/unicode/2660.png deleted file mode 100755 index bea3f6c5c9..0000000000 Binary files a/plugins/emoji/public/images/unicode/2660.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2665.png b/plugins/emoji/public/images/unicode/2665.png deleted file mode 100755 index 38df7756d6..0000000000 Binary files a/plugins/emoji/public/images/unicode/2665.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2666.png b/plugins/emoji/public/images/unicode/2666.png deleted file mode 100755 index d87b63506e..0000000000 Binary files a/plugins/emoji/public/images/unicode/2666.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2668.png b/plugins/emoji/public/images/unicode/2668.png deleted file mode 100755 index 640a77c215..0000000000 Binary files a/plugins/emoji/public/images/unicode/2668.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/267b.png b/plugins/emoji/public/images/unicode/267b.png deleted file mode 100755 index 345185aac0..0000000000 Binary files a/plugins/emoji/public/images/unicode/267b.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/267f.png b/plugins/emoji/public/images/unicode/267f.png deleted file mode 100755 index adc77e471b..0000000000 Binary files a/plugins/emoji/public/images/unicode/267f.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2693.png b/plugins/emoji/public/images/unicode/2693.png deleted file mode 100755 index 76bccd6020..0000000000 Binary files a/plugins/emoji/public/images/unicode/2693.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/26a0.png b/plugins/emoji/public/images/unicode/26a0.png deleted file mode 100755 index 09c7008f24..0000000000 Binary files a/plugins/emoji/public/images/unicode/26a0.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/26a1.png b/plugins/emoji/public/images/unicode/26a1.png deleted file mode 100755 index 2db46498ab..0000000000 Binary files a/plugins/emoji/public/images/unicode/26a1.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/26aa.png b/plugins/emoji/public/images/unicode/26aa.png deleted file mode 100755 index 48f7a5c1fb..0000000000 Binary files a/plugins/emoji/public/images/unicode/26aa.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/26ab.png b/plugins/emoji/public/images/unicode/26ab.png deleted file mode 100755 index 629fe0132b..0000000000 Binary files a/plugins/emoji/public/images/unicode/26ab.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/26bd.png b/plugins/emoji/public/images/unicode/26bd.png deleted file mode 100755 index c5c96efac2..0000000000 Binary files a/plugins/emoji/public/images/unicode/26bd.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/26be.png b/plugins/emoji/public/images/unicode/26be.png deleted file mode 100755 index 638d75fcbe..0000000000 Binary files a/plugins/emoji/public/images/unicode/26be.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/26c4.png b/plugins/emoji/public/images/unicode/26c4.png deleted file mode 100755 index 126d36e0e0..0000000000 Binary files a/plugins/emoji/public/images/unicode/26c4.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/26c5.png b/plugins/emoji/public/images/unicode/26c5.png deleted file mode 100755 index 5df5752801..0000000000 Binary files a/plugins/emoji/public/images/unicode/26c5.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/26ce.png b/plugins/emoji/public/images/unicode/26ce.png deleted file mode 100755 index cad2f28b33..0000000000 Binary files a/plugins/emoji/public/images/unicode/26ce.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/26d4.png b/plugins/emoji/public/images/unicode/26d4.png deleted file mode 100755 index a61e0ec1e7..0000000000 Binary files a/plugins/emoji/public/images/unicode/26d4.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/26ea.png b/plugins/emoji/public/images/unicode/26ea.png deleted file mode 100755 index b3a0d6b3b1..0000000000 Binary files a/plugins/emoji/public/images/unicode/26ea.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/26f2.png b/plugins/emoji/public/images/unicode/26f2.png deleted file mode 100755 index 8fb8d760db..0000000000 Binary files a/plugins/emoji/public/images/unicode/26f2.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/26f3.png b/plugins/emoji/public/images/unicode/26f3.png deleted file mode 100755 index e143d8b3f7..0000000000 Binary files a/plugins/emoji/public/images/unicode/26f3.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/26f5.png b/plugins/emoji/public/images/unicode/26f5.png deleted file mode 100755 index 04fffbe414..0000000000 Binary files a/plugins/emoji/public/images/unicode/26f5.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/26fa.png b/plugins/emoji/public/images/unicode/26fa.png deleted file mode 100755 index eecf0972d5..0000000000 Binary files a/plugins/emoji/public/images/unicode/26fa.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/26fd.png b/plugins/emoji/public/images/unicode/26fd.png deleted file mode 100755 index e6ee9589ad..0000000000 Binary files a/plugins/emoji/public/images/unicode/26fd.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2702.png b/plugins/emoji/public/images/unicode/2702.png deleted file mode 100755 index 6e96f14f7b..0000000000 Binary files a/plugins/emoji/public/images/unicode/2702.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2705.png b/plugins/emoji/public/images/unicode/2705.png deleted file mode 100755 index 31a445be22..0000000000 Binary files a/plugins/emoji/public/images/unicode/2705.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2708.png b/plugins/emoji/public/images/unicode/2708.png deleted file mode 100755 index bf05fc1e77..0000000000 Binary files a/plugins/emoji/public/images/unicode/2708.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/270b.png b/plugins/emoji/public/images/unicode/270b.png deleted file mode 100755 index 4788cea61e..0000000000 Binary files a/plugins/emoji/public/images/unicode/270b.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/270f.png b/plugins/emoji/public/images/unicode/270f.png deleted file mode 100755 index 9d9e083a3b..0000000000 Binary files a/plugins/emoji/public/images/unicode/270f.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2712.png b/plugins/emoji/public/images/unicode/2712.png deleted file mode 100755 index d4686e7f89..0000000000 Binary files a/plugins/emoji/public/images/unicode/2712.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2716.png b/plugins/emoji/public/images/unicode/2716.png deleted file mode 100755 index d1c2f1c983..0000000000 Binary files a/plugins/emoji/public/images/unicode/2716.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2728.png b/plugins/emoji/public/images/unicode/2728.png deleted file mode 100755 index 03599af7d5..0000000000 Binary files a/plugins/emoji/public/images/unicode/2728.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2733.png b/plugins/emoji/public/images/unicode/2733.png deleted file mode 100755 index 632c23e2d7..0000000000 Binary files a/plugins/emoji/public/images/unicode/2733.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2734.png b/plugins/emoji/public/images/unicode/2734.png deleted file mode 100755 index 61a2403ead..0000000000 Binary files a/plugins/emoji/public/images/unicode/2734.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2744.png b/plugins/emoji/public/images/unicode/2744.png deleted file mode 100755 index a457805ff0..0000000000 Binary files a/plugins/emoji/public/images/unicode/2744.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2747.png b/plugins/emoji/public/images/unicode/2747.png deleted file mode 100755 index fd707156e4..0000000000 Binary files a/plugins/emoji/public/images/unicode/2747.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/274e.png b/plugins/emoji/public/images/unicode/274e.png deleted file mode 100755 index c4e7838b0d..0000000000 Binary files a/plugins/emoji/public/images/unicode/274e.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2754.png b/plugins/emoji/public/images/unicode/2754.png deleted file mode 100755 index c08b419341..0000000000 Binary files a/plugins/emoji/public/images/unicode/2754.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2755.png b/plugins/emoji/public/images/unicode/2755.png deleted file mode 100755 index b8ef270c5a..0000000000 Binary files a/plugins/emoji/public/images/unicode/2755.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2757.png b/plugins/emoji/public/images/unicode/2757.png deleted file mode 100755 index 9391f2bea9..0000000000 Binary files a/plugins/emoji/public/images/unicode/2757.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2796.png b/plugins/emoji/public/images/unicode/2796.png deleted file mode 100755 index 1d7b40c4b1..0000000000 Binary files a/plugins/emoji/public/images/unicode/2796.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2797.png b/plugins/emoji/public/images/unicode/2797.png deleted file mode 100755 index 7d39350ee8..0000000000 Binary files a/plugins/emoji/public/images/unicode/2797.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/27a1.png b/plugins/emoji/public/images/unicode/27a1.png deleted file mode 100755 index a0284e7971..0000000000 Binary files a/plugins/emoji/public/images/unicode/27a1.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/27bf.png b/plugins/emoji/public/images/unicode/27bf.png deleted file mode 100755 index 535e320742..0000000000 Binary files a/plugins/emoji/public/images/unicode/27bf.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2934.png b/plugins/emoji/public/images/unicode/2934.png deleted file mode 100755 index 7514d5a745..0000000000 Binary files a/plugins/emoji/public/images/unicode/2934.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2935.png b/plugins/emoji/public/images/unicode/2935.png deleted file mode 100755 index d0c29b2c61..0000000000 Binary files a/plugins/emoji/public/images/unicode/2935.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2b05.png b/plugins/emoji/public/images/unicode/2b05.png deleted file mode 100755 index 95c5091f07..0000000000 Binary files a/plugins/emoji/public/images/unicode/2b05.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2b06.png b/plugins/emoji/public/images/unicode/2b06.png deleted file mode 100755 index f6fd179fa5..0000000000 Binary files a/plugins/emoji/public/images/unicode/2b06.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2b07.png b/plugins/emoji/public/images/unicode/2b07.png deleted file mode 100755 index 1d11413aca..0000000000 Binary files a/plugins/emoji/public/images/unicode/2b07.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/2b55.png b/plugins/emoji/public/images/unicode/2b55.png deleted file mode 100755 index ff18c28785..0000000000 Binary files a/plugins/emoji/public/images/unicode/2b55.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/3030.png b/plugins/emoji/public/images/unicode/3030.png deleted file mode 100755 index d055a6de52..0000000000 Binary files a/plugins/emoji/public/images/unicode/3030.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/303d.png b/plugins/emoji/public/images/unicode/303d.png deleted file mode 100755 index 2291f06d6e..0000000000 Binary files a/plugins/emoji/public/images/unicode/303d.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/3297.png b/plugins/emoji/public/images/unicode/3297.png deleted file mode 100755 index 557de6c77f..0000000000 Binary files a/plugins/emoji/public/images/unicode/3297.png and /dev/null differ diff --git a/plugins/emoji/public/images/unicode/3299.png b/plugins/emoji/public/images/unicode/3299.png deleted file mode 100755 index c5b4f4227b..0000000000 Binary files a/plugins/emoji/public/images/unicode/3299.png and /dev/null differ diff --git a/plugins/emoji/public/images/unlock.png b/plugins/emoji/public/images/unlock.png deleted file mode 120000 index d099026e1a..0000000000 --- a/plugins/emoji/public/images/unlock.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f513.png \ No newline at end of file diff --git a/plugins/emoji/public/images/up.png b/plugins/emoji/public/images/up.png deleted file mode 120000 index aeef791319..0000000000 --- a/plugins/emoji/public/images/up.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f199.png \ No newline at end of file diff --git a/plugins/emoji/public/images/us.png b/plugins/emoji/public/images/us.png deleted file mode 120000 index 5cbd32aba9..0000000000 --- a/plugins/emoji/public/images/us.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f1fa-1f1f8.png \ No newline at end of file diff --git a/plugins/emoji/public/images/v.png b/plugins/emoji/public/images/v.png deleted file mode 120000 index fd69dfb603..0000000000 --- a/plugins/emoji/public/images/v.png +++ /dev/null @@ -1 +0,0 @@ -unicode/270c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/vertical_traffic_light.png b/plugins/emoji/public/images/vertical_traffic_light.png deleted file mode 120000 index a25fff6514..0000000000 --- a/plugins/emoji/public/images/vertical_traffic_light.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6a6.png \ No newline at end of file diff --git a/plugins/emoji/public/images/vhs.png b/plugins/emoji/public/images/vhs.png deleted file mode 120000 index dd44ef29d3..0000000000 --- a/plugins/emoji/public/images/vhs.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4fc.png \ No newline at end of file diff --git a/plugins/emoji/public/images/vibration_mode.png b/plugins/emoji/public/images/vibration_mode.png deleted file mode 120000 index d754d5e3e7..0000000000 --- a/plugins/emoji/public/images/vibration_mode.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4f3.png \ No newline at end of file diff --git a/plugins/emoji/public/images/video_camera.png b/plugins/emoji/public/images/video_camera.png deleted file mode 120000 index e4adee991e..0000000000 --- a/plugins/emoji/public/images/video_camera.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4f9.png \ No newline at end of file diff --git a/plugins/emoji/public/images/video_game.png b/plugins/emoji/public/images/video_game.png deleted file mode 120000 index d108a542bf..0000000000 --- a/plugins/emoji/public/images/video_game.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3ae.png \ No newline at end of file diff --git a/plugins/emoji/public/images/violin.png b/plugins/emoji/public/images/violin.png deleted file mode 120000 index d29467f9f8..0000000000 --- a/plugins/emoji/public/images/violin.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f3bb.png \ No newline at end of file diff --git a/plugins/emoji/public/images/virgo.png b/plugins/emoji/public/images/virgo.png deleted file mode 120000 index ba706ef0d6..0000000000 --- a/plugins/emoji/public/images/virgo.png +++ /dev/null @@ -1 +0,0 @@ -unicode/264d.png \ No newline at end of file diff --git a/plugins/emoji/public/images/volcano.png b/plugins/emoji/public/images/volcano.png deleted file mode 120000 index 6e4837e532..0000000000 --- a/plugins/emoji/public/images/volcano.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f30b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/vs.png b/plugins/emoji/public/images/vs.png deleted file mode 120000 index 4c7be8986c..0000000000 --- a/plugins/emoji/public/images/vs.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f19a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/walking.png b/plugins/emoji/public/images/walking.png deleted file mode 120000 index 51cf020d22..0000000000 --- a/plugins/emoji/public/images/walking.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6b6.png \ No newline at end of file diff --git a/plugins/emoji/public/images/waning_crescent_moon.png b/plugins/emoji/public/images/waning_crescent_moon.png deleted file mode 120000 index 937d72e52c..0000000000 --- a/plugins/emoji/public/images/waning_crescent_moon.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f318.png \ No newline at end of file diff --git a/plugins/emoji/public/images/waning_gibbous_moon.png b/plugins/emoji/public/images/waning_gibbous_moon.png deleted file mode 120000 index afe1127253..0000000000 --- a/plugins/emoji/public/images/waning_gibbous_moon.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f316.png \ No newline at end of file diff --git a/plugins/emoji/public/images/warning.png b/plugins/emoji/public/images/warning.png deleted file mode 120000 index 9df8811097..0000000000 --- a/plugins/emoji/public/images/warning.png +++ /dev/null @@ -1 +0,0 @@ -unicode/26a0.png \ No newline at end of file diff --git a/plugins/emoji/public/images/watch.png b/plugins/emoji/public/images/watch.png deleted file mode 120000 index ee115707ef..0000000000 --- a/plugins/emoji/public/images/watch.png +++ /dev/null @@ -1 +0,0 @@ -unicode/231a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/water_buffalo.png b/plugins/emoji/public/images/water_buffalo.png deleted file mode 120000 index e9da975772..0000000000 --- a/plugins/emoji/public/images/water_buffalo.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f403.png \ No newline at end of file diff --git a/plugins/emoji/public/images/watermelon.png b/plugins/emoji/public/images/watermelon.png deleted file mode 120000 index 7ce5c0683a..0000000000 --- a/plugins/emoji/public/images/watermelon.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f349.png \ No newline at end of file diff --git a/plugins/emoji/public/images/wave.png b/plugins/emoji/public/images/wave.png deleted file mode 120000 index a8eb534c69..0000000000 --- a/plugins/emoji/public/images/wave.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f44b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/wavy_dash.png b/plugins/emoji/public/images/wavy_dash.png deleted file mode 120000 index 27ebde71c6..0000000000 --- a/plugins/emoji/public/images/wavy_dash.png +++ /dev/null @@ -1 +0,0 @@ -unicode/3030.png \ No newline at end of file diff --git a/plugins/emoji/public/images/waxing_crescent_moon.png b/plugins/emoji/public/images/waxing_crescent_moon.png deleted file mode 120000 index fba84d6dcd..0000000000 --- a/plugins/emoji/public/images/waxing_crescent_moon.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f312.png \ No newline at end of file diff --git a/plugins/emoji/public/images/waxing_gibbous_moon.png b/plugins/emoji/public/images/waxing_gibbous_moon.png deleted file mode 120000 index 1c589610f6..0000000000 --- a/plugins/emoji/public/images/waxing_gibbous_moon.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f314.png \ No newline at end of file diff --git a/plugins/emoji/public/images/wc.png b/plugins/emoji/public/images/wc.png deleted file mode 120000 index e99ff27cec..0000000000 --- a/plugins/emoji/public/images/wc.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6be.png \ No newline at end of file diff --git a/plugins/emoji/public/images/weary.png b/plugins/emoji/public/images/weary.png deleted file mode 120000 index 02c5636be6..0000000000 --- a/plugins/emoji/public/images/weary.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f629.png \ No newline at end of file diff --git a/plugins/emoji/public/images/wedding.png b/plugins/emoji/public/images/wedding.png deleted file mode 120000 index fd1d2d1159..0000000000 --- a/plugins/emoji/public/images/wedding.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f492.png \ No newline at end of file diff --git a/plugins/emoji/public/images/whale.png b/plugins/emoji/public/images/whale.png deleted file mode 120000 index 2604404c2e..0000000000 --- a/plugins/emoji/public/images/whale.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f433.png \ No newline at end of file diff --git a/plugins/emoji/public/images/whale2.png b/plugins/emoji/public/images/whale2.png deleted file mode 120000 index ae62b29788..0000000000 --- a/plugins/emoji/public/images/whale2.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f40b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/wheelchair.png b/plugins/emoji/public/images/wheelchair.png deleted file mode 120000 index 35cc6c3dce..0000000000 --- a/plugins/emoji/public/images/wheelchair.png +++ /dev/null @@ -1 +0,0 @@ -unicode/267f.png \ No newline at end of file diff --git a/plugins/emoji/public/images/white_check_mark.png b/plugins/emoji/public/images/white_check_mark.png deleted file mode 120000 index 1c30128873..0000000000 --- a/plugins/emoji/public/images/white_check_mark.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2705.png \ No newline at end of file diff --git a/plugins/emoji/public/images/white_circle.png b/plugins/emoji/public/images/white_circle.png deleted file mode 120000 index 30505e96c1..0000000000 --- a/plugins/emoji/public/images/white_circle.png +++ /dev/null @@ -1 +0,0 @@ -unicode/26aa.png \ No newline at end of file diff --git a/plugins/emoji/public/images/white_flower.png b/plugins/emoji/public/images/white_flower.png deleted file mode 120000 index d2a05197fb..0000000000 --- a/plugins/emoji/public/images/white_flower.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4ae.png \ No newline at end of file diff --git a/plugins/emoji/public/images/white_large_square.png b/plugins/emoji/public/images/white_large_square.png deleted file mode 120000 index 90c128071c..0000000000 --- a/plugins/emoji/public/images/white_large_square.png +++ /dev/null @@ -1 +0,0 @@ -unicode/2b1c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/white_medium_small_square.png b/plugins/emoji/public/images/white_medium_small_square.png deleted file mode 120000 index cff4cd1d45..0000000000 --- a/plugins/emoji/public/images/white_medium_small_square.png +++ /dev/null @@ -1 +0,0 @@ -unicode/25fd.png \ No newline at end of file diff --git a/plugins/emoji/public/images/white_medium_square.png b/plugins/emoji/public/images/white_medium_square.png deleted file mode 120000 index 57702e4889..0000000000 --- a/plugins/emoji/public/images/white_medium_square.png +++ /dev/null @@ -1 +0,0 @@ -unicode/25fb.png \ No newline at end of file diff --git a/plugins/emoji/public/images/white_small_square.png b/plugins/emoji/public/images/white_small_square.png deleted file mode 120000 index d79315d809..0000000000 --- a/plugins/emoji/public/images/white_small_square.png +++ /dev/null @@ -1 +0,0 @@ -unicode/25ab.png \ No newline at end of file diff --git a/plugins/emoji/public/images/white_square_button.png b/plugins/emoji/public/images/white_square_button.png deleted file mode 120000 index 9018615663..0000000000 --- a/plugins/emoji/public/images/white_square_button.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f533.png \ No newline at end of file diff --git a/plugins/emoji/public/images/wind_chime.png b/plugins/emoji/public/images/wind_chime.png deleted file mode 120000 index d9348911bf..0000000000 --- a/plugins/emoji/public/images/wind_chime.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f390.png \ No newline at end of file diff --git a/plugins/emoji/public/images/wine_glass.png b/plugins/emoji/public/images/wine_glass.png deleted file mode 120000 index dd6a6ad03e..0000000000 --- a/plugins/emoji/public/images/wine_glass.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f377.png \ No newline at end of file diff --git a/plugins/emoji/public/images/wink.png b/plugins/emoji/public/images/wink.png deleted file mode 120000 index e19070d3f4..0000000000 --- a/plugins/emoji/public/images/wink.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f609.png \ No newline at end of file diff --git a/plugins/emoji/public/images/wolf.png b/plugins/emoji/public/images/wolf.png deleted file mode 120000 index 7fdcf8f756..0000000000 --- a/plugins/emoji/public/images/wolf.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f43a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/woman.png b/plugins/emoji/public/images/woman.png deleted file mode 120000 index c9a45f3982..0000000000 --- a/plugins/emoji/public/images/woman.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f469.png \ No newline at end of file diff --git a/plugins/emoji/public/images/womans_clothes.png b/plugins/emoji/public/images/womans_clothes.png deleted file mode 120000 index dfdf15fadb..0000000000 --- a/plugins/emoji/public/images/womans_clothes.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f45a.png \ No newline at end of file diff --git a/plugins/emoji/public/images/womans_hat.png b/plugins/emoji/public/images/womans_hat.png deleted file mode 120000 index fea765acda..0000000000 --- a/plugins/emoji/public/images/womans_hat.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f452.png \ No newline at end of file diff --git a/plugins/emoji/public/images/womens.png b/plugins/emoji/public/images/womens.png deleted file mode 120000 index 4b2c93c184..0000000000 --- a/plugins/emoji/public/images/womens.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f6ba.png \ No newline at end of file diff --git a/plugins/emoji/public/images/worried.png b/plugins/emoji/public/images/worried.png deleted file mode 120000 index 0087d65a40..0000000000 --- a/plugins/emoji/public/images/worried.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f61f.png \ No newline at end of file diff --git a/plugins/emoji/public/images/wrench.png b/plugins/emoji/public/images/wrench.png deleted file mode 120000 index 5ff3e8c1fd..0000000000 --- a/plugins/emoji/public/images/wrench.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f527.png \ No newline at end of file diff --git a/plugins/emoji/public/images/x.png b/plugins/emoji/public/images/x.png deleted file mode 120000 index ebb129dece..0000000000 --- a/plugins/emoji/public/images/x.png +++ /dev/null @@ -1 +0,0 @@ -unicode/274c.png \ No newline at end of file diff --git a/plugins/emoji/public/images/yellow_heart.png b/plugins/emoji/public/images/yellow_heart.png deleted file mode 120000 index 8e529fc548..0000000000 --- a/plugins/emoji/public/images/yellow_heart.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f49b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/yen.png b/plugins/emoji/public/images/yen.png deleted file mode 120000 index d2215b588c..0000000000 --- a/plugins/emoji/public/images/yen.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4b4.png \ No newline at end of file diff --git a/plugins/emoji/public/images/yum.png b/plugins/emoji/public/images/yum.png deleted file mode 120000 index 98071348c3..0000000000 --- a/plugins/emoji/public/images/yum.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f60b.png \ No newline at end of file diff --git a/plugins/emoji/public/images/zap.png b/plugins/emoji/public/images/zap.png deleted file mode 120000 index 8c83de84bb..0000000000 --- a/plugins/emoji/public/images/zap.png +++ /dev/null @@ -1 +0,0 @@ -unicode/26a1.png \ No newline at end of file diff --git a/plugins/emoji/public/images/zero.png b/plugins/emoji/public/images/zero.png deleted file mode 120000 index c691c0825b..0000000000 --- a/plugins/emoji/public/images/zero.png +++ /dev/null @@ -1 +0,0 @@ -unicode/0030.png \ No newline at end of file diff --git a/plugins/emoji/public/images/zzz.png b/plugins/emoji/public/images/zzz.png deleted file mode 120000 index 4901a6bed4..0000000000 --- a/plugins/emoji/public/images/zzz.png +++ /dev/null @@ -1 +0,0 @@ -unicode/1f4a4.png \ No newline at end of file diff --git a/plugins/lazyYT/assets/javascripts/lazyYT.js b/plugins/lazyYT/assets/javascripts/lazyYT.js index 5c9f7cfba1..1864440044 100644 --- a/plugins/lazyYT/assets/javascripts/lazyYT.js +++ b/plugins/lazyYT/assets/javascripts/lazyYT.js @@ -1,61 +1,126 @@ -/*! LazyYT (lazy load Youtube videos plugin) - v0.3.4 - 2014-06-30 -* Usage:
loading...
-* Copyright (c) 2014 Tyler Pearson; Licensed MIT */ - +/*! +* lazyYT (lazy load YouTube videos) +* v1.0.1 - 2014-12-30 +* (CC) This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. +* http://creativecommons.org/licenses/by-sa/4.0/ +* Contributors: https://github.com/tylerpearson/lazyYT/graphs/contributors || https://github.com/daugilas/lazyYT/graphs/contributors +* +* Usage:
loading...
+*/ ;(function ($) { - 'use strict'; + 'use strict'; - function setUp($el) { - var width = $el.data('width'), - height = $el.data('height'), - ratio = $el.data('ratio'), - id = $el.data('youtube-id'), - aspectRatio = ['16', '9'], - paddingTop = 0, - youtubeParameters = $el.data('parameters') || ''; + function setUp($el, settings) { + var width = $el.data('width'), + height = $el.data('height'), + ratio = ($el.data('ratio')) ? $el.data('ratio') : settings.default_ratio, + id = $el.data('youtube-id'), + padding_bottom, + innerHtml = [], + $thumb, + thumb_img, + loading_text = $el.text() ? $el.text() : settings.loading_text, + youtube_parameters = $el.data('parameters') || ''; - if (typeof width === 'undefined' || typeof height === 'undefined') { - height = 0; - width = '100%'; - aspectRatio = (ratio.split(":")[1] / ratio.split(":")[0]) * 100; - paddingTop = aspectRatio + '%'; - } + ratio = ratio.split(":"); - $el.css({ - 'position': 'relative', - 'height': height, - 'width': width, - 'padding-top': paddingTop, - 'background': 'url(//img.youtube.com/vi/' + id + '/hqdefault.jpg) center center no-repeat', - 'cursor': 'pointer', - 'background-size': 'cover' - }) - .html('

') - .addClass('lazyYT-image-loaded'); - - var $el_title = $el.find("p.lazyYT-title"); //get reference to the current container title element - - $.getJSON('https://gdata.youtube.com/feeds/api/videos/' + id + '?v=2&alt=json', function (data) { - $el_title.text(data.entry.title.$t); - }); + // width and height might override default_ratio value + if (typeof width === 'number' && typeof height === 'number') { + $el.width(width); + padding_bottom = height + 'px'; + } else if (typeof width === 'number') { + $el.width(width); + padding_bottom = (width * ratio[1] / ratio[0]) + 'px'; + } else { + width = $el.width(); - $el.on('click', function (e) { - e.preventDefault(); - if (!$el.hasClass('lazyYT-video-loaded') && $el.hasClass('lazyYT-image-loaded')) { - $el.html('') - .removeClass('lazyYT-image-loaded') - .addClass('lazyYT-video-loaded'); - } - }); + // no width means that container is fluid and will be the size of its parent + if (width === 0) { + width = $el.parent().width(); + } + padding_bottom = (ratio[1] / ratio[0] * 100) + '%'; } - $.fn.lazyYT = function () { - return this.each(function () { - var $el = $(this).css('cursor', 'pointer'); - setUp($el); - }); + // + // This HTML will be placed inside 'lazyYT' container + + innerHtml.push('
'); + + // Play button from YouTube (exactly as it is in YouTube) + innerHtml.push('
'); + innerHtml.push(''); + innerHtml.push(''); + innerHtml.push(''); + innerHtml.push(''); + innerHtml.push('
'); // end of .ytp-large-play-button + + innerHtml.push('
'); // end of .ytp-thumbnail + + // Video title (info bar) + innerHtml.push('
'); + innerHtml.push('
'); + innerHtml.push('
'); + innerHtml.push(''); + innerHtml.push(loading_text); + innerHtml.push(''); + innerHtml.push('
'); // .html5-title + innerHtml.push('
'); // .html5-title-text-wrapper + innerHtml.push('
'); // end of Video title .html5-info-bar + + $el.css({ + 'padding-bottom': padding_bottom + }) + .html(innerHtml.join('')); + + if (width > 640) { + thumb_img = 'maxresdefault.jpg'; + } else if (width > 480) { + thumb_img = 'sddefault.jpg'; + } else if (width > 320) { + thumb_img = 'hqdefault.jpg'; + } else if (width > 120) { + thumb_img = 'mqdefault.jpg'; + } else if (width === 0) { // sometimes it fails on fluid layout + thumb_img = 'hqdefault.jpg'; + } else { + thumb_img = 'default.jpg'; + } + + $thumb = $el.find('.ytp-thumbnail').css({ + 'background-image': ['url(http://img.youtube.com/vi/', id, '/', thumb_img, ')'].join('') + }) + .addClass('lazyYT-image-loaded') + .on('click', function (e) { + e.preventDefault(); + if (!$el.hasClass('lazyYT-video-loaded') && $thumb.hasClass('lazyYT-image-loaded')) { + $el.html('') + .addClass('lazyYT-video-loaded'); + } + }); + + $.getJSON('https://gdata.youtube.com/feeds/api/videos/' + id + '?v=2&alt=json', function (data) { + $el.find('#lazyYT-title-' + id).text(data.entry.title.$t); + }); + + } + + $.fn.lazyYT = function (newSettings) { + var defaultSettings = { + loading_text: 'Loading...', + default_ratio: '16:9', + callback: null, // ToDO execute callback if given + container_class: 'lazyYT-container' }; + var settings = $.extend(defaultSettings, newSettings); + + return this.each(function () { + var $el = $(this).addClass(settings.container_class); + setUp($el, settings); + }); + }; }(jQuery)); diff --git a/plugins/lazyYT/assets/stylesheets/lazyYT.css b/plugins/lazyYT/assets/stylesheets/lazyYT.css index 43e49d4c65..732735138a 100644 --- a/plugins/lazyYT/assets/stylesheets/lazyYT.css +++ b/plugins/lazyYT/assets/stylesheets/lazyYT.css @@ -1,36 +1,116 @@ /*! -* lazyyt -* v0.3.4 - 2014-06-30 -* Copyright (c) 2014 Tyler Pearson (http://tylerp.me); Licensed MIT %> +* lazyYT (lazy load YouTube videos) +* v1.0.1 - 2014-12-30 +* (CC) This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. +* http://creativecommons.org/licenses/by-sa/4.0/ +* Contributors: https://github.com/tylerpearson/lazyYT/graphs/contributors || https://github.com/daugilas/lazyYT/graphs/contributors */ -.lazyYT-title { - z-index: 100!important; - color: #fff!important; - font-family: sans-serif!important; - font-size: 12px!important; - top: 10px!important; - left: 12px!important; - position: absolute!important; - margin: 0!important; - padding: 0.5em!important; - line-height: 1!important; - font-style: normal!important; - font-weight: normal!important; - background-color: rgba(0,0,0,0.8)!important; - border-radius: 0.5em!important; +.lazyYT-container { + position: relative; + display: block; + height: 0; + padding: 0 0 56.25% 0; + overflow: hidden; + background-color: #000000; } -.lazyYT-button { - margin: 0!important; - padding: 0!important; - width: 60px!important; - height: 41px!important; - z-index: 100!important; - position: absolute!important; - top: 50%!important; - margin-top: -22px!important; - left: 50%!important; - margin-left: -30px!important; - background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAApCAYAAABp50paAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABV9JREFUeNrcWk1IK1cUvrmZGBOjJvr6xKe+Slvroi6kK6GrUkSxO12IC6GgUFBcuOlC8GdRulERV3VRQV0IKhRU0NJupK3tpi1dCRaxffWHGjWZPJNnNJlMz9FzX+ZNkzylcxP1wMdMZiYz97vnu+ee+2Njmc0GyANwgANQDCgAuABOwGOAG6AAiuh+MV3Lo+fc9KyN3qGYvnEBSAA0QBTwAnBp+P0ccAxQ6bkw/T4HRABn9B+8F6f/ZiRktjJANaCSUE0kkVApoBDgAeQTIeM7dAKjAuiG6+b7wniKstgMv+2m5xMGslgZIUAAEAOcAv4GHAL+BDwDBNMRxvOPAZ8C3iUPOVNUiGaCfgOV3MZe9z5OlSDAUzxzQZXyC+BLwM+pCtMJGCTvCTJxOlpBxGpLVzF2ajqiMvyAzwDfGuXyPuALkmyY5KGZ5GVGri1duXRyVIyOPsA7gO9R/iIYtQLKiSy7Q6T+b2UkSN7vAT4U7QGDz9Mbtp/7SPySjh9gj8EpApeyh2/YoyicuhlvhuBkVU3n2jA/cBkJxyUVjBuipz2HEf0qYcKs5w1ify6DbFVVVbSrq0s9ODhQlpaWClRVdUtWUyqLE0cPEn5CXkhI8HC8ubk5ODw8XJBIJOJNTU2H/f39j/f29jxZDJI2Iowq9nJiLk1KZWWYqcKHOC9pa2t7c21tLdTR0XHgdDo1kng22rdGQaucU2YlraY1TRPJACrIXldXVzU/P+8YHR09rqysVIkwz1JWxjmNZqQRBimbBxdIvLyvr8+7uroabGxs9NP1bAS0fOmE09Q2SsxdX19fsbCw4BgcHNzzeDxhGrDItCJO2s52hiVIO3w+X+nIyEgxRPCjmpqaE8lp7VU/nCeTMASrTO++GrTDM8UQzStB4uHOzs5niqIkJLRrLIeTG2QkpVZtthu9Fgk6amtrn8zMzLgmJyePvV7vmcVl0kUuncfuhumkiIqenh7f4uJiAJKWMwuDmS4krdyxURKOYz0Qvd0NDQ1Ri9+tKIbh050Zx+q6fjg1NaWtr6/7SO5WvTuq0ABZuNyWY7L6ycnJ0dDQ0OXc3FxFJBKxW0w4opCEcmnYrDh4Vd3c3FS7u7t929vbj6ipWT3IuOREOFeeRQ/GQqGQf2xsLNDS0vIIyBbRdU2Cgl5K2pYD+SKpF1tbW0cDAwOu5eXlKkleNdqVpMMyk3eQaioJ6zCo8M/OzsZh6Fi0v79fYsi+ZNpzJByU6WHD4AEJ4QxpfHd392hiYuJyenq64vz8XGGvrlJIHSbix46lavc60xISVjc2NsK9vb0ukHKZYeIhG00I7WpeOirxQ3xnZwc99w90MaHx8fFAa2trMZAtYcl542wYOhbXoU7xox8BvmLJFTxLCRcWFkbb29tVv9+vrKyseOnj2SL6MqUEHAA+QcJ1gDl2PTcdldCexeKXCEq5GIrizOzvgC5OUTrI/rtua1ncYsl1nlzm7CjpCKcTld3vtaQbZVlY+SJoBR4wUeHIPUyykDBOwP8mZgTYw1pQQy755N2fsGlxurgO+JUmAxyGh/V7VgHmMjspNn0D+IEZZhOwDf/FrheOn7Lkdgexkm43vfB18rF8JuQGvYCD4DSUH69/B/gccJSqgG+z630euJb6Fv3JaSCfjpBm6McTFqWKNvbqPg6eIefXSbYXVAbc8PIH4EfA1+x620NGj2Cf7KMOG7cm4fi0hK5XUw0KiG1MeHQTHBZGVrFjJ0znuEXpjIItdnX7FHRPCeLeITPt4LmtBEWNiz1XYj7MxZJbmPIJpSy5pUlUXDrPxAzKiBBJN53vk9fE/q0okY4ZVBS7jaL+FWAA/y++OTUmOgsAAAAASUVORK5CYII=')!important; +.lazyYT-container iframe { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + border: 0; +} + +/* +* Video Title (YouTube style) +*/ + +.lazyYT-container .html5-info-bar { + position: absolute; + top: 0; + z-index: 935; + width: 100%; + height: 30px; + overflow: hidden; + font-family: Arial, Helvetica, sans-serif; + font-size: 12px; + color: #fff; + background-color: rgba(0, 0, 0, 0.8); + -webkit-transition: opacity 0.25s cubic-bezier(0, 0, 0.2, 1); + -moz-transition: opacity 0.25s cubic-bezier(0, 0, 0.2, 1); + transition: opacity 0.25s cubic-bezier(0, 0, 0.2, 1); +} + +.lazyYT-container .html5-title { + padding-right: 6px; + padding-left: 12px; +} + +.lazyYT-container .html5-title-text-wrapper { + overflow: hidden; + -o-text-overflow: ellipsis; + text-overflow: ellipsis; + word-wrap: normal; + white-space: nowrap; +} + +.lazyYT-container .html5-title-text { + width: 100%; + font-size: 13px; + line-height: 30px; + color: #ccc; + text-decoration: none; +} + +.lazyYT-container .html5-title-text:hover { + color: #fff; + text-decoration: underline; +} + +/* +* Thumbnail +*/ + +.ytp-thumbnail { + padding-bottom: inherit; + cursor: pointer; + background-position: 50% 50%; + background-repeat: no-repeat; + -webkit-background-size: cover; + -moz-background-size: cover; + -o-background-size: cover; + background-size: cover; +} + +/* +* Play button (YouTube style) +*/ + +.ytp-large-play-button { + position: absolute; + top: 50% !important; + left: 50% !important; + width: 86px !important; + height: 60px !important; + padding: 0 !important; + margin: -29px 0 0 -42px !important; + font-size: normal !important; + font-weight: normal !important; + line-height: 1 !important; + opacity: .9; +} + +.ytp-large-play-button-svg { + opacity: .9; + fill: #1f1f1f; +} + +.lazyYT-image-loaded:hover .ytp-large-play-button-svg, +.ytp-large-play-button:focus .ytp-large-play-button-svg { + opacity: 1; + fill: #cc181e; } diff --git a/plugins/lazyYT/plugin.rb b/plugins/lazyYT/plugin.rb index 95bc299145..cf02d90882 100644 --- a/plugins/lazyYT/plugin.rb +++ b/plugins/lazyYT/plugin.rb @@ -1,7 +1,8 @@ # name: lazyYT # about: Uses the lazyYT plugin to lazy load Youtube videos -# version: 0.1 +# version: 1.0.1 # authors: Arpit Jalan +# url: https://github.com/discourse/discourse/tree/master/plugins/lazyYT # javascript register_asset "javascripts/lazyYT.js" diff --git a/plugins/poll/README.md b/plugins/poll/README.md index 5fabf2cc03..805c0818c7 100644 --- a/plugins/poll/README.md +++ b/plugins/poll/README.md @@ -9,7 +9,7 @@ Allows you to add a poll to the first post of a topic. ## Closing the poll -Change the start of the topic title from "Poll: " to "Closed Poll: ". This feature is disabled if the `allow_user_locale` site setting is enabled. +Change the start of the topic title from "Poll: " to "Closed Poll: ". This feature uses the locale of the user who started the topic. _Note: closing a topic will also close the poll._ diff --git a/plugins/poll/assets/javascripts/controllers/poll.js.es6 b/plugins/poll/assets/javascripts/controllers/poll.js.es6 index 5ac1db7bd4..f582c58f68 100644 --- a/plugins/poll/assets/javascripts/controllers/poll.js.es6 +++ b/plugins/poll/assets/javascripts/controllers/poll.js.es6 @@ -5,7 +5,7 @@ export default DiscourseController.extend({ showResults: Em.computed.oneWay('poll.closed'), disableRadio: Em.computed.any('poll.closed', 'loading'), showToggleClosePoll: function() { - return this.get('poll.post.topic.details.can_edit') && !Discourse.SiteSettings.allow_user_locale; + return this.get('poll.post.topic.details.can_edit'); }.property('poll.post.topic.details.can_edit'), actions: { diff --git a/plugins/poll/assets/javascripts/discourse/templates/poll.hbs b/plugins/poll/assets/javascripts/discourse/templates/poll.hbs index 7d26c141d9..51dfd73c0b 100644 --- a/plugins/poll/assets/javascripts/discourse/templates/poll.hbs +++ b/plugins/poll/assets/javascripts/discourse/templates/poll.hbs @@ -1,13 +1,13 @@ - {{#each poll.options}} - + {{#each po in poll.options}} + @@ -18,10 +18,10 @@ @@ -29,10 +29,10 @@ {{/if}} diff --git a/plugins/poll/assets/javascripts/initializers/poll.js.es6 b/plugins/poll/assets/javascripts/initializers/poll.js.es6 index c55cac7eca..701b6edad1 100644 --- a/plugins/poll/assets/javascripts/initializers/poll.js.es6 +++ b/plugins/poll/assets/javascripts/initializers/poll.js.es6 @@ -1,4 +1,5 @@ import PollController from "discourse/plugins/poll/controllers/poll"; +import PostView from "discourse/views/post"; var Poll = Discourse.Model.extend({ post: null, @@ -49,12 +50,6 @@ var Poll = Discourse.Model.extend({ var PollView = Ember.View.extend({ templateName: "poll", classNames: ['poll-ui'], - - replaceElement: function(target) { - this._insertElementLater(function() { - target.replaceWith(this.$()); - }); - } }); function initializePollView(self) { @@ -70,18 +65,14 @@ function initializePollView(self) { postController: self.get('controller') }); - var pollView = self.createChildView(PollView, { - controller: pollController - }); - return pollView; + return self.createChildView(PollView, { controller: pollController }); } - export default { name: 'poll', initialize: function() { - Discourse.PostView.reopen({ + PostView.reopen({ createPollUI: function($post) { var post = this.get('post'); @@ -90,13 +81,14 @@ export default { } var view = initializePollView(this); - var pollContainer = $post.find(".poll-ui:first"); if (pollContainer.length === 0) { pollContainer = $post.find("ul:first"); } - view.replaceElement(pollContainer); + var $div = $('
'); + pollContainer.replaceWith($div); + view.constructor.renderer.appendTo(view, $div[0]); this.set('pollView', view); }.on('postViewInserted'), @@ -108,4 +100,4 @@ export default { }.on('willClearRender') }); } -} +}; diff --git a/plugins/poll/config/locales/client.ar.yml b/plugins/poll/config/locales/client.ar.yml new file mode 100644 index 0000000000..45eff0a048 --- /dev/null +++ b/plugins/poll/config/locales/client.ar.yml @@ -0,0 +1,22 @@ +# encoding: utf-8 +# +# Never edit this file. It will be overwritten when translations are pulled from Transifex. +# +# To work with us on translations, join this project: +# https://www.transifex.com/projects/p/discourse-org/ + +ar: + js: + poll: + voteCount: + zero: "صوت 1" + one: "صوت 1" + two: "صوت 1" + few: "صوت 1" + many: "%{احسب} الأصوات" + other: "%{احسب} الأصوات" + results: + show: إظهار النتائج + hide: إخفاء النتائج + close_poll: "إغلاق التصويت" + open_poll: "فتح التصويت" diff --git a/plugins/poll/config/locales/client.pt_BR.yml b/plugins/poll/config/locales/client.pt_BR.yml new file mode 100644 index 0000000000..ddfea867fe --- /dev/null +++ b/plugins/poll/config/locales/client.pt_BR.yml @@ -0,0 +1,29 @@ +# encoding: utf-8 +# This file contains content for the client portion of Discourse, sent out +# to the Javascript app. +# +# To work with us on translations, see: +# https://www.transifex.com/projects/p/discourse-org/ +# +# This is a "source" file, which is used by Transifex to get translations for other languages. +# After this file is changed, it needs to be pushed by a maintainer to Transifex: +# +# tx push -s +# +# Read more here: https://meta.discourse.org/t/contribute-a-translation-to-discourse/14882 +# +# To validate this YAML file after you change it, please paste it into +# http://yamllint.com/ + +pt_BR: + js: + poll: + voteCount: + one: "1 voto" + other: "%{count} votos" + results: + show: Mostrar Resultados + hide: Esconder Resultados + + close_poll: "Fechar Enquete " + open_poll: "Enquete aberta" diff --git a/plugins/poll/config/locales/client.te.yml b/plugins/poll/config/locales/client.te.yml new file mode 100644 index 0000000000..bc2b7210ed --- /dev/null +++ b/plugins/poll/config/locales/client.te.yml @@ -0,0 +1,18 @@ +# encoding: utf-8 +# +# Never edit this file. It will be overwritten when translations are pulled from Transifex. +# +# To work with us on translations, join this project: +# https://www.transifex.com/projects/p/discourse-org/ + +te: + js: + poll: + voteCount: + one: "ఒక ఓటు" + other: "%{count} ఓట్లు" + results: + show: ఫలితాలు చూపించు + hide: ఫలితాలు దాయు + close_poll: "ఓటు ముగించు" + open_poll: "ఓటు తెరువు" diff --git a/plugins/poll/config/locales/client.tr_TR.yml b/plugins/poll/config/locales/client.tr_TR.yml new file mode 100644 index 0000000000..721807dd24 --- /dev/null +++ b/plugins/poll/config/locales/client.tr_TR.yml @@ -0,0 +1,17 @@ +# encoding: utf-8 +# +# Never edit this file. It will be overwritten when translations are pulled from Transifex. +# +# To work with us on translations, join this project: +# https://www.transifex.com/projects/p/discourse-org/ + +tr_TR: + js: + poll: + voteCount: + other: "%{count} oy" + results: + show: Sonuçları göster + hide: Sonuçları gizle + close_poll: "Anketi Bitir" + open_poll: "Anket Başlat" diff --git a/plugins/poll/config/locales/server.ar.yml b/plugins/poll/config/locales/server.ar.yml new file mode 100644 index 0000000000..a66bdc8860 --- /dev/null +++ b/plugins/poll/config/locales/server.ar.yml @@ -0,0 +1,18 @@ +# encoding: utf-8 +# +# Never edit this file. It will be overwritten when translations are pulled from Transifex. +# +# To work with us on translations, join this project: +# https://www.transifex.com/projects/p/discourse-org/ + +ar: + activerecord: + attributes: + post: + poll_options: "خيارات التصويت" + poll: + must_contain_poll_options: "يجب أن يحتوي على قائمة خيارات التصويت" + cannot_have_modified_options: "التعديل غير ممكن بعد مضي 5 دقائق. اتصل بالمسؤول إذا كنت بحاجة لتغييرها." + cannot_add_or_remove_options: "تستطيع تعديله فقط ولا يمكنك إضافته أو حذفه. إذا كنت بحاجة لإضافة أو حذف خيارات يجب أن تُقفل هذا العنوان وتنشئ عنوان جديد." + prefix: "تصويت" + closed_prefix: "هذا التصويت مغلق" diff --git a/plugins/poll/config/locales/server.pt.yml b/plugins/poll/config/locales/server.pt.yml index 397cf5ed8c..61af621360 100644 --- a/plugins/poll/config/locales/server.pt.yml +++ b/plugins/poll/config/locales/server.pt.yml @@ -12,7 +12,7 @@ pt: poll_options: "Opções da votação" poll: must_contain_poll_options: "tem que conter uma lista de opções de votação" - cannot_have_modified_options: "não podem ser modificadas depois dos primeiros cinco minutos. Contacte um moderador se precisar de as alterar." - cannot_add_or_remove_options: "apenas podem ser editadas, não adicionadas ou removidas. Se precisar de adicionar ou remover opções, deverá bloquear este tópico e criar um novo." + cannot_have_modified_options: "não podem ser modificadas depois dos primeiros cinco minutos. Contacte um moderador se precisar de alterá-las." + cannot_add_or_remove_options: "podem apenas ser editadas, não podendo ser adicionadas ou removidas. Se precisar de adicionar ou remover opções, deverá bloquear este tópico e criar um novo." prefix: "Votação" closed_prefix: "Votação encerrada" diff --git a/plugins/poll/config/locales/server.pt_BR.yml b/plugins/poll/config/locales/server.pt_BR.yml new file mode 100644 index 0000000000..085d44ce7c --- /dev/null +++ b/plugins/poll/config/locales/server.pt_BR.yml @@ -0,0 +1,28 @@ +# encoding: utf-8 +# +# This file contains content for the server portion of Discourse used by Ruby +# +# To work with us on translations, see: +# https://www.transifex.com/projects/p/discourse-org/ +# +# This is a "source" file, which is used by Transifex to get translations for other languages. +# After this file is changed, it needs to be pushed by a maintainer to Transifex: +# +# tx push -s +# +# Read more here: https://meta.discourse.org/t/contribute-a-translation-to-discourse/14882 +# +# To validate this YAML file after you change it, please paste it into +# http://yamllint.com/ + +pt_BR: + activerecord: + attributes: + post: + poll_options: "Opções de votação " + poll: + must_contain_poll_options: "deve conter uma lista de opções de votação" + cannot_have_modified_options: "não pode ser modificado após os primeiros cinco minutos. Contate o moderador se necessitar fazer alguma mudança." + cannot_add_or_remove_options: "Só pode ser editado, mas não adicionar nem remover. Se precisar das opções para adicionar ou remover, você deve bloquear este tópico e criar um novo." + prefix: "Votação" + closed_prefix: "Votação encerrada" diff --git a/plugins/poll/config/locales/server.te.yml b/plugins/poll/config/locales/server.te.yml new file mode 100644 index 0000000000..50b7c548c8 --- /dev/null +++ b/plugins/poll/config/locales/server.te.yml @@ -0,0 +1,18 @@ +# encoding: utf-8 +# +# Never edit this file. It will be overwritten when translations are pulled from Transifex. +# +# To work with us on translations, join this project: +# https://www.transifex.com/projects/p/discourse-org/ + +te: + activerecord: + attributes: + post: + poll_options: "ఓటు ఐచ్చికాలు" + poll: + must_contain_poll_options: "తప్పనిసరి ఓటు ఐచ్చికాల జాబితా కలిగి ఉండాలి" + cannot_have_modified_options: "మొదటి ఐదు నిమిషాల తర్వాత మార్చైత కాదు. వీటిని మార్చాలంటే ఒక నిర్వాహకుడిని సంప్రదించండి. " + cannot_add_or_remove_options: "కేవలం సవరించవచ్చు, కలపైత కాదు, తొలగించైత కాదు. మీరు కలపడం లేదా తొలగించడం చేయాలంటే ఈ విషయానికి తాళం వేసి మరో కొత్త విషయం సృష్టించాలి" + prefix: "ఓటు" + closed_prefix: "మూసేసిన ఓటు" diff --git a/plugins/poll/config/locales/server.tr_TR.yml b/plugins/poll/config/locales/server.tr_TR.yml new file mode 100644 index 0000000000..d32284a4b9 --- /dev/null +++ b/plugins/poll/config/locales/server.tr_TR.yml @@ -0,0 +1,18 @@ +# encoding: utf-8 +# +# Never edit this file. It will be overwritten when translations are pulled from Transifex. +# +# To work with us on translations, join this project: +# https://www.transifex.com/projects/p/discourse-org/ + +tr_TR: + activerecord: + attributes: + post: + poll_options: "Anket seçenekleri" + poll: + must_contain_poll_options: "anket seçenekleri listesini içermeli" + cannot_have_modified_options: "ilk beş dakikadan sonra değişiklik yapılamaz. Değişiklik yapmanız gerekiyorsa, bir moderatör ile iletişime geçin." + cannot_add_or_remove_options: "sadece düzenlenebilir, ekleme veya çıkarma yapılamaz. Seçenek ekleme veya çıkarmanız gerekiyorsa, bu konuyu kitlemeli ve yeni bir konu oluşturmalısınız." + prefix: "Anket" + closed_prefix: "Bitmiş Anket" diff --git a/plugins/poll/config/locales/server.zh_CN.yml b/plugins/poll/config/locales/server.zh_CN.yml index 876dee963e..64e2a1ea24 100644 --- a/plugins/poll/config/locales/server.zh_CN.yml +++ b/plugins/poll/config/locales/server.zh_CN.yml @@ -11,8 +11,8 @@ zh_CN: post: poll_options: "投票选项" poll: - must_contain_poll_options: "必须包含投票选项列表" - cannot_have_modified_options: "在开始的五分钟后不能修改。如果需要修改他们,请联系版主。" - cannot_add_or_remove_options: "只能被编辑,不能添加或者删除。如果您需要添加或者删除选项,您需要锁定这个主题并创建新的。" + must_contain_poll_options: "必须包含投票选项" + cannot_have_modified_options: "在开始的五分钟后不能修改。如果需要修改他们,请联系一位版主。" + cannot_add_or_remove_options: "只能被编辑,不能添加或者删除。如果您需要添加或者删除选项,你需要锁定这个投票并创建新的投票。" prefix: "投票" closed_prefix: "已关闭的投票:" diff --git a/plugins/poll/plugin.rb b/plugins/poll/plugin.rb index a4a6fb9e30..56abf4f302 100644 --- a/plugins/poll/plugin.rb +++ b/plugins/poll/plugin.rb @@ -2,6 +2,7 @@ # about: adds poll support to Discourse # version: 0.1 # authors: Vikhyat Korrapati +# url: https://github.com/discourse/discourse/tree/master/plugins/poll load File.expand_path("../poll.rb", __FILE__) @@ -80,10 +81,12 @@ after_initialize do end # Modify topic title. - if topic.title =~ /^(#{I18n.t('poll.prefix').strip})\s?:/i - topic.title = topic.title.gsub(/^(#{I18n.t('poll.prefix').strip})\s?:/i, I18n.t('poll.closed_prefix') + ':') - elsif topic.title =~ /^(#{I18n.t('poll.closed_prefix').strip})\s?:/i - topic.title = topic.title.gsub(/^(#{I18n.t('poll.closed_prefix').strip})\s?:/i, I18n.t('poll.prefix') + ':') + I18n.with_locale(topic.user.effective_locale) do + if topic.title =~ /^(#{I18n.t('poll.prefix').strip})\s?:/i + topic.title = topic.title.gsub(/^(#{I18n.t('poll.prefix').strip})\s?:/i, I18n.t('poll.closed_prefix') + ':') + elsif topic.title =~ /^(#{I18n.t('poll.closed_prefix').strip})\s?:/i + topic.title = topic.title.gsub(/^(#{I18n.t('poll.closed_prefix').strip})\s?:/i, I18n.t('poll.prefix') + ':') + end end topic.acting_user = current_user diff --git a/plugins/poll/poll.rb b/plugins/poll/poll.rb index a833ffb082..35ceab7c37 100644 --- a/plugins/poll/poll.rb +++ b/plugins/poll/poll.rb @@ -6,34 +6,24 @@ module ::PollPlugin end def is_poll? - if !@post.post_number.nil? and @post.post_number > 1 - # Not a new post, and also not the first post. - return false - end + # Not a new post, and also not the first post. + return false if @post.post_number.present? && @post.post_number > 1 topic = @post.topic # Topic is not set in a couple of cases in the Discourse test suite. - return false if topic.nil? + return false if topic.nil? || topic.user.nil? - if @post.post_number.nil? and topic.highest_post_number > 0 - # New post, but not the first post in the topic. - return false + # New post, but not the first post in the topic. + return false if @post.post_number.nil? && topic.highest_post_number > 0 + + I18n.with_locale(topic.user.effective_locale) do + topic.title =~ /^(#{I18n.t('poll.prefix').strip}|#{I18n.t('poll.closed_prefix').strip})\s?:/i end - - topic.title =~ /^(#{I18n.t('poll.prefix').strip}|#{I18n.t('poll.closed_prefix').strip})\s?:/i end def has_poll_details? - if SiteSetting.allow_user_locale? - # If we allow users to select their locale of choice we cannot detect polls - # by the prefix, so we fall back to checking if the poll details is set in - # places to make sure polls are still accessible by users using a different - # locale than the one used by the topic creator. - not self.details.nil? - else - self.is_poll? - end + self.is_poll? end # Called during validation of poll posts. Discourse already restricts edits to @@ -60,7 +50,8 @@ module ::PollPlugin end def is_closed? - @post.topic.closed? || @post.topic.archived? || (!SiteSetting.allow_user_locale? && (@post.topic.title =~ /^#{I18n.t('poll.closed_prefix')}/i) === 0) + topic = @post.topic + topic.closed? || topic.archived? || (topic.title =~ /^#{I18n.t('poll.closed_prefix', locale: topic.user.effective_locale)}/i) === 0 end def options diff --git a/plugins/poll/spec/poll_plugin/poll_controller_spec.rb b/plugins/poll/spec/poll_plugin/poll_controller_spec.rb index a621db5762..8f912d8492 100644 --- a/plugins/poll/spec/poll_plugin/poll_controller_spec.rb +++ b/plugins/poll/spec/poll_plugin/poll_controller_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe PollPlugin::PollController, type: :controller do + routes { PollPlugin::Engine.routes } + let(:topic) { create_topic(title: "Poll: Chitoge vs Onodera") } let!(:post) { create_post(topic: topic, raw: "Pick one.\n\n[poll]\n* Chitoge\n* Onodera\n[/poll]") } let(:user1) { Fabricate(:user) } @@ -9,43 +11,43 @@ describe PollPlugin::PollController, type: :controller do describe 'vote' do it "returns 403 if no user is logged in" do - xhr :put, :vote, post_id: post.id, option: "Chitoge", use_route: :poll + xhr :put, :vote, post_id: post.id, option: "Chitoge" response.should be_forbidden end it "returns 400 if post_id or invalid option is not specified" do log_in_user user1 - xhr :put, :vote, use_route: :poll + xhr :put, :vote response.status.should eq(400) - xhr :put, :vote, post_id: post.id, use_route: :poll + xhr :put, :vote, post_id: post.id response.status.should eq(400) - xhr :put, :vote, option: "Chitoge", use_route: :poll + xhr :put, :vote, option: "Chitoge" response.status.should eq(400) - xhr :put, :vote, post_id: post.id, option: "Tsugumi", use_route: :poll + xhr :put, :vote, post_id: post.id, option: "Tsugumi" response.status.should eq(400) end it "returns 400 if post_id doesn't correspond to a poll post" do log_in_user user1 post2 = create_post(topic: topic, raw: "Generic reply") - xhr :put, :vote, post_id: post2.id, option: "Chitoge", use_route: :poll + xhr :put, :vote, post_id: post2.id, option: "Chitoge" end it "saves votes correctly" do MessageBus.expects(:publish).times(3) log_in_user user1 - xhr :put, :vote, post_id: post.id, option: "Chitoge", use_route: :poll + xhr :put, :vote, post_id: post.id, option: "Chitoge" PollPlugin::Poll.new(post).get_vote(user1).should eq("Chitoge") log_in_user user2 - xhr :put, :vote, post_id: post.id, option: "Onodera", use_route: :poll + xhr :put, :vote, post_id: post.id, option: "Onodera" PollPlugin::Poll.new(post).get_vote(user2).should eq("Onodera") PollPlugin::Poll.new(post).details["Chitoge"].should eq(1) PollPlugin::Poll.new(post).details["Onodera"].should eq(1) - xhr :put, :vote, post_id: post.id, option: "Chitoge", use_route: :poll + xhr :put, :vote, post_id: post.id, option: "Chitoge" PollPlugin::Poll.new(post).get_vote(user2).should eq("Chitoge") PollPlugin::Poll.new(post).details["Chitoge"].should eq(2) @@ -57,21 +59,21 @@ describe PollPlugin::PollController, type: :controller do it "returns 400 if post_id doesn't correspond to a poll post" do log_in_user admin post2 = create_post(topic: topic, raw: "Generic reply") - xhr :put, :toggle_close, post_id: post2.id, use_route: :poll + xhr :put, :toggle_close, post_id: post2.id response.status.should eq(400) end it "returns 400 if the topic is locked" do log_in_user admin topic.update_attributes closed: true - xhr :put, :toggle_close, post_id: post.id, use_route: :poll + xhr :put, :toggle_close, post_id: post.id response.status.should eq(400) end it "raises Discourse::InvalidAccess is the user is not authorized" do log_in_user user1 expect do - xhr :put, :toggle_close, post_id: post.id, use_route: :poll + xhr :put, :toggle_close, post_id: post.id end.to raise_error(Discourse::InvalidAccess) end @@ -79,10 +81,10 @@ describe PollPlugin::PollController, type: :controller do I18n.stubs(:t).with('poll.prefix').returns("Poll ") I18n.stubs(:t).with('poll.closed_prefix').returns("Closed Poll ") log_in_user admin - xhr :put, :toggle_close, post_id: post.id, use_route: :poll + xhr :put, :toggle_close, post_id: post.id response.status.should eq(200) topic.reload.title.should == "Closed Poll : Chitoge vs Onodera" - xhr :put, :toggle_close, post_id: post.id, use_route: :poll + xhr :put, :toggle_close, post_id: post.id response.status.should eq(200) topic.reload.title.should == "Poll : Chitoge vs Onodera" end diff --git a/public/403.ar.html b/public/403.ar.html new file mode 100644 index 0000000000..1e2006dd1a --- /dev/null +++ b/public/403.ar.html @@ -0,0 +1,26 @@ + + +لا يمكنك عمل ذلك (403) + + + + +
+

403

+

لا يمكنك عرض المحتوى!

+ +

سيتم تبديل الصفحة الحالية بصفحة ديسكورس 403 مخصصة.

+
+ + diff --git a/public/403.pt.html b/public/403.pt.html index 0467920ab5..674df0279a 100644 --- a/public/403.pt.html +++ b/public/403.pt.html @@ -18,9 +18,9 @@

403

-

Não pode aceder a esse recurso!

+

Não pode ver esse recurso!

-

Isto será substituído por uma página 403 personalizada do Discourse

+

Isto será substituído por uma página 403 personalizada do Discourse.

diff --git a/public/403.te.html b/public/403.te.html new file mode 100644 index 0000000000..80659a1515 --- /dev/null +++ b/public/403.te.html @@ -0,0 +1,26 @@ + + +మీరది చేయలేరు(403) + + + + +
+

403

+

ఆ రీసోర్సు ఇప్పుడు చూడలేరు

+ +

ఇది అభిమత డిస్కోర్సు 403 పేజీతో రీప్లేసవుతుంది.

+
+ + diff --git a/public/403.tr_TR.html b/public/403.tr_TR.html new file mode 100644 index 0000000000..85d06c6ff9 --- /dev/null +++ b/public/403.tr_TR.html @@ -0,0 +1,26 @@ + + +Bunu yapamazsınız (403) + + + + +
+

403

+

Bu kaynağı görüntüleyemezsiniz!

+ +

Bu özel bir Discourse 403 sayfası ile değiştirilecektir.

+
+ + diff --git a/public/422.ar.html b/public/422.ar.html new file mode 100644 index 0000000000..e8a38314ba --- /dev/null +++ b/public/422.ar.html @@ -0,0 +1,25 @@ + + +التغيير الذي طلبته غير مقبول (422) + + + + + +
+

التغيير الذي طلبته غير مقبول.

+

ربما حاولت تغيير شيء لا تملك الصلاحية لتغييره.

+
+ + diff --git a/public/422.pt.html b/public/422.pt.html index 903ec6cb30..2e64ef6607 100644 --- a/public/422.pt.html +++ b/public/422.pt.html @@ -1,6 +1,6 @@ -A alteração que você solicitou foi rejeitada (422) +A alteração que solicitou foi rejeitada (422) + + + +
+

మీరు చేయాలనుకున్న మార్పు తిరస్కారమైంది.

+

బహుశా మీకు అనుమతిలేనిదాన్ని దేన్నో మార్చాలని చూసారు

+
+ + diff --git a/public/422.tr_TR.html b/public/422.tr_TR.html new file mode 100644 index 0000000000..489b978cb5 --- /dev/null +++ b/public/422.tr_TR.html @@ -0,0 +1,25 @@ + + +İstediğiniz değişiklik reddedildi (422) + + + + + +
+

İstediğiniz değişiklik reddedildi.

+

Belki erişiminiz olmayan bir şeyi değiştirmeye çalıştınız.

+
+ + diff --git a/public/500.ar.html b/public/500.ar.html new file mode 100644 index 0000000000..d89df35a9a --- /dev/null +++ b/public/500.ar.html @@ -0,0 +1,12 @@ + + +عذرا - خطأ 500 + + + +

عذرا

+

البرنامج المشّغل لـ منصة المناقشة واجه خطأ غير متوقع. نحن نعتذر عن اي ازعاج.

+

تم تخزين معلومات تفصيلية عن هذا الخطا. و تم انشاء تنبية تلقائي. سوف نلقي نظرة على ذلك.

+

لا يوجد اي اجراء آخر ضروري. مع ذلك ، اذا الخطا استمر بالظهور، يمكنك تقديم معلومات اضافية , بما فية الخطوات لظهور الخطا ، عن طريق اضافة موضوع مناقشة في meta category.

+ + diff --git a/public/500.pt.html b/public/500.pt.html index 2f1f4893f0..58e6c45db6 100644 --- a/public/500.pt.html +++ b/public/500.pt.html @@ -5,8 +5,8 @@

Oops

-

O software que sustenta este fórum de discussão encontrou um problema inesperado. Pedimos desculpas pelo inconveniente.

-

Informações detalhadas sobre o erro foram registradas, e uma notificação automática foi gerada. Vamos dar uma olhada nisso.

-

Nenhuma ação adicional é necessária. No entanto, se a condição de erro persistir, você pode fornecer detalhes adicionais, incluindo as etapas para reproduzir o erro, postando um tópico de discussão no categoria meta</ a>.

+

O software que sustenta este fórum de discussão encontrou um problema inesperado. Pedimos desculpa pelo inconveniente.

+

Informações detalhadas sobre o erro foram registadas, e uma notificação automática foi gerada. Iremos verificar estes registos.

+

Nenhuma ação adicional é necessária. No entanto, se a condição de erro persistir, pode fornecer detalhes adicionais, incluindo as etapas para reproduzir o erro, publicando um tópico de discussão na categoria meta</ a>.

diff --git a/public/500.te.html b/public/500.te.html new file mode 100644 index 0000000000..bdd4b2ba1a --- /dev/null +++ b/public/500.te.html @@ -0,0 +1,12 @@ + + +అయ్యో - దోషం 500 + + + +

అయ్యో

+

ఈ చర్చావేదికను శక్తివంతం చేస్తున్న సాఫ్ట్‌వేర్ ఒక అనూహ్యమైన సమస్యకు లోనైంది. అసౌకర్యానికి చింతిస్తున్నాం.

+

ఈ దోషం గురించి పూర్తి సమాచారం నమోదుచేయబడింది. ఒక ఆటోమేటిక్ గమనింపు తయారైంది. మేము దాన్ని పరిశీలిస్తాము.

+

ఇంకెటువంటి చర్య అవసరంలేదు. కానీ ఈ దోషం మరలా మరలా వస్తూంటే మీరు ఇక్కడ మెటా వర్గం లో చర్చ మొదలుపెట్టి ఇంకాస్త సమాచారం ఇవ్వవచ్చు.

+ + diff --git a/public/500.tr_TR.html b/public/500.tr_TR.html new file mode 100644 index 0000000000..a89213f175 --- /dev/null +++ b/public/500.tr_TR.html @@ -0,0 +1,12 @@ + + +Oops - Hata 500 + + + +

Oops

+

Bu tartışma forumunu çalıştıran yazılım beklenmeyen bir problem ile karşılaştı. Verdiğimiz rahatsızlık için özür dileriz.

+

Hata ile ilgili detaylı bilgiler kaydedildi ve bize otomatik olarak bildirildi. İnceleyeceğiz.

+

Başka bir şey yapmanız gerekmiyor. Ancak eğer hatayı tekrar oluşturabiliyorsanız meta kategorisi altına hatanın oluşmasına neden olan adımları detaylı olarak açıklayan bir başlık açabilirsiniz.

+ + diff --git a/public/503.ar.html b/public/503.ar.html new file mode 100644 index 0000000000..48b6e2d1bb --- /dev/null +++ b/public/503.ar.html @@ -0,0 +1,11 @@ + + +الموقع تحت الصيانة - Discourse.org + + + +

نعمل حالياً على صيانة دورية للموقع.

+

يرجى التحقق لاحقا في بضع دقائق .

+

نأسف للإزعاج!

+ + diff --git a/public/503.pt.html b/public/503.pt.html index 29a2d302fb..18f73d1d3d 100644 --- a/public/503.pt.html +++ b/public/503.pt.html @@ -1,11 +1,11 @@ -Site Está em Manutenção +O sítio está em Manutenção -

Estamos em manutenção programada do site

-

Por favor volte em alguns minutos.

-

Desculpe pelo inconveniente!

+

Estamos em baixo para realizar uma manutenção planeada do sítio

+

Por favor volte em alguns minutos.

+

Pedimos desculpa pelo inconveniente!

diff --git a/public/503.sq.html b/public/503.sq.html index 07ab20d4a9..1ce9f13064 100644 --- a/public/503.sq.html +++ b/public/503.sq.html @@ -6,6 +6,6 @@

Siç e kishim planifikuar, faqja është në një fazë mirëmbajtje.

Ju lutem na vizitoni pas disa minutash.

-

Na vjen keq për shqetësimin!

+

Sorry for the inconvenience!

diff --git a/public/503.te.html b/public/503.te.html new file mode 100644 index 0000000000..57aaa206c4 --- /dev/null +++ b/public/503.te.html @@ -0,0 +1,11 @@ + + +డిస్కోర్స్ డాట్ ఆర్గ్ సైటు నిర్వహణలో ఉంది. + + + +

ప్రణాళికాబద్ద నిర్వహణ కోసం సైటు ప్రస్తుతం మూసేసి ఉంది.

+

దయచేసి iకొద్ది నిమిషాల తర్వాత మరలా దర్శించండి

+

అసౌకర్యానికి క్షమాపణలు

+ + diff --git a/public/503.tr_TR.html b/public/503.tr_TR.html new file mode 100644 index 0000000000..ff627445d7 --- /dev/null +++ b/public/503.tr_TR.html @@ -0,0 +1,11 @@ + + +Site Bakıma Alınıyor - Discourse.org + + + +

Önceden planlanmış site bakımı nedeniyle şu an kapalıyız.

+

Lütfen birkaç dakika içinde tekrar kontrol edin.

+

Verdiğimiz rahatsızlık için özür dileriz!

+ + diff --git a/public/images/emoji/apple/+1.png b/public/images/emoji/apple/+1.png new file mode 100644 index 0000000000..e1d89aada6 Binary files /dev/null and b/public/images/emoji/apple/+1.png differ diff --git a/public/images/emoji/apple/-1.png b/public/images/emoji/apple/-1.png new file mode 100644 index 0000000000..b67e9e1e14 Binary files /dev/null and b/public/images/emoji/apple/-1.png differ diff --git a/public/images/emoji/apple/100.png b/public/images/emoji/apple/100.png new file mode 100644 index 0000000000..225e308772 Binary files /dev/null and b/public/images/emoji/apple/100.png differ diff --git a/public/images/emoji/apple/1234.png b/public/images/emoji/apple/1234.png new file mode 100644 index 0000000000..22e560c116 Binary files /dev/null and b/public/images/emoji/apple/1234.png differ diff --git a/public/images/emoji/apple/8ball.png b/public/images/emoji/apple/8ball.png new file mode 100644 index 0000000000..8d7bf27a1e Binary files /dev/null and b/public/images/emoji/apple/8ball.png differ diff --git a/public/images/emoji/apple/a.png b/public/images/emoji/apple/a.png new file mode 100644 index 0000000000..cb9931af4d Binary files /dev/null and b/public/images/emoji/apple/a.png differ diff --git a/public/images/emoji/apple/ab.png b/public/images/emoji/apple/ab.png new file mode 100644 index 0000000000..5edb24891e Binary files /dev/null and b/public/images/emoji/apple/ab.png differ diff --git a/public/images/emoji/apple/abc.png b/public/images/emoji/apple/abc.png new file mode 100644 index 0000000000..be0db0d75a Binary files /dev/null and b/public/images/emoji/apple/abc.png differ diff --git a/public/images/emoji/apple/abcd.png b/public/images/emoji/apple/abcd.png new file mode 100644 index 0000000000..72713ba661 Binary files /dev/null and b/public/images/emoji/apple/abcd.png differ diff --git a/public/images/emoji/apple/accept.png b/public/images/emoji/apple/accept.png new file mode 100644 index 0000000000..8d7128c825 Binary files /dev/null and b/public/images/emoji/apple/accept.png differ diff --git a/public/images/emoji/apple/aerial_tramway.png b/public/images/emoji/apple/aerial_tramway.png new file mode 100644 index 0000000000..2a00e86920 Binary files /dev/null and b/public/images/emoji/apple/aerial_tramway.png differ diff --git a/public/images/emoji/apple/airplane.png b/public/images/emoji/apple/airplane.png new file mode 100644 index 0000000000..01f79a7e28 Binary files /dev/null and b/public/images/emoji/apple/airplane.png differ diff --git a/public/images/emoji/apple/alarm_clock.png b/public/images/emoji/apple/alarm_clock.png new file mode 100644 index 0000000000..74b3151e40 Binary files /dev/null and b/public/images/emoji/apple/alarm_clock.png differ diff --git a/public/images/emoji/apple/alien.png b/public/images/emoji/apple/alien.png new file mode 100644 index 0000000000..1cc33ff0a7 Binary files /dev/null and b/public/images/emoji/apple/alien.png differ diff --git a/public/images/emoji/apple/ambulance.png b/public/images/emoji/apple/ambulance.png new file mode 100644 index 0000000000..50addc4b40 Binary files /dev/null and b/public/images/emoji/apple/ambulance.png differ diff --git a/public/images/emoji/apple/anchor.png b/public/images/emoji/apple/anchor.png new file mode 100644 index 0000000000..7761da427b Binary files /dev/null and b/public/images/emoji/apple/anchor.png differ diff --git a/public/images/emoji/apple/angel.png b/public/images/emoji/apple/angel.png new file mode 100644 index 0000000000..9f67e6830c Binary files /dev/null and b/public/images/emoji/apple/angel.png differ diff --git a/public/images/emoji/apple/anger.png b/public/images/emoji/apple/anger.png new file mode 100644 index 0000000000..a4d5128dfe Binary files /dev/null and b/public/images/emoji/apple/anger.png differ diff --git a/public/images/emoji/apple/angry.png b/public/images/emoji/apple/angry.png new file mode 100644 index 0000000000..d60e7220dc Binary files /dev/null and b/public/images/emoji/apple/angry.png differ diff --git a/public/images/emoji/apple/anguished.png b/public/images/emoji/apple/anguished.png new file mode 100644 index 0000000000..470b36572f Binary files /dev/null and b/public/images/emoji/apple/anguished.png differ diff --git a/public/images/emoji/apple/ant.png b/public/images/emoji/apple/ant.png new file mode 100644 index 0000000000..4d619880a2 Binary files /dev/null and b/public/images/emoji/apple/ant.png differ diff --git a/public/images/emoji/apple/apple.png b/public/images/emoji/apple/apple.png new file mode 100644 index 0000000000..7e8777802b Binary files /dev/null and b/public/images/emoji/apple/apple.png differ diff --git a/public/images/emoji/apple/aquarius.png b/public/images/emoji/apple/aquarius.png new file mode 100644 index 0000000000..034fbbd2c4 Binary files /dev/null and b/public/images/emoji/apple/aquarius.png differ diff --git a/public/images/emoji/apple/aries.png b/public/images/emoji/apple/aries.png new file mode 100644 index 0000000000..eae37f5584 Binary files /dev/null and b/public/images/emoji/apple/aries.png differ diff --git a/public/images/emoji/apple/arrow_backward.png b/public/images/emoji/apple/arrow_backward.png new file mode 100644 index 0000000000..86a99c5290 Binary files /dev/null and b/public/images/emoji/apple/arrow_backward.png differ diff --git a/public/images/emoji/apple/arrow_double_down.png b/public/images/emoji/apple/arrow_double_down.png new file mode 100644 index 0000000000..cfb82e6bfc Binary files /dev/null and b/public/images/emoji/apple/arrow_double_down.png differ diff --git a/public/images/emoji/apple/arrow_double_up.png b/public/images/emoji/apple/arrow_double_up.png new file mode 100644 index 0000000000..1caa574742 Binary files /dev/null and b/public/images/emoji/apple/arrow_double_up.png differ diff --git a/public/images/emoji/apple/arrow_down.png b/public/images/emoji/apple/arrow_down.png new file mode 100644 index 0000000000..3fd0141fe9 Binary files /dev/null and b/public/images/emoji/apple/arrow_down.png differ diff --git a/public/images/emoji/apple/arrow_down_small.png b/public/images/emoji/apple/arrow_down_small.png new file mode 100644 index 0000000000..c7886bd9ed Binary files /dev/null and b/public/images/emoji/apple/arrow_down_small.png differ diff --git a/public/images/emoji/apple/arrow_forward.png b/public/images/emoji/apple/arrow_forward.png new file mode 100644 index 0000000000..5e5b3ee397 Binary files /dev/null and b/public/images/emoji/apple/arrow_forward.png differ diff --git a/public/images/emoji/apple/arrow_heading_down.png b/public/images/emoji/apple/arrow_heading_down.png new file mode 100644 index 0000000000..bad0e05880 Binary files /dev/null and b/public/images/emoji/apple/arrow_heading_down.png differ diff --git a/public/images/emoji/apple/arrow_heading_up.png b/public/images/emoji/apple/arrow_heading_up.png new file mode 100644 index 0000000000..66f3e0672e Binary files /dev/null and b/public/images/emoji/apple/arrow_heading_up.png differ diff --git a/public/images/emoji/apple/arrow_left.png b/public/images/emoji/apple/arrow_left.png new file mode 100644 index 0000000000..14698d5143 Binary files /dev/null and b/public/images/emoji/apple/arrow_left.png differ diff --git a/public/images/emoji/apple/arrow_lower_left.png b/public/images/emoji/apple/arrow_lower_left.png new file mode 100644 index 0000000000..693f882781 Binary files /dev/null and b/public/images/emoji/apple/arrow_lower_left.png differ diff --git a/public/images/emoji/apple/arrow_lower_right.png b/public/images/emoji/apple/arrow_lower_right.png new file mode 100644 index 0000000000..84e22ceded Binary files /dev/null and b/public/images/emoji/apple/arrow_lower_right.png differ diff --git a/public/images/emoji/apple/arrow_right.png b/public/images/emoji/apple/arrow_right.png new file mode 100644 index 0000000000..ab3fe36882 Binary files /dev/null and b/public/images/emoji/apple/arrow_right.png differ diff --git a/public/images/emoji/apple/arrow_right_hook.png b/public/images/emoji/apple/arrow_right_hook.png new file mode 100644 index 0000000000..0190f904b8 Binary files /dev/null and b/public/images/emoji/apple/arrow_right_hook.png differ diff --git a/public/images/emoji/apple/arrow_up.png b/public/images/emoji/apple/arrow_up.png new file mode 100644 index 0000000000..e462a4f848 Binary files /dev/null and b/public/images/emoji/apple/arrow_up.png differ diff --git a/public/images/emoji/apple/arrow_up_down.png b/public/images/emoji/apple/arrow_up_down.png new file mode 100644 index 0000000000..9b313192f2 Binary files /dev/null and b/public/images/emoji/apple/arrow_up_down.png differ diff --git a/public/images/emoji/apple/arrow_up_small.png b/public/images/emoji/apple/arrow_up_small.png new file mode 100644 index 0000000000..56154249f9 Binary files /dev/null and b/public/images/emoji/apple/arrow_up_small.png differ diff --git a/public/images/emoji/apple/arrow_upper_left.png b/public/images/emoji/apple/arrow_upper_left.png new file mode 100644 index 0000000000..5f88e0a4d2 Binary files /dev/null and b/public/images/emoji/apple/arrow_upper_left.png differ diff --git a/public/images/emoji/apple/arrow_upper_right.png b/public/images/emoji/apple/arrow_upper_right.png new file mode 100644 index 0000000000..cb956fc863 Binary files /dev/null and b/public/images/emoji/apple/arrow_upper_right.png differ diff --git a/public/images/emoji/apple/arrows_clockwise.png b/public/images/emoji/apple/arrows_clockwise.png new file mode 100644 index 0000000000..6c3608ba86 Binary files /dev/null and b/public/images/emoji/apple/arrows_clockwise.png differ diff --git a/public/images/emoji/apple/arrows_counterclockwise.png b/public/images/emoji/apple/arrows_counterclockwise.png new file mode 100644 index 0000000000..f4d55f0521 Binary files /dev/null and b/public/images/emoji/apple/arrows_counterclockwise.png differ diff --git a/public/images/emoji/apple/art.png b/public/images/emoji/apple/art.png new file mode 100644 index 0000000000..e2da2e8db5 Binary files /dev/null and b/public/images/emoji/apple/art.png differ diff --git a/public/images/emoji/apple/articulated_lorry.png b/public/images/emoji/apple/articulated_lorry.png new file mode 100644 index 0000000000..c26dd70678 Binary files /dev/null and b/public/images/emoji/apple/articulated_lorry.png differ diff --git a/public/images/emoji/apple/astonished.png b/public/images/emoji/apple/astonished.png new file mode 100644 index 0000000000..fe80340f4d Binary files /dev/null and b/public/images/emoji/apple/astonished.png differ diff --git a/public/images/emoji/apple/athletic_shoe.png b/public/images/emoji/apple/athletic_shoe.png new file mode 100644 index 0000000000..36767601db Binary files /dev/null and b/public/images/emoji/apple/athletic_shoe.png differ diff --git a/public/images/emoji/apple/atm.png b/public/images/emoji/apple/atm.png new file mode 100644 index 0000000000..93e57ac41a Binary files /dev/null and b/public/images/emoji/apple/atm.png differ diff --git a/public/images/emoji/apple/b.png b/public/images/emoji/apple/b.png new file mode 100644 index 0000000000..d22733d040 Binary files /dev/null and b/public/images/emoji/apple/b.png differ diff --git a/public/images/emoji/apple/baby.png b/public/images/emoji/apple/baby.png new file mode 100644 index 0000000000..f4bb76fe45 Binary files /dev/null and b/public/images/emoji/apple/baby.png differ diff --git a/public/images/emoji/apple/baby_bottle.png b/public/images/emoji/apple/baby_bottle.png new file mode 100644 index 0000000000..351b477591 Binary files /dev/null and b/public/images/emoji/apple/baby_bottle.png differ diff --git a/public/images/emoji/apple/baby_chick.png b/public/images/emoji/apple/baby_chick.png new file mode 100644 index 0000000000..b80a07df37 Binary files /dev/null and b/public/images/emoji/apple/baby_chick.png differ diff --git a/public/images/emoji/apple/baby_symbol.png b/public/images/emoji/apple/baby_symbol.png new file mode 100644 index 0000000000..73cde87a21 Binary files /dev/null and b/public/images/emoji/apple/baby_symbol.png differ diff --git a/public/images/emoji/apple/back.png b/public/images/emoji/apple/back.png new file mode 100644 index 0000000000..f1e401c540 Binary files /dev/null and b/public/images/emoji/apple/back.png differ diff --git a/public/images/emoji/apple/baggage_claim.png b/public/images/emoji/apple/baggage_claim.png new file mode 100644 index 0000000000..5fb3bbfee6 Binary files /dev/null and b/public/images/emoji/apple/baggage_claim.png differ diff --git a/public/images/emoji/apple/balloon.png b/public/images/emoji/apple/balloon.png new file mode 100644 index 0000000000..b23ace295e Binary files /dev/null and b/public/images/emoji/apple/balloon.png differ diff --git a/public/images/emoji/apple/ballot_box_with_check.png b/public/images/emoji/apple/ballot_box_with_check.png new file mode 100644 index 0000000000..b3cfd72098 Binary files /dev/null and b/public/images/emoji/apple/ballot_box_with_check.png differ diff --git a/public/images/emoji/apple/bamboo.png b/public/images/emoji/apple/bamboo.png new file mode 100644 index 0000000000..34e06476a1 Binary files /dev/null and b/public/images/emoji/apple/bamboo.png differ diff --git a/public/images/emoji/apple/banana.png b/public/images/emoji/apple/banana.png new file mode 100644 index 0000000000..0e1fdefab0 Binary files /dev/null and b/public/images/emoji/apple/banana.png differ diff --git a/public/images/emoji/apple/bangbang.png b/public/images/emoji/apple/bangbang.png new file mode 100644 index 0000000000..8a5728d0cf Binary files /dev/null and b/public/images/emoji/apple/bangbang.png differ diff --git a/public/images/emoji/apple/bank.png b/public/images/emoji/apple/bank.png new file mode 100644 index 0000000000..cdf03d1ba4 Binary files /dev/null and b/public/images/emoji/apple/bank.png differ diff --git a/public/images/emoji/apple/bar_chart.png b/public/images/emoji/apple/bar_chart.png new file mode 100644 index 0000000000..e3cdecc0e3 Binary files /dev/null and b/public/images/emoji/apple/bar_chart.png differ diff --git a/public/images/emoji/apple/barber.png b/public/images/emoji/apple/barber.png new file mode 100644 index 0000000000..cacd2555ec Binary files /dev/null and b/public/images/emoji/apple/barber.png differ diff --git a/public/images/emoji/apple/baseball.png b/public/images/emoji/apple/baseball.png new file mode 100644 index 0000000000..32c0d6b112 Binary files /dev/null and b/public/images/emoji/apple/baseball.png differ diff --git a/public/images/emoji/apple/basketball.png b/public/images/emoji/apple/basketball.png new file mode 100644 index 0000000000..96b575797d Binary files /dev/null and b/public/images/emoji/apple/basketball.png differ diff --git a/public/images/emoji/apple/bath.png b/public/images/emoji/apple/bath.png new file mode 100644 index 0000000000..2c100a9cc9 Binary files /dev/null and b/public/images/emoji/apple/bath.png differ diff --git a/public/images/emoji/apple/bathtub.png b/public/images/emoji/apple/bathtub.png new file mode 100644 index 0000000000..7e6cef0925 Binary files /dev/null and b/public/images/emoji/apple/bathtub.png differ diff --git a/public/images/emoji/apple/battery.png b/public/images/emoji/apple/battery.png new file mode 100644 index 0000000000..1d09b988e8 Binary files /dev/null and b/public/images/emoji/apple/battery.png differ diff --git a/public/images/emoji/apple/bear.png b/public/images/emoji/apple/bear.png new file mode 100644 index 0000000000..4896c9abb4 Binary files /dev/null and b/public/images/emoji/apple/bear.png differ diff --git a/public/images/emoji/apple/bee.png b/public/images/emoji/apple/bee.png new file mode 100644 index 0000000000..8ae23dfbcf Binary files /dev/null and b/public/images/emoji/apple/bee.png differ diff --git a/public/images/emoji/apple/beer.png b/public/images/emoji/apple/beer.png new file mode 100644 index 0000000000..b5ce16b2e9 Binary files /dev/null and b/public/images/emoji/apple/beer.png differ diff --git a/public/images/emoji/apple/beers.png b/public/images/emoji/apple/beers.png new file mode 100644 index 0000000000..22e5fc51b4 Binary files /dev/null and b/public/images/emoji/apple/beers.png differ diff --git a/public/images/emoji/apple/beetle.png b/public/images/emoji/apple/beetle.png new file mode 100644 index 0000000000..52bb51f26c Binary files /dev/null and b/public/images/emoji/apple/beetle.png differ diff --git a/public/images/emoji/apple/beginner.png b/public/images/emoji/apple/beginner.png new file mode 100644 index 0000000000..8813761dc5 Binary files /dev/null and b/public/images/emoji/apple/beginner.png differ diff --git a/public/images/emoji/apple/bell.png b/public/images/emoji/apple/bell.png new file mode 100644 index 0000000000..db9f0e2f57 Binary files /dev/null and b/public/images/emoji/apple/bell.png differ diff --git a/public/images/emoji/apple/bento.png b/public/images/emoji/apple/bento.png new file mode 100644 index 0000000000..89236d6f08 Binary files /dev/null and b/public/images/emoji/apple/bento.png differ diff --git a/public/images/emoji/apple/bicyclist.png b/public/images/emoji/apple/bicyclist.png new file mode 100644 index 0000000000..5c65e33601 Binary files /dev/null and b/public/images/emoji/apple/bicyclist.png differ diff --git a/public/images/emoji/apple/bike.png b/public/images/emoji/apple/bike.png new file mode 100644 index 0000000000..5d989ee5bd Binary files /dev/null and b/public/images/emoji/apple/bike.png differ diff --git a/public/images/emoji/apple/bikini.png b/public/images/emoji/apple/bikini.png new file mode 100644 index 0000000000..ad1ce7ce88 Binary files /dev/null and b/public/images/emoji/apple/bikini.png differ diff --git a/public/images/emoji/apple/bird.png b/public/images/emoji/apple/bird.png new file mode 100644 index 0000000000..51a3a36d65 Binary files /dev/null and b/public/images/emoji/apple/bird.png differ diff --git a/public/images/emoji/apple/birthday.png b/public/images/emoji/apple/birthday.png new file mode 100644 index 0000000000..cf40216a08 Binary files /dev/null and b/public/images/emoji/apple/birthday.png differ diff --git a/public/images/emoji/apple/black_circle.png b/public/images/emoji/apple/black_circle.png new file mode 100644 index 0000000000..3b929ddf7a Binary files /dev/null and b/public/images/emoji/apple/black_circle.png differ diff --git a/public/images/emoji/apple/black_joker.png b/public/images/emoji/apple/black_joker.png new file mode 100644 index 0000000000..1a83d7b97f Binary files /dev/null and b/public/images/emoji/apple/black_joker.png differ diff --git a/public/images/emoji/apple/black_large_square.png b/public/images/emoji/apple/black_large_square.png new file mode 100644 index 0000000000..d0b82fd684 Binary files /dev/null and b/public/images/emoji/apple/black_large_square.png differ diff --git a/public/images/emoji/apple/black_medium_small_square.png b/public/images/emoji/apple/black_medium_small_square.png new file mode 100644 index 0000000000..bb65f1580d Binary files /dev/null and b/public/images/emoji/apple/black_medium_small_square.png differ diff --git a/public/images/emoji/apple/black_medium_square.png b/public/images/emoji/apple/black_medium_square.png new file mode 100644 index 0000000000..d0b82fd684 Binary files /dev/null and b/public/images/emoji/apple/black_medium_square.png differ diff --git a/public/images/emoji/apple/black_nib.png b/public/images/emoji/apple/black_nib.png new file mode 100644 index 0000000000..760e2fafb0 Binary files /dev/null and b/public/images/emoji/apple/black_nib.png differ diff --git a/public/images/emoji/apple/black_small_square.png b/public/images/emoji/apple/black_small_square.png new file mode 100644 index 0000000000..73ad0e6f30 Binary files /dev/null and b/public/images/emoji/apple/black_small_square.png differ diff --git a/public/images/emoji/apple/black_square_button.png b/public/images/emoji/apple/black_square_button.png new file mode 100644 index 0000000000..27af757d89 Binary files /dev/null and b/public/images/emoji/apple/black_square_button.png differ diff --git a/public/images/emoji/apple/blossom.png b/public/images/emoji/apple/blossom.png new file mode 100644 index 0000000000..679a61e224 Binary files /dev/null and b/public/images/emoji/apple/blossom.png differ diff --git a/public/images/emoji/apple/blowfish.png b/public/images/emoji/apple/blowfish.png new file mode 100644 index 0000000000..d480416c51 Binary files /dev/null and b/public/images/emoji/apple/blowfish.png differ diff --git a/public/images/emoji/apple/blue_book.png b/public/images/emoji/apple/blue_book.png new file mode 100644 index 0000000000..de037bcdd4 Binary files /dev/null and b/public/images/emoji/apple/blue_book.png differ diff --git a/public/images/emoji/apple/blue_car.png b/public/images/emoji/apple/blue_car.png new file mode 100644 index 0000000000..a5d2700fc1 Binary files /dev/null and b/public/images/emoji/apple/blue_car.png differ diff --git a/public/images/emoji/apple/blue_heart.png b/public/images/emoji/apple/blue_heart.png new file mode 100644 index 0000000000..749b639c2f Binary files /dev/null and b/public/images/emoji/apple/blue_heart.png differ diff --git a/public/images/emoji/apple/blush.png b/public/images/emoji/apple/blush.png new file mode 100644 index 0000000000..78953c9fee Binary files /dev/null and b/public/images/emoji/apple/blush.png differ diff --git a/public/images/emoji/apple/boar.png b/public/images/emoji/apple/boar.png new file mode 100644 index 0000000000..ce8dc6cb83 Binary files /dev/null and b/public/images/emoji/apple/boar.png differ diff --git a/public/images/emoji/apple/boat.png b/public/images/emoji/apple/boat.png new file mode 100644 index 0000000000..a175599015 Binary files /dev/null and b/public/images/emoji/apple/boat.png differ diff --git a/public/images/emoji/apple/bomb.png b/public/images/emoji/apple/bomb.png new file mode 100644 index 0000000000..9f163f4e7a Binary files /dev/null and b/public/images/emoji/apple/bomb.png differ diff --git a/public/images/emoji/apple/book.png b/public/images/emoji/apple/book.png new file mode 100644 index 0000000000..3393a9b3c8 Binary files /dev/null and b/public/images/emoji/apple/book.png differ diff --git a/public/images/emoji/apple/bookmark.png b/public/images/emoji/apple/bookmark.png new file mode 100644 index 0000000000..6807f58fe9 Binary files /dev/null and b/public/images/emoji/apple/bookmark.png differ diff --git a/public/images/emoji/apple/bookmark_tabs.png b/public/images/emoji/apple/bookmark_tabs.png new file mode 100644 index 0000000000..3ad3cc803c Binary files /dev/null and b/public/images/emoji/apple/bookmark_tabs.png differ diff --git a/public/images/emoji/apple/books.png b/public/images/emoji/apple/books.png new file mode 100644 index 0000000000..ca7a047da5 Binary files /dev/null and b/public/images/emoji/apple/books.png differ diff --git a/public/images/emoji/apple/boom.png b/public/images/emoji/apple/boom.png new file mode 100644 index 0000000000..c16baf2aec Binary files /dev/null and b/public/images/emoji/apple/boom.png differ diff --git a/public/images/emoji/apple/boot.png b/public/images/emoji/apple/boot.png new file mode 100644 index 0000000000..f835a6041e Binary files /dev/null and b/public/images/emoji/apple/boot.png differ diff --git a/public/images/emoji/apple/bouquet.png b/public/images/emoji/apple/bouquet.png new file mode 100644 index 0000000000..e2c55468c7 Binary files /dev/null and b/public/images/emoji/apple/bouquet.png differ diff --git a/public/images/emoji/apple/bow.png b/public/images/emoji/apple/bow.png new file mode 100644 index 0000000000..166bab5e87 Binary files /dev/null and b/public/images/emoji/apple/bow.png differ diff --git a/public/images/emoji/apple/bowling.png b/public/images/emoji/apple/bowling.png new file mode 100644 index 0000000000..5ebda9d7cf Binary files /dev/null and b/public/images/emoji/apple/bowling.png differ diff --git a/public/images/emoji/apple/boy.png b/public/images/emoji/apple/boy.png new file mode 100644 index 0000000000..847b1698c2 Binary files /dev/null and b/public/images/emoji/apple/boy.png differ diff --git a/public/images/emoji/apple/bread.png b/public/images/emoji/apple/bread.png new file mode 100644 index 0000000000..9c3f40d328 Binary files /dev/null and b/public/images/emoji/apple/bread.png differ diff --git a/public/images/emoji/apple/bride_with_veil.png b/public/images/emoji/apple/bride_with_veil.png new file mode 100644 index 0000000000..5d3e746d86 Binary files /dev/null and b/public/images/emoji/apple/bride_with_veil.png differ diff --git a/public/images/emoji/apple/bridge_at_night.png b/public/images/emoji/apple/bridge_at_night.png new file mode 100644 index 0000000000..d9933f5053 Binary files /dev/null and b/public/images/emoji/apple/bridge_at_night.png differ diff --git a/public/images/emoji/apple/briefcase.png b/public/images/emoji/apple/briefcase.png new file mode 100644 index 0000000000..11b89c5380 Binary files /dev/null and b/public/images/emoji/apple/briefcase.png differ diff --git a/public/images/emoji/apple/broken_heart.png b/public/images/emoji/apple/broken_heart.png new file mode 100644 index 0000000000..0d8d825529 Binary files /dev/null and b/public/images/emoji/apple/broken_heart.png differ diff --git a/public/images/emoji/apple/bug.png b/public/images/emoji/apple/bug.png new file mode 100644 index 0000000000..14b0d890a5 Binary files /dev/null and b/public/images/emoji/apple/bug.png differ diff --git a/public/images/emoji/apple/bulb.png b/public/images/emoji/apple/bulb.png new file mode 100644 index 0000000000..abb3d62f1a Binary files /dev/null and b/public/images/emoji/apple/bulb.png differ diff --git a/public/images/emoji/apple/bullettrain_front.png b/public/images/emoji/apple/bullettrain_front.png new file mode 100644 index 0000000000..573259ac7f Binary files /dev/null and b/public/images/emoji/apple/bullettrain_front.png differ diff --git a/public/images/emoji/apple/bullettrain_side.png b/public/images/emoji/apple/bullettrain_side.png new file mode 100644 index 0000000000..cc3a3791d5 Binary files /dev/null and b/public/images/emoji/apple/bullettrain_side.png differ diff --git a/public/images/emoji/apple/bus.png b/public/images/emoji/apple/bus.png new file mode 100644 index 0000000000..44149941e5 Binary files /dev/null and b/public/images/emoji/apple/bus.png differ diff --git a/public/images/emoji/apple/busstop.png b/public/images/emoji/apple/busstop.png new file mode 100644 index 0000000000..1b6d9de136 Binary files /dev/null and b/public/images/emoji/apple/busstop.png differ diff --git a/public/images/emoji/apple/bust_in_silhouette.png b/public/images/emoji/apple/bust_in_silhouette.png new file mode 100644 index 0000000000..c48af4ab9b Binary files /dev/null and b/public/images/emoji/apple/bust_in_silhouette.png differ diff --git a/public/images/emoji/apple/busts_in_silhouette.png b/public/images/emoji/apple/busts_in_silhouette.png new file mode 100644 index 0000000000..4c70f9365e Binary files /dev/null and b/public/images/emoji/apple/busts_in_silhouette.png differ diff --git a/public/images/emoji/apple/cactus.png b/public/images/emoji/apple/cactus.png new file mode 100644 index 0000000000..cc8e4b1e9e Binary files /dev/null and b/public/images/emoji/apple/cactus.png differ diff --git a/public/images/emoji/apple/cake.png b/public/images/emoji/apple/cake.png new file mode 100644 index 0000000000..f5396ca061 Binary files /dev/null and b/public/images/emoji/apple/cake.png differ diff --git a/public/images/emoji/apple/calendar.png b/public/images/emoji/apple/calendar.png new file mode 100644 index 0000000000..a6ba1537f6 Binary files /dev/null and b/public/images/emoji/apple/calendar.png differ diff --git a/public/images/emoji/apple/calling.png b/public/images/emoji/apple/calling.png new file mode 100644 index 0000000000..773a17cce5 Binary files /dev/null and b/public/images/emoji/apple/calling.png differ diff --git a/public/images/emoji/apple/camel.png b/public/images/emoji/apple/camel.png new file mode 100644 index 0000000000..b73a80df41 Binary files /dev/null and b/public/images/emoji/apple/camel.png differ diff --git a/public/images/emoji/apple/camera.png b/public/images/emoji/apple/camera.png new file mode 100644 index 0000000000..2b5ca74a16 Binary files /dev/null and b/public/images/emoji/apple/camera.png differ diff --git a/public/images/emoji/apple/cancer.png b/public/images/emoji/apple/cancer.png new file mode 100644 index 0000000000..053ccd0996 Binary files /dev/null and b/public/images/emoji/apple/cancer.png differ diff --git a/public/images/emoji/apple/candy.png b/public/images/emoji/apple/candy.png new file mode 100644 index 0000000000..2e5c316b4a Binary files /dev/null and b/public/images/emoji/apple/candy.png differ diff --git a/public/images/emoji/apple/capital_abcd.png b/public/images/emoji/apple/capital_abcd.png new file mode 100644 index 0000000000..ba09fb2cf3 Binary files /dev/null and b/public/images/emoji/apple/capital_abcd.png differ diff --git a/public/images/emoji/apple/capricorn.png b/public/images/emoji/apple/capricorn.png new file mode 100644 index 0000000000..04b4c6ef71 Binary files /dev/null and b/public/images/emoji/apple/capricorn.png differ diff --git a/public/images/emoji/apple/car.png b/public/images/emoji/apple/car.png new file mode 100644 index 0000000000..b5d86c4def Binary files /dev/null and b/public/images/emoji/apple/car.png differ diff --git a/public/images/emoji/apple/card_index.png b/public/images/emoji/apple/card_index.png new file mode 100644 index 0000000000..bfaed033e5 Binary files /dev/null and b/public/images/emoji/apple/card_index.png differ diff --git a/public/images/emoji/apple/carousel_horse.png b/public/images/emoji/apple/carousel_horse.png new file mode 100644 index 0000000000..17b1eb51fb Binary files /dev/null and b/public/images/emoji/apple/carousel_horse.png differ diff --git a/public/images/emoji/apple/cat.png b/public/images/emoji/apple/cat.png new file mode 100644 index 0000000000..147dad43cf Binary files /dev/null and b/public/images/emoji/apple/cat.png differ diff --git a/public/images/emoji/apple/cat2.png b/public/images/emoji/apple/cat2.png new file mode 100644 index 0000000000..5192f8c56a Binary files /dev/null and b/public/images/emoji/apple/cat2.png differ diff --git a/public/images/emoji/apple/cd.png b/public/images/emoji/apple/cd.png new file mode 100644 index 0000000000..4efc27dcd5 Binary files /dev/null and b/public/images/emoji/apple/cd.png differ diff --git a/public/images/emoji/apple/chart.png b/public/images/emoji/apple/chart.png new file mode 100644 index 0000000000..ee8e8f81bf Binary files /dev/null and b/public/images/emoji/apple/chart.png differ diff --git a/public/images/emoji/apple/chart_with_downwards_trend.png b/public/images/emoji/apple/chart_with_downwards_trend.png new file mode 100644 index 0000000000..2698a455c3 Binary files /dev/null and b/public/images/emoji/apple/chart_with_downwards_trend.png differ diff --git a/public/images/emoji/apple/chart_with_upwards_trend.png b/public/images/emoji/apple/chart_with_upwards_trend.png new file mode 100644 index 0000000000..cdc80d4b62 Binary files /dev/null and b/public/images/emoji/apple/chart_with_upwards_trend.png differ diff --git a/public/images/emoji/apple/checkered_flag.png b/public/images/emoji/apple/checkered_flag.png new file mode 100644 index 0000000000..ff10a1d54e Binary files /dev/null and b/public/images/emoji/apple/checkered_flag.png differ diff --git a/public/images/emoji/apple/cherries.png b/public/images/emoji/apple/cherries.png new file mode 100644 index 0000000000..16c48b0323 Binary files /dev/null and b/public/images/emoji/apple/cherries.png differ diff --git a/public/images/emoji/apple/cherry_blossom.png b/public/images/emoji/apple/cherry_blossom.png new file mode 100644 index 0000000000..abb6c3d998 Binary files /dev/null and b/public/images/emoji/apple/cherry_blossom.png differ diff --git a/public/images/emoji/apple/chestnut.png b/public/images/emoji/apple/chestnut.png new file mode 100644 index 0000000000..fc87a87972 Binary files /dev/null and b/public/images/emoji/apple/chestnut.png differ diff --git a/public/images/emoji/apple/chicken.png b/public/images/emoji/apple/chicken.png new file mode 100644 index 0000000000..6243d8d611 Binary files /dev/null and b/public/images/emoji/apple/chicken.png differ diff --git a/public/images/emoji/apple/children_crossing.png b/public/images/emoji/apple/children_crossing.png new file mode 100644 index 0000000000..c0f34d93d1 Binary files /dev/null and b/public/images/emoji/apple/children_crossing.png differ diff --git a/public/images/emoji/apple/chocolate_bar.png b/public/images/emoji/apple/chocolate_bar.png new file mode 100644 index 0000000000..3d78c230f3 Binary files /dev/null and b/public/images/emoji/apple/chocolate_bar.png differ diff --git a/public/images/emoji/apple/christmas_tree.png b/public/images/emoji/apple/christmas_tree.png new file mode 100644 index 0000000000..cf58a681e5 Binary files /dev/null and b/public/images/emoji/apple/christmas_tree.png differ diff --git a/public/images/emoji/apple/church.png b/public/images/emoji/apple/church.png new file mode 100644 index 0000000000..99109f9f64 Binary files /dev/null and b/public/images/emoji/apple/church.png differ diff --git a/public/images/emoji/apple/cinema.png b/public/images/emoji/apple/cinema.png new file mode 100644 index 0000000000..0cd0135c9e Binary files /dev/null and b/public/images/emoji/apple/cinema.png differ diff --git a/public/images/emoji/apple/circus_tent.png b/public/images/emoji/apple/circus_tent.png new file mode 100644 index 0000000000..fe663269e1 Binary files /dev/null and b/public/images/emoji/apple/circus_tent.png differ diff --git a/public/images/emoji/apple/city_sunrise.png b/public/images/emoji/apple/city_sunrise.png new file mode 100644 index 0000000000..ac0380f47f Binary files /dev/null and b/public/images/emoji/apple/city_sunrise.png differ diff --git a/public/images/emoji/apple/city_sunset.png b/public/images/emoji/apple/city_sunset.png new file mode 100644 index 0000000000..2eb5754e1a Binary files /dev/null and b/public/images/emoji/apple/city_sunset.png differ diff --git a/public/images/emoji/apple/cl.png b/public/images/emoji/apple/cl.png new file mode 100644 index 0000000000..25dc41637f Binary files /dev/null and b/public/images/emoji/apple/cl.png differ diff --git a/public/images/emoji/apple/clap.png b/public/images/emoji/apple/clap.png new file mode 100644 index 0000000000..44f6d3130f Binary files /dev/null and b/public/images/emoji/apple/clap.png differ diff --git a/public/images/emoji/apple/clapper.png b/public/images/emoji/apple/clapper.png new file mode 100644 index 0000000000..9c793bd969 Binary files /dev/null and b/public/images/emoji/apple/clapper.png differ diff --git a/public/images/emoji/apple/clipboard.png b/public/images/emoji/apple/clipboard.png new file mode 100644 index 0000000000..c01719f86c Binary files /dev/null and b/public/images/emoji/apple/clipboard.png differ diff --git a/public/images/emoji/apple/clock1.png b/public/images/emoji/apple/clock1.png new file mode 100644 index 0000000000..1826755cf4 Binary files /dev/null and b/public/images/emoji/apple/clock1.png differ diff --git a/public/images/emoji/apple/clock10.png b/public/images/emoji/apple/clock10.png new file mode 100644 index 0000000000..e7c1c2bf72 Binary files /dev/null and b/public/images/emoji/apple/clock10.png differ diff --git a/public/images/emoji/apple/clock1030.png b/public/images/emoji/apple/clock1030.png new file mode 100644 index 0000000000..e39d969b33 Binary files /dev/null and b/public/images/emoji/apple/clock1030.png differ diff --git a/public/images/emoji/apple/clock11.png b/public/images/emoji/apple/clock11.png new file mode 100644 index 0000000000..8dd8ad49e1 Binary files /dev/null and b/public/images/emoji/apple/clock11.png differ diff --git a/public/images/emoji/apple/clock1130.png b/public/images/emoji/apple/clock1130.png new file mode 100644 index 0000000000..dd1fd36ce1 Binary files /dev/null and b/public/images/emoji/apple/clock1130.png differ diff --git a/public/images/emoji/apple/clock12.png b/public/images/emoji/apple/clock12.png new file mode 100644 index 0000000000..1bf191d5c7 Binary files /dev/null and b/public/images/emoji/apple/clock12.png differ diff --git a/public/images/emoji/apple/clock1230.png b/public/images/emoji/apple/clock1230.png new file mode 100644 index 0000000000..ab5262fe7d Binary files /dev/null and b/public/images/emoji/apple/clock1230.png differ diff --git a/public/images/emoji/apple/clock130.png b/public/images/emoji/apple/clock130.png new file mode 100644 index 0000000000..9b0787837c Binary files /dev/null and b/public/images/emoji/apple/clock130.png differ diff --git a/public/images/emoji/apple/clock2.png b/public/images/emoji/apple/clock2.png new file mode 100644 index 0000000000..c3548f80ba Binary files /dev/null and b/public/images/emoji/apple/clock2.png differ diff --git a/public/images/emoji/apple/clock230.png b/public/images/emoji/apple/clock230.png new file mode 100644 index 0000000000..537f9677ac Binary files /dev/null and b/public/images/emoji/apple/clock230.png differ diff --git a/public/images/emoji/apple/clock3.png b/public/images/emoji/apple/clock3.png new file mode 100644 index 0000000000..ba5c1f439b Binary files /dev/null and b/public/images/emoji/apple/clock3.png differ diff --git a/public/images/emoji/apple/clock330.png b/public/images/emoji/apple/clock330.png new file mode 100644 index 0000000000..13bc5b5145 Binary files /dev/null and b/public/images/emoji/apple/clock330.png differ diff --git a/public/images/emoji/apple/clock4.png b/public/images/emoji/apple/clock4.png new file mode 100644 index 0000000000..02a7fe2f1b Binary files /dev/null and b/public/images/emoji/apple/clock4.png differ diff --git a/public/images/emoji/apple/clock430.png b/public/images/emoji/apple/clock430.png new file mode 100644 index 0000000000..e213359a11 Binary files /dev/null and b/public/images/emoji/apple/clock430.png differ diff --git a/public/images/emoji/apple/clock5.png b/public/images/emoji/apple/clock5.png new file mode 100644 index 0000000000..f6e00cc61b Binary files /dev/null and b/public/images/emoji/apple/clock5.png differ diff --git a/public/images/emoji/apple/clock530.png b/public/images/emoji/apple/clock530.png new file mode 100644 index 0000000000..7305fc046a Binary files /dev/null and b/public/images/emoji/apple/clock530.png differ diff --git a/public/images/emoji/apple/clock6.png b/public/images/emoji/apple/clock6.png new file mode 100644 index 0000000000..4fccf99807 Binary files /dev/null and b/public/images/emoji/apple/clock6.png differ diff --git a/public/images/emoji/apple/clock630.png b/public/images/emoji/apple/clock630.png new file mode 100644 index 0000000000..100736c1fd Binary files /dev/null and b/public/images/emoji/apple/clock630.png differ diff --git a/public/images/emoji/apple/clock7.png b/public/images/emoji/apple/clock7.png new file mode 100644 index 0000000000..159bfdaa71 Binary files /dev/null and b/public/images/emoji/apple/clock7.png differ diff --git a/public/images/emoji/apple/clock730.png b/public/images/emoji/apple/clock730.png new file mode 100644 index 0000000000..c7cb99a0f0 Binary files /dev/null and b/public/images/emoji/apple/clock730.png differ diff --git a/public/images/emoji/apple/clock8.png b/public/images/emoji/apple/clock8.png new file mode 100644 index 0000000000..d481c1c57d Binary files /dev/null and b/public/images/emoji/apple/clock8.png differ diff --git a/public/images/emoji/apple/clock830.png b/public/images/emoji/apple/clock830.png new file mode 100644 index 0000000000..85ef43d7ef Binary files /dev/null and b/public/images/emoji/apple/clock830.png differ diff --git a/public/images/emoji/apple/clock9.png b/public/images/emoji/apple/clock9.png new file mode 100644 index 0000000000..d2114ff4b8 Binary files /dev/null and b/public/images/emoji/apple/clock9.png differ diff --git a/public/images/emoji/apple/clock930.png b/public/images/emoji/apple/clock930.png new file mode 100644 index 0000000000..34af13eb60 Binary files /dev/null and b/public/images/emoji/apple/clock930.png differ diff --git a/public/images/emoji/apple/closed_book.png b/public/images/emoji/apple/closed_book.png new file mode 100644 index 0000000000..a24ad94400 Binary files /dev/null and b/public/images/emoji/apple/closed_book.png differ diff --git a/public/images/emoji/apple/closed_lock_with_key.png b/public/images/emoji/apple/closed_lock_with_key.png new file mode 100644 index 0000000000..18d3d693c8 Binary files /dev/null and b/public/images/emoji/apple/closed_lock_with_key.png differ diff --git a/public/images/emoji/apple/closed_umbrella.png b/public/images/emoji/apple/closed_umbrella.png new file mode 100644 index 0000000000..596a31236a Binary files /dev/null and b/public/images/emoji/apple/closed_umbrella.png differ diff --git a/public/images/emoji/apple/cloud.png b/public/images/emoji/apple/cloud.png new file mode 100644 index 0000000000..8e51f77254 Binary files /dev/null and b/public/images/emoji/apple/cloud.png differ diff --git a/public/images/emoji/apple/clubs.png b/public/images/emoji/apple/clubs.png new file mode 100644 index 0000000000..230d9d032e Binary files /dev/null and b/public/images/emoji/apple/clubs.png differ diff --git a/public/images/emoji/apple/cn.png b/public/images/emoji/apple/cn.png new file mode 100644 index 0000000000..5a132ac7cb Binary files /dev/null and b/public/images/emoji/apple/cn.png differ diff --git a/public/images/emoji/apple/cocktail.png b/public/images/emoji/apple/cocktail.png new file mode 100644 index 0000000000..9dcfbce91b Binary files /dev/null and b/public/images/emoji/apple/cocktail.png differ diff --git a/public/images/emoji/apple/coffee.png b/public/images/emoji/apple/coffee.png new file mode 100644 index 0000000000..d2e343dbb0 Binary files /dev/null and b/public/images/emoji/apple/coffee.png differ diff --git a/public/images/emoji/apple/cold_sweat.png b/public/images/emoji/apple/cold_sweat.png new file mode 100644 index 0000000000..0d22e62fa8 Binary files /dev/null and b/public/images/emoji/apple/cold_sweat.png differ diff --git a/public/images/emoji/apple/collision.png b/public/images/emoji/apple/collision.png new file mode 100644 index 0000000000..c16baf2aec Binary files /dev/null and b/public/images/emoji/apple/collision.png differ diff --git a/public/images/emoji/apple/computer.png b/public/images/emoji/apple/computer.png new file mode 100644 index 0000000000..9f6b8be581 Binary files /dev/null and b/public/images/emoji/apple/computer.png differ diff --git a/public/images/emoji/apple/confetti_ball.png b/public/images/emoji/apple/confetti_ball.png new file mode 100644 index 0000000000..742816ab44 Binary files /dev/null and b/public/images/emoji/apple/confetti_ball.png differ diff --git a/public/images/emoji/apple/confounded.png b/public/images/emoji/apple/confounded.png new file mode 100644 index 0000000000..cb45c1c8c6 Binary files /dev/null and b/public/images/emoji/apple/confounded.png differ diff --git a/public/images/emoji/apple/confused.png b/public/images/emoji/apple/confused.png new file mode 100644 index 0000000000..ac533670c2 Binary files /dev/null and b/public/images/emoji/apple/confused.png differ diff --git a/public/images/emoji/apple/congratulations.png b/public/images/emoji/apple/congratulations.png new file mode 100644 index 0000000000..41ef1b63d5 Binary files /dev/null and b/public/images/emoji/apple/congratulations.png differ diff --git a/public/images/emoji/apple/construction.png b/public/images/emoji/apple/construction.png new file mode 100644 index 0000000000..a32fbd4233 Binary files /dev/null and b/public/images/emoji/apple/construction.png differ diff --git a/public/images/emoji/apple/construction_worker.png b/public/images/emoji/apple/construction_worker.png new file mode 100644 index 0000000000..8768e47de0 Binary files /dev/null and b/public/images/emoji/apple/construction_worker.png differ diff --git a/public/images/emoji/apple/convenience_store.png b/public/images/emoji/apple/convenience_store.png new file mode 100644 index 0000000000..b8fba9aab0 Binary files /dev/null and b/public/images/emoji/apple/convenience_store.png differ diff --git a/public/images/emoji/apple/cookie.png b/public/images/emoji/apple/cookie.png new file mode 100644 index 0000000000..2a6feff345 Binary files /dev/null and b/public/images/emoji/apple/cookie.png differ diff --git a/public/images/emoji/apple/cool.png b/public/images/emoji/apple/cool.png new file mode 100644 index 0000000000..d901fe824c Binary files /dev/null and b/public/images/emoji/apple/cool.png differ diff --git a/public/images/emoji/apple/cop.png b/public/images/emoji/apple/cop.png new file mode 100644 index 0000000000..88131338ae Binary files /dev/null and b/public/images/emoji/apple/cop.png differ diff --git a/public/images/emoji/apple/copyright.png b/public/images/emoji/apple/copyright.png new file mode 100644 index 0000000000..a7578a29b5 Binary files /dev/null and b/public/images/emoji/apple/copyright.png differ diff --git a/public/images/emoji/apple/corn.png b/public/images/emoji/apple/corn.png new file mode 100644 index 0000000000..75825d1d52 Binary files /dev/null and b/public/images/emoji/apple/corn.png differ diff --git a/public/images/emoji/apple/couple.png b/public/images/emoji/apple/couple.png new file mode 100644 index 0000000000..442448bb63 Binary files /dev/null and b/public/images/emoji/apple/couple.png differ diff --git a/public/images/emoji/apple/couple_with_heart.png b/public/images/emoji/apple/couple_with_heart.png new file mode 100644 index 0000000000..41490f6d18 Binary files /dev/null and b/public/images/emoji/apple/couple_with_heart.png differ diff --git a/public/images/emoji/apple/couplekiss.png b/public/images/emoji/apple/couplekiss.png new file mode 100644 index 0000000000..cc28fa8487 Binary files /dev/null and b/public/images/emoji/apple/couplekiss.png differ diff --git a/public/images/emoji/apple/cow.png b/public/images/emoji/apple/cow.png new file mode 100644 index 0000000000..61e0b7d560 Binary files /dev/null and b/public/images/emoji/apple/cow.png differ diff --git a/public/images/emoji/apple/cow2.png b/public/images/emoji/apple/cow2.png new file mode 100644 index 0000000000..2143ba5959 Binary files /dev/null and b/public/images/emoji/apple/cow2.png differ diff --git a/public/images/emoji/apple/credit_card.png b/public/images/emoji/apple/credit_card.png new file mode 100644 index 0000000000..cff26e4080 Binary files /dev/null and b/public/images/emoji/apple/credit_card.png differ diff --git a/public/images/emoji/apple/crescent_moon.png b/public/images/emoji/apple/crescent_moon.png new file mode 100644 index 0000000000..f9b393baca Binary files /dev/null and b/public/images/emoji/apple/crescent_moon.png differ diff --git a/public/images/emoji/apple/crocodile.png b/public/images/emoji/apple/crocodile.png new file mode 100644 index 0000000000..902210255d Binary files /dev/null and b/public/images/emoji/apple/crocodile.png differ diff --git a/public/images/emoji/apple/crossed_flags.png b/public/images/emoji/apple/crossed_flags.png new file mode 100644 index 0000000000..4d2b611bfb Binary files /dev/null and b/public/images/emoji/apple/crossed_flags.png differ diff --git a/public/images/emoji/apple/crown.png b/public/images/emoji/apple/crown.png new file mode 100644 index 0000000000..13f55e7861 Binary files /dev/null and b/public/images/emoji/apple/crown.png differ diff --git a/public/images/emoji/apple/cry.png b/public/images/emoji/apple/cry.png new file mode 100644 index 0000000000..1718b42ed4 Binary files /dev/null and b/public/images/emoji/apple/cry.png differ diff --git a/public/images/emoji/apple/crying_cat_face.png b/public/images/emoji/apple/crying_cat_face.png new file mode 100644 index 0000000000..3b3a47159a Binary files /dev/null and b/public/images/emoji/apple/crying_cat_face.png differ diff --git a/public/images/emoji/apple/crystal_ball.png b/public/images/emoji/apple/crystal_ball.png new file mode 100644 index 0000000000..bee8b57650 Binary files /dev/null and b/public/images/emoji/apple/crystal_ball.png differ diff --git a/public/images/emoji/apple/cupid.png b/public/images/emoji/apple/cupid.png new file mode 100644 index 0000000000..468a8cc1c7 Binary files /dev/null and b/public/images/emoji/apple/cupid.png differ diff --git a/public/images/emoji/apple/curly_loop.png b/public/images/emoji/apple/curly_loop.png new file mode 100644 index 0000000000..e042005ed8 Binary files /dev/null and b/public/images/emoji/apple/curly_loop.png differ diff --git a/public/images/emoji/apple/currency_exchange.png b/public/images/emoji/apple/currency_exchange.png new file mode 100644 index 0000000000..dbec08ff5c Binary files /dev/null and b/public/images/emoji/apple/currency_exchange.png differ diff --git a/public/images/emoji/apple/curry.png b/public/images/emoji/apple/curry.png new file mode 100644 index 0000000000..a36875225f Binary files /dev/null and b/public/images/emoji/apple/curry.png differ diff --git a/public/images/emoji/apple/custard.png b/public/images/emoji/apple/custard.png new file mode 100644 index 0000000000..c851e10338 Binary files /dev/null and b/public/images/emoji/apple/custard.png differ diff --git a/public/images/emoji/apple/customs.png b/public/images/emoji/apple/customs.png new file mode 100644 index 0000000000..71a647d788 Binary files /dev/null and b/public/images/emoji/apple/customs.png differ diff --git a/public/images/emoji/apple/cyclone.png b/public/images/emoji/apple/cyclone.png new file mode 100644 index 0000000000..0aea4feff7 Binary files /dev/null and b/public/images/emoji/apple/cyclone.png differ diff --git a/public/images/emoji/apple/dancer.png b/public/images/emoji/apple/dancer.png new file mode 100644 index 0000000000..c4ee0d6dee Binary files /dev/null and b/public/images/emoji/apple/dancer.png differ diff --git a/public/images/emoji/apple/dancers.png b/public/images/emoji/apple/dancers.png new file mode 100644 index 0000000000..b3f2be5dba Binary files /dev/null and b/public/images/emoji/apple/dancers.png differ diff --git a/public/images/emoji/apple/dango.png b/public/images/emoji/apple/dango.png new file mode 100644 index 0000000000..493edaa4d1 Binary files /dev/null and b/public/images/emoji/apple/dango.png differ diff --git a/public/images/emoji/apple/dart.png b/public/images/emoji/apple/dart.png new file mode 100644 index 0000000000..d40e087766 Binary files /dev/null and b/public/images/emoji/apple/dart.png differ diff --git a/public/images/emoji/apple/dash.png b/public/images/emoji/apple/dash.png new file mode 100644 index 0000000000..bdc0d85a32 Binary files /dev/null and b/public/images/emoji/apple/dash.png differ diff --git a/public/images/emoji/apple/date.png b/public/images/emoji/apple/date.png new file mode 100644 index 0000000000..cf88ac2bbc Binary files /dev/null and b/public/images/emoji/apple/date.png differ diff --git a/public/images/emoji/apple/de.png b/public/images/emoji/apple/de.png new file mode 100644 index 0000000000..28d4a2f142 Binary files /dev/null and b/public/images/emoji/apple/de.png differ diff --git a/public/images/emoji/apple/deciduous_tree.png b/public/images/emoji/apple/deciduous_tree.png new file mode 100644 index 0000000000..2dd403043b Binary files /dev/null and b/public/images/emoji/apple/deciduous_tree.png differ diff --git a/public/images/emoji/apple/department_store.png b/public/images/emoji/apple/department_store.png new file mode 100644 index 0000000000..024ea83115 Binary files /dev/null and b/public/images/emoji/apple/department_store.png differ diff --git a/public/images/emoji/apple/diamond_shape_with_a_dot_inside.png b/public/images/emoji/apple/diamond_shape_with_a_dot_inside.png new file mode 100644 index 0000000000..ca5ac05d2c Binary files /dev/null and b/public/images/emoji/apple/diamond_shape_with_a_dot_inside.png differ diff --git a/public/images/emoji/apple/diamonds.png b/public/images/emoji/apple/diamonds.png new file mode 100644 index 0000000000..08ddc3872c Binary files /dev/null and b/public/images/emoji/apple/diamonds.png differ diff --git a/public/images/emoji/apple/disappointed.png b/public/images/emoji/apple/disappointed.png new file mode 100644 index 0000000000..0d3f52286f Binary files /dev/null and b/public/images/emoji/apple/disappointed.png differ diff --git a/public/images/emoji/apple/disappointed_relieved.png b/public/images/emoji/apple/disappointed_relieved.png new file mode 100644 index 0000000000..4a7a9fd69d Binary files /dev/null and b/public/images/emoji/apple/disappointed_relieved.png differ diff --git a/public/images/emoji/apple/dizzy.png b/public/images/emoji/apple/dizzy.png new file mode 100644 index 0000000000..94314d0d0a Binary files /dev/null and b/public/images/emoji/apple/dizzy.png differ diff --git a/public/images/emoji/apple/dizzy_face.png b/public/images/emoji/apple/dizzy_face.png new file mode 100644 index 0000000000..92bb5851c7 Binary files /dev/null and b/public/images/emoji/apple/dizzy_face.png differ diff --git a/public/images/emoji/apple/do_not_litter.png b/public/images/emoji/apple/do_not_litter.png new file mode 100644 index 0000000000..87a95f9d80 Binary files /dev/null and b/public/images/emoji/apple/do_not_litter.png differ diff --git a/public/images/emoji/apple/dog.png b/public/images/emoji/apple/dog.png new file mode 100644 index 0000000000..c83e5ceb16 Binary files /dev/null and b/public/images/emoji/apple/dog.png differ diff --git a/public/images/emoji/apple/dog2.png b/public/images/emoji/apple/dog2.png new file mode 100644 index 0000000000..6070161781 Binary files /dev/null and b/public/images/emoji/apple/dog2.png differ diff --git a/public/images/emoji/apple/dollar.png b/public/images/emoji/apple/dollar.png new file mode 100644 index 0000000000..e002955849 Binary files /dev/null and b/public/images/emoji/apple/dollar.png differ diff --git a/public/images/emoji/apple/dolls.png b/public/images/emoji/apple/dolls.png new file mode 100644 index 0000000000..cbd0f30530 Binary files /dev/null and b/public/images/emoji/apple/dolls.png differ diff --git a/public/images/emoji/apple/dolphin.png b/public/images/emoji/apple/dolphin.png new file mode 100644 index 0000000000..083819034c Binary files /dev/null and b/public/images/emoji/apple/dolphin.png differ diff --git a/public/images/emoji/apple/door.png b/public/images/emoji/apple/door.png new file mode 100644 index 0000000000..8e5e6eab4e Binary files /dev/null and b/public/images/emoji/apple/door.png differ diff --git a/public/images/emoji/apple/doughnut.png b/public/images/emoji/apple/doughnut.png new file mode 100644 index 0000000000..2b6ff68fe8 Binary files /dev/null and b/public/images/emoji/apple/doughnut.png differ diff --git a/public/images/emoji/apple/dragon.png b/public/images/emoji/apple/dragon.png new file mode 100644 index 0000000000..b649b76df3 Binary files /dev/null and b/public/images/emoji/apple/dragon.png differ diff --git a/public/images/emoji/apple/dragon_face.png b/public/images/emoji/apple/dragon_face.png new file mode 100644 index 0000000000..326898d697 Binary files /dev/null and b/public/images/emoji/apple/dragon_face.png differ diff --git a/public/images/emoji/apple/dress.png b/public/images/emoji/apple/dress.png new file mode 100644 index 0000000000..55a6aa8fb0 Binary files /dev/null and b/public/images/emoji/apple/dress.png differ diff --git a/public/images/emoji/apple/dromedary_camel.png b/public/images/emoji/apple/dromedary_camel.png new file mode 100644 index 0000000000..54acbd1c2a Binary files /dev/null and b/public/images/emoji/apple/dromedary_camel.png differ diff --git a/public/images/emoji/apple/droplet.png b/public/images/emoji/apple/droplet.png new file mode 100644 index 0000000000..d28e56aa63 Binary files /dev/null and b/public/images/emoji/apple/droplet.png differ diff --git a/public/images/emoji/apple/dvd.png b/public/images/emoji/apple/dvd.png new file mode 100644 index 0000000000..2813986555 Binary files /dev/null and b/public/images/emoji/apple/dvd.png differ diff --git a/public/images/emoji/apple/e-mail.png b/public/images/emoji/apple/e-mail.png new file mode 100644 index 0000000000..858234e9a3 Binary files /dev/null and b/public/images/emoji/apple/e-mail.png differ diff --git a/public/images/emoji/apple/ear.png b/public/images/emoji/apple/ear.png new file mode 100644 index 0000000000..c8e64b560d Binary files /dev/null and b/public/images/emoji/apple/ear.png differ diff --git a/public/images/emoji/apple/ear_of_rice.png b/public/images/emoji/apple/ear_of_rice.png new file mode 100644 index 0000000000..3fc2d4cf76 Binary files /dev/null and b/public/images/emoji/apple/ear_of_rice.png differ diff --git a/public/images/emoji/apple/earth_africa.png b/public/images/emoji/apple/earth_africa.png new file mode 100644 index 0000000000..dbfb2a7ff7 Binary files /dev/null and b/public/images/emoji/apple/earth_africa.png differ diff --git a/public/images/emoji/apple/earth_americas.png b/public/images/emoji/apple/earth_americas.png new file mode 100644 index 0000000000..e18944d892 Binary files /dev/null and b/public/images/emoji/apple/earth_americas.png differ diff --git a/public/images/emoji/apple/earth_asia.png b/public/images/emoji/apple/earth_asia.png new file mode 100644 index 0000000000..9edcc3f88d Binary files /dev/null and b/public/images/emoji/apple/earth_asia.png differ diff --git a/public/images/emoji/apple/egg.png b/public/images/emoji/apple/egg.png new file mode 100644 index 0000000000..af0f846ba7 Binary files /dev/null and b/public/images/emoji/apple/egg.png differ diff --git a/public/images/emoji/apple/eggplant.png b/public/images/emoji/apple/eggplant.png new file mode 100644 index 0000000000..13e3636314 Binary files /dev/null and b/public/images/emoji/apple/eggplant.png differ diff --git a/public/images/emoji/apple/eight.png b/public/images/emoji/apple/eight.png new file mode 100644 index 0000000000..f76e43d179 Binary files /dev/null and b/public/images/emoji/apple/eight.png differ diff --git a/public/images/emoji/apple/eight_pointed_black_star.png b/public/images/emoji/apple/eight_pointed_black_star.png new file mode 100644 index 0000000000..6f4b4eac98 Binary files /dev/null and b/public/images/emoji/apple/eight_pointed_black_star.png differ diff --git a/public/images/emoji/apple/eight_spoked_asterisk.png b/public/images/emoji/apple/eight_spoked_asterisk.png new file mode 100644 index 0000000000..efb7f02f52 Binary files /dev/null and b/public/images/emoji/apple/eight_spoked_asterisk.png differ diff --git a/public/images/emoji/apple/electric_plug.png b/public/images/emoji/apple/electric_plug.png new file mode 100644 index 0000000000..4262ad05cd Binary files /dev/null and b/public/images/emoji/apple/electric_plug.png differ diff --git a/public/images/emoji/apple/elephant.png b/public/images/emoji/apple/elephant.png new file mode 100644 index 0000000000..8fe220de84 Binary files /dev/null and b/public/images/emoji/apple/elephant.png differ diff --git a/public/images/emoji/apple/email.png b/public/images/emoji/apple/email.png new file mode 100644 index 0000000000..e843883b5c Binary files /dev/null and b/public/images/emoji/apple/email.png differ diff --git a/public/images/emoji/apple/end.png b/public/images/emoji/apple/end.png new file mode 100644 index 0000000000..ae7b020ce9 Binary files /dev/null and b/public/images/emoji/apple/end.png differ diff --git a/public/images/emoji/apple/envelope.png b/public/images/emoji/apple/envelope.png new file mode 100644 index 0000000000..e843883b5c Binary files /dev/null and b/public/images/emoji/apple/envelope.png differ diff --git a/public/images/emoji/apple/envelope_with_arrow.png b/public/images/emoji/apple/envelope_with_arrow.png new file mode 100644 index 0000000000..24a4f008b5 Binary files /dev/null and b/public/images/emoji/apple/envelope_with_arrow.png differ diff --git a/public/images/emoji/apple/es.png b/public/images/emoji/apple/es.png new file mode 100644 index 0000000000..afcdcf7b40 Binary files /dev/null and b/public/images/emoji/apple/es.png differ diff --git a/public/images/emoji/apple/euro.png b/public/images/emoji/apple/euro.png new file mode 100644 index 0000000000..17e0d34ace Binary files /dev/null and b/public/images/emoji/apple/euro.png differ diff --git a/public/images/emoji/apple/european_castle.png b/public/images/emoji/apple/european_castle.png new file mode 100644 index 0000000000..3f513b8a94 Binary files /dev/null and b/public/images/emoji/apple/european_castle.png differ diff --git a/public/images/emoji/apple/european_post_office.png b/public/images/emoji/apple/european_post_office.png new file mode 100644 index 0000000000..193d6a5b0b Binary files /dev/null and b/public/images/emoji/apple/european_post_office.png differ diff --git a/public/images/emoji/apple/evergreen_tree.png b/public/images/emoji/apple/evergreen_tree.png new file mode 100644 index 0000000000..4710e8861f Binary files /dev/null and b/public/images/emoji/apple/evergreen_tree.png differ diff --git a/public/images/emoji/apple/exclamation.png b/public/images/emoji/apple/exclamation.png new file mode 100644 index 0000000000..43f4583418 Binary files /dev/null and b/public/images/emoji/apple/exclamation.png differ diff --git a/public/images/emoji/apple/expressionless.png b/public/images/emoji/apple/expressionless.png new file mode 100644 index 0000000000..4882027ec9 Binary files /dev/null and b/public/images/emoji/apple/expressionless.png differ diff --git a/public/images/emoji/apple/eyeglasses.png b/public/images/emoji/apple/eyeglasses.png new file mode 100644 index 0000000000..9f4e057a7a Binary files /dev/null and b/public/images/emoji/apple/eyeglasses.png differ diff --git a/public/images/emoji/apple/eyes.png b/public/images/emoji/apple/eyes.png new file mode 100644 index 0000000000..f8ed2b2b2d Binary files /dev/null and b/public/images/emoji/apple/eyes.png differ diff --git a/public/images/emoji/apple/facepunch.png b/public/images/emoji/apple/facepunch.png new file mode 100644 index 0000000000..3a76b98e78 Binary files /dev/null and b/public/images/emoji/apple/facepunch.png differ diff --git a/public/images/emoji/apple/factory.png b/public/images/emoji/apple/factory.png new file mode 100644 index 0000000000..5e196b31c5 Binary files /dev/null and b/public/images/emoji/apple/factory.png differ diff --git a/public/images/emoji/apple/fallen_leaf.png b/public/images/emoji/apple/fallen_leaf.png new file mode 100644 index 0000000000..1804751ac3 Binary files /dev/null and b/public/images/emoji/apple/fallen_leaf.png differ diff --git a/public/images/emoji/apple/family.png b/public/images/emoji/apple/family.png new file mode 100644 index 0000000000..f4e470e577 Binary files /dev/null and b/public/images/emoji/apple/family.png differ diff --git a/public/images/emoji/apple/fast_forward.png b/public/images/emoji/apple/fast_forward.png new file mode 100644 index 0000000000..c5636e73a0 Binary files /dev/null and b/public/images/emoji/apple/fast_forward.png differ diff --git a/public/images/emoji/apple/fax.png b/public/images/emoji/apple/fax.png new file mode 100644 index 0000000000..ad5def318d Binary files /dev/null and b/public/images/emoji/apple/fax.png differ diff --git a/public/images/emoji/apple/fearful.png b/public/images/emoji/apple/fearful.png new file mode 100644 index 0000000000..086503a7f6 Binary files /dev/null and b/public/images/emoji/apple/fearful.png differ diff --git a/public/images/emoji/apple/feet.png b/public/images/emoji/apple/feet.png new file mode 100644 index 0000000000..f80ff2e67a Binary files /dev/null and b/public/images/emoji/apple/feet.png differ diff --git a/public/images/emoji/apple/ferris_wheel.png b/public/images/emoji/apple/ferris_wheel.png new file mode 100644 index 0000000000..5ae1b8110a Binary files /dev/null and b/public/images/emoji/apple/ferris_wheel.png differ diff --git a/public/images/emoji/apple/file_folder.png b/public/images/emoji/apple/file_folder.png new file mode 100644 index 0000000000..da3462be08 Binary files /dev/null and b/public/images/emoji/apple/file_folder.png differ diff --git a/public/images/emoji/apple/fire.png b/public/images/emoji/apple/fire.png new file mode 100644 index 0000000000..a819ed95f1 Binary files /dev/null and b/public/images/emoji/apple/fire.png differ diff --git a/public/images/emoji/apple/fire_engine.png b/public/images/emoji/apple/fire_engine.png new file mode 100644 index 0000000000..d7df6e86bd Binary files /dev/null and b/public/images/emoji/apple/fire_engine.png differ diff --git a/public/images/emoji/apple/fireworks.png b/public/images/emoji/apple/fireworks.png new file mode 100644 index 0000000000..809342d460 Binary files /dev/null and b/public/images/emoji/apple/fireworks.png differ diff --git a/public/images/emoji/apple/first_quarter_moon.png b/public/images/emoji/apple/first_quarter_moon.png new file mode 100644 index 0000000000..adaf1d6cff Binary files /dev/null and b/public/images/emoji/apple/first_quarter_moon.png differ diff --git a/public/images/emoji/apple/first_quarter_moon_with_face.png b/public/images/emoji/apple/first_quarter_moon_with_face.png new file mode 100644 index 0000000000..c884089df5 Binary files /dev/null and b/public/images/emoji/apple/first_quarter_moon_with_face.png differ diff --git a/public/images/emoji/apple/fish.png b/public/images/emoji/apple/fish.png new file mode 100644 index 0000000000..8a4039ed25 Binary files /dev/null and b/public/images/emoji/apple/fish.png differ diff --git a/public/images/emoji/apple/fish_cake.png b/public/images/emoji/apple/fish_cake.png new file mode 100644 index 0000000000..8a80c2f42f Binary files /dev/null and b/public/images/emoji/apple/fish_cake.png differ diff --git a/public/images/emoji/apple/fishing_pole_and_fish.png b/public/images/emoji/apple/fishing_pole_and_fish.png new file mode 100644 index 0000000000..ffc6109710 Binary files /dev/null and b/public/images/emoji/apple/fishing_pole_and_fish.png differ diff --git a/public/images/emoji/apple/fist.png b/public/images/emoji/apple/fist.png new file mode 100644 index 0000000000..6456c7430c Binary files /dev/null and b/public/images/emoji/apple/fist.png differ diff --git a/public/images/emoji/apple/five.png b/public/images/emoji/apple/five.png new file mode 100644 index 0000000000..9555767a9f Binary files /dev/null and b/public/images/emoji/apple/five.png differ diff --git a/public/images/emoji/apple/flags.png b/public/images/emoji/apple/flags.png new file mode 100644 index 0000000000..578fa6e5df Binary files /dev/null and b/public/images/emoji/apple/flags.png differ diff --git a/public/images/emoji/apple/flashlight.png b/public/images/emoji/apple/flashlight.png new file mode 100644 index 0000000000..6f4487471b Binary files /dev/null and b/public/images/emoji/apple/flashlight.png differ diff --git a/public/images/emoji/apple/flipper.png b/public/images/emoji/apple/flipper.png new file mode 100644 index 0000000000..083819034c Binary files /dev/null and b/public/images/emoji/apple/flipper.png differ diff --git a/public/images/emoji/apple/floppy_disk.png b/public/images/emoji/apple/floppy_disk.png new file mode 100644 index 0000000000..f00a8d4e6c Binary files /dev/null and b/public/images/emoji/apple/floppy_disk.png differ diff --git a/public/images/emoji/apple/flower_playing_cards.png b/public/images/emoji/apple/flower_playing_cards.png new file mode 100644 index 0000000000..8cf8725462 Binary files /dev/null and b/public/images/emoji/apple/flower_playing_cards.png differ diff --git a/public/images/emoji/apple/flushed.png b/public/images/emoji/apple/flushed.png new file mode 100644 index 0000000000..e6aad287d0 Binary files /dev/null and b/public/images/emoji/apple/flushed.png differ diff --git a/public/images/emoji/apple/foggy.png b/public/images/emoji/apple/foggy.png new file mode 100644 index 0000000000..794601e1f0 Binary files /dev/null and b/public/images/emoji/apple/foggy.png differ diff --git a/public/images/emoji/apple/football.png b/public/images/emoji/apple/football.png new file mode 100644 index 0000000000..037f7a76e1 Binary files /dev/null and b/public/images/emoji/apple/football.png differ diff --git a/public/images/emoji/apple/footprints.png b/public/images/emoji/apple/footprints.png new file mode 100644 index 0000000000..3dbc1f795c Binary files /dev/null and b/public/images/emoji/apple/footprints.png differ diff --git a/public/images/emoji/apple/fork_and_knife.png b/public/images/emoji/apple/fork_and_knife.png new file mode 100644 index 0000000000..2fbad780ba Binary files /dev/null and b/public/images/emoji/apple/fork_and_knife.png differ diff --git a/public/images/emoji/apple/fountain.png b/public/images/emoji/apple/fountain.png new file mode 100644 index 0000000000..3e64f78fcb Binary files /dev/null and b/public/images/emoji/apple/fountain.png differ diff --git a/public/images/emoji/apple/four.png b/public/images/emoji/apple/four.png new file mode 100644 index 0000000000..e0bab58f05 Binary files /dev/null and b/public/images/emoji/apple/four.png differ diff --git a/public/images/emoji/apple/four_leaf_clover.png b/public/images/emoji/apple/four_leaf_clover.png new file mode 100644 index 0000000000..a4e25b1f3e Binary files /dev/null and b/public/images/emoji/apple/four_leaf_clover.png differ diff --git a/public/images/emoji/apple/fr.png b/public/images/emoji/apple/fr.png new file mode 100644 index 0000000000..c4ed930176 Binary files /dev/null and b/public/images/emoji/apple/fr.png differ diff --git a/public/images/emoji/apple/free.png b/public/images/emoji/apple/free.png new file mode 100644 index 0000000000..8f7e6d8a49 Binary files /dev/null and b/public/images/emoji/apple/free.png differ diff --git a/public/images/emoji/apple/fried_shrimp.png b/public/images/emoji/apple/fried_shrimp.png new file mode 100644 index 0000000000..4b6711a21a Binary files /dev/null and b/public/images/emoji/apple/fried_shrimp.png differ diff --git a/public/images/emoji/apple/fries.png b/public/images/emoji/apple/fries.png new file mode 100644 index 0000000000..38badf5e60 Binary files /dev/null and b/public/images/emoji/apple/fries.png differ diff --git a/public/images/emoji/apple/frog.png b/public/images/emoji/apple/frog.png new file mode 100644 index 0000000000..5abb7e2e38 Binary files /dev/null and b/public/images/emoji/apple/frog.png differ diff --git a/public/images/emoji/apple/frowning.png b/public/images/emoji/apple/frowning.png new file mode 100644 index 0000000000..e4dc6f0121 Binary files /dev/null and b/public/images/emoji/apple/frowning.png differ diff --git a/public/images/emoji/apple/fuelpump.png b/public/images/emoji/apple/fuelpump.png new file mode 100644 index 0000000000..1495737a6b Binary files /dev/null and b/public/images/emoji/apple/fuelpump.png differ diff --git a/public/images/emoji/apple/full_moon.png b/public/images/emoji/apple/full_moon.png new file mode 100644 index 0000000000..127f2dcdee Binary files /dev/null and b/public/images/emoji/apple/full_moon.png differ diff --git a/public/images/emoji/apple/full_moon_with_face.png b/public/images/emoji/apple/full_moon_with_face.png new file mode 100644 index 0000000000..ee30e5a027 Binary files /dev/null and b/public/images/emoji/apple/full_moon_with_face.png differ diff --git a/public/images/emoji/apple/game_die.png b/public/images/emoji/apple/game_die.png new file mode 100644 index 0000000000..02ee30d4ff Binary files /dev/null and b/public/images/emoji/apple/game_die.png differ diff --git a/public/images/emoji/apple/gb.png b/public/images/emoji/apple/gb.png new file mode 100644 index 0000000000..edb2ec7642 Binary files /dev/null and b/public/images/emoji/apple/gb.png differ diff --git a/public/images/emoji/apple/gem.png b/public/images/emoji/apple/gem.png new file mode 100644 index 0000000000..7122fba5f8 Binary files /dev/null and b/public/images/emoji/apple/gem.png differ diff --git a/public/images/emoji/apple/gemini.png b/public/images/emoji/apple/gemini.png new file mode 100644 index 0000000000..b1a40509ea Binary files /dev/null and b/public/images/emoji/apple/gemini.png differ diff --git a/public/images/emoji/apple/ghost.png b/public/images/emoji/apple/ghost.png new file mode 100644 index 0000000000..3aad399bd4 Binary files /dev/null and b/public/images/emoji/apple/ghost.png differ diff --git a/public/images/emoji/apple/gift.png b/public/images/emoji/apple/gift.png new file mode 100644 index 0000000000..4d5db2ef75 Binary files /dev/null and b/public/images/emoji/apple/gift.png differ diff --git a/public/images/emoji/apple/gift_heart.png b/public/images/emoji/apple/gift_heart.png new file mode 100644 index 0000000000..7715b6c744 Binary files /dev/null and b/public/images/emoji/apple/gift_heart.png differ diff --git a/public/images/emoji/apple/girl.png b/public/images/emoji/apple/girl.png new file mode 100644 index 0000000000..0c20fb9903 Binary files /dev/null and b/public/images/emoji/apple/girl.png differ diff --git a/public/images/emoji/apple/globe_with_meridians.png b/public/images/emoji/apple/globe_with_meridians.png new file mode 100644 index 0000000000..352316135b Binary files /dev/null and b/public/images/emoji/apple/globe_with_meridians.png differ diff --git a/public/images/emoji/apple/goat.png b/public/images/emoji/apple/goat.png new file mode 100644 index 0000000000..e3f39c2f79 Binary files /dev/null and b/public/images/emoji/apple/goat.png differ diff --git a/public/images/emoji/apple/golf.png b/public/images/emoji/apple/golf.png new file mode 100644 index 0000000000..49b743d3c2 Binary files /dev/null and b/public/images/emoji/apple/golf.png differ diff --git a/public/images/emoji/apple/grapes.png b/public/images/emoji/apple/grapes.png new file mode 100644 index 0000000000..1c72b6ac84 Binary files /dev/null and b/public/images/emoji/apple/grapes.png differ diff --git a/public/images/emoji/apple/green_apple.png b/public/images/emoji/apple/green_apple.png new file mode 100644 index 0000000000..ea4d2f1773 Binary files /dev/null and b/public/images/emoji/apple/green_apple.png differ diff --git a/public/images/emoji/apple/green_book.png b/public/images/emoji/apple/green_book.png new file mode 100644 index 0000000000..d7e53e3941 Binary files /dev/null and b/public/images/emoji/apple/green_book.png differ diff --git a/public/images/emoji/apple/green_heart.png b/public/images/emoji/apple/green_heart.png new file mode 100644 index 0000000000..6b006eadd8 Binary files /dev/null and b/public/images/emoji/apple/green_heart.png differ diff --git a/public/images/emoji/apple/grey_exclamation.png b/public/images/emoji/apple/grey_exclamation.png new file mode 100644 index 0000000000..02c4c84bdb Binary files /dev/null and b/public/images/emoji/apple/grey_exclamation.png differ diff --git a/public/images/emoji/apple/grey_question.png b/public/images/emoji/apple/grey_question.png new file mode 100644 index 0000000000..b36ee2d7d3 Binary files /dev/null and b/public/images/emoji/apple/grey_question.png differ diff --git a/public/images/emoji/apple/grimacing.png b/public/images/emoji/apple/grimacing.png new file mode 100644 index 0000000000..cd7f99ee18 Binary files /dev/null and b/public/images/emoji/apple/grimacing.png differ diff --git a/public/images/emoji/apple/grin.png b/public/images/emoji/apple/grin.png new file mode 100644 index 0000000000..d00c7e66fd Binary files /dev/null and b/public/images/emoji/apple/grin.png differ diff --git a/public/images/emoji/apple/grinning.png b/public/images/emoji/apple/grinning.png new file mode 100644 index 0000000000..77d0fd081a Binary files /dev/null and b/public/images/emoji/apple/grinning.png differ diff --git a/public/images/emoji/apple/guardsman.png b/public/images/emoji/apple/guardsman.png new file mode 100644 index 0000000000..e22e2b4047 Binary files /dev/null and b/public/images/emoji/apple/guardsman.png differ diff --git a/public/images/emoji/apple/guitar.png b/public/images/emoji/apple/guitar.png new file mode 100644 index 0000000000..aa92056c09 Binary files /dev/null and b/public/images/emoji/apple/guitar.png differ diff --git a/public/images/emoji/apple/gun.png b/public/images/emoji/apple/gun.png new file mode 100644 index 0000000000..a5a278eb33 Binary files /dev/null and b/public/images/emoji/apple/gun.png differ diff --git a/public/images/emoji/apple/haircut.png b/public/images/emoji/apple/haircut.png new file mode 100644 index 0000000000..8a2702166f Binary files /dev/null and b/public/images/emoji/apple/haircut.png differ diff --git a/public/images/emoji/apple/hamburger.png b/public/images/emoji/apple/hamburger.png new file mode 100644 index 0000000000..7fbc23ddb1 Binary files /dev/null and b/public/images/emoji/apple/hamburger.png differ diff --git a/public/images/emoji/apple/hammer.png b/public/images/emoji/apple/hammer.png new file mode 100644 index 0000000000..cb30c5d5e3 Binary files /dev/null and b/public/images/emoji/apple/hammer.png differ diff --git a/public/images/emoji/apple/hamster.png b/public/images/emoji/apple/hamster.png new file mode 100644 index 0000000000..3c3c509ab6 Binary files /dev/null and b/public/images/emoji/apple/hamster.png differ diff --git a/public/images/emoji/apple/hand.png b/public/images/emoji/apple/hand.png new file mode 100644 index 0000000000..dea3153758 Binary files /dev/null and b/public/images/emoji/apple/hand.png differ diff --git a/public/images/emoji/apple/handbag.png b/public/images/emoji/apple/handbag.png new file mode 100644 index 0000000000..aa0e951ad4 Binary files /dev/null and b/public/images/emoji/apple/handbag.png differ diff --git a/public/images/emoji/apple/hankey.png b/public/images/emoji/apple/hankey.png new file mode 100644 index 0000000000..75544655b3 Binary files /dev/null and b/public/images/emoji/apple/hankey.png differ diff --git a/public/images/emoji/apple/hash.png b/public/images/emoji/apple/hash.png new file mode 100644 index 0000000000..e1e5923d41 Binary files /dev/null and b/public/images/emoji/apple/hash.png differ diff --git a/public/images/emoji/apple/hatched_chick.png b/public/images/emoji/apple/hatched_chick.png new file mode 100644 index 0000000000..3e6d9bd73e Binary files /dev/null and b/public/images/emoji/apple/hatched_chick.png differ diff --git a/public/images/emoji/apple/hatching_chick.png b/public/images/emoji/apple/hatching_chick.png new file mode 100644 index 0000000000..1e11c41520 Binary files /dev/null and b/public/images/emoji/apple/hatching_chick.png differ diff --git a/public/images/emoji/apple/headphones.png b/public/images/emoji/apple/headphones.png new file mode 100644 index 0000000000..3c1a1240ca Binary files /dev/null and b/public/images/emoji/apple/headphones.png differ diff --git a/public/images/emoji/apple/hear_no_evil.png b/public/images/emoji/apple/hear_no_evil.png new file mode 100644 index 0000000000..7e8a512825 Binary files /dev/null and b/public/images/emoji/apple/hear_no_evil.png differ diff --git a/public/images/emoji/apple/heart.png b/public/images/emoji/apple/heart.png new file mode 100644 index 0000000000..8e908d53f9 Binary files /dev/null and b/public/images/emoji/apple/heart.png differ diff --git a/public/images/emoji/apple/heart_decoration.png b/public/images/emoji/apple/heart_decoration.png new file mode 100644 index 0000000000..458a0ca07f Binary files /dev/null and b/public/images/emoji/apple/heart_decoration.png differ diff --git a/public/images/emoji/apple/heart_eyes.png b/public/images/emoji/apple/heart_eyes.png new file mode 100644 index 0000000000..e63dbfda55 Binary files /dev/null and b/public/images/emoji/apple/heart_eyes.png differ diff --git a/public/images/emoji/apple/heart_eyes_cat.png b/public/images/emoji/apple/heart_eyes_cat.png new file mode 100644 index 0000000000..b978329cbc Binary files /dev/null and b/public/images/emoji/apple/heart_eyes_cat.png differ diff --git a/public/images/emoji/apple/heartbeat.png b/public/images/emoji/apple/heartbeat.png new file mode 100644 index 0000000000..9f4b41d32a Binary files /dev/null and b/public/images/emoji/apple/heartbeat.png differ diff --git a/public/images/emoji/apple/heartpulse.png b/public/images/emoji/apple/heartpulse.png new file mode 100644 index 0000000000..6e99d65f5c Binary files /dev/null and b/public/images/emoji/apple/heartpulse.png differ diff --git a/public/images/emoji/apple/hearts.png b/public/images/emoji/apple/hearts.png new file mode 100644 index 0000000000..bd48264b35 Binary files /dev/null and b/public/images/emoji/apple/hearts.png differ diff --git a/public/images/emoji/apple/heavy_check_mark.png b/public/images/emoji/apple/heavy_check_mark.png new file mode 100644 index 0000000000..34d8b9729e Binary files /dev/null and b/public/images/emoji/apple/heavy_check_mark.png differ diff --git a/public/images/emoji/apple/heavy_division_sign.png b/public/images/emoji/apple/heavy_division_sign.png new file mode 100644 index 0000000000..1604f29170 Binary files /dev/null and b/public/images/emoji/apple/heavy_division_sign.png differ diff --git a/public/images/emoji/apple/heavy_dollar_sign.png b/public/images/emoji/apple/heavy_dollar_sign.png new file mode 100644 index 0000000000..103e0a4ce3 Binary files /dev/null and b/public/images/emoji/apple/heavy_dollar_sign.png differ diff --git a/public/images/emoji/apple/heavy_exclamation_mark.png b/public/images/emoji/apple/heavy_exclamation_mark.png new file mode 100644 index 0000000000..43f4583418 Binary files /dev/null and b/public/images/emoji/apple/heavy_exclamation_mark.png differ diff --git a/public/images/emoji/apple/heavy_minus_sign.png b/public/images/emoji/apple/heavy_minus_sign.png new file mode 100644 index 0000000000..6c64405f73 Binary files /dev/null and b/public/images/emoji/apple/heavy_minus_sign.png differ diff --git a/public/images/emoji/apple/heavy_multiplication_x.png b/public/images/emoji/apple/heavy_multiplication_x.png new file mode 100644 index 0000000000..eaf9a6392f Binary files /dev/null and b/public/images/emoji/apple/heavy_multiplication_x.png differ diff --git a/public/images/emoji/apple/heavy_plus_sign.png b/public/images/emoji/apple/heavy_plus_sign.png new file mode 100644 index 0000000000..07d7b8a905 Binary files /dev/null and b/public/images/emoji/apple/heavy_plus_sign.png differ diff --git a/public/images/emoji/apple/helicopter.png b/public/images/emoji/apple/helicopter.png new file mode 100644 index 0000000000..1ee25fa3ab Binary files /dev/null and b/public/images/emoji/apple/helicopter.png differ diff --git a/public/images/emoji/apple/herb.png b/public/images/emoji/apple/herb.png new file mode 100644 index 0000000000..75df4cee05 Binary files /dev/null and b/public/images/emoji/apple/herb.png differ diff --git a/public/images/emoji/apple/hibiscus.png b/public/images/emoji/apple/hibiscus.png new file mode 100644 index 0000000000..567f8cc027 Binary files /dev/null and b/public/images/emoji/apple/hibiscus.png differ diff --git a/public/images/emoji/apple/high_brightness.png b/public/images/emoji/apple/high_brightness.png new file mode 100644 index 0000000000..49bfcfdfe5 Binary files /dev/null and b/public/images/emoji/apple/high_brightness.png differ diff --git a/public/images/emoji/apple/high_heel.png b/public/images/emoji/apple/high_heel.png new file mode 100644 index 0000000000..4562be9dab Binary files /dev/null and b/public/images/emoji/apple/high_heel.png differ diff --git a/public/images/emoji/apple/hocho.png b/public/images/emoji/apple/hocho.png new file mode 100644 index 0000000000..630397cd39 Binary files /dev/null and b/public/images/emoji/apple/hocho.png differ diff --git a/public/images/emoji/apple/honey_pot.png b/public/images/emoji/apple/honey_pot.png new file mode 100644 index 0000000000..d7fbb7408f Binary files /dev/null and b/public/images/emoji/apple/honey_pot.png differ diff --git a/public/images/emoji/apple/honeybee.png b/public/images/emoji/apple/honeybee.png new file mode 100644 index 0000000000..8ae23dfbcf Binary files /dev/null and b/public/images/emoji/apple/honeybee.png differ diff --git a/public/images/emoji/apple/horse.png b/public/images/emoji/apple/horse.png new file mode 100644 index 0000000000..f8b53487d3 Binary files /dev/null and b/public/images/emoji/apple/horse.png differ diff --git a/public/images/emoji/apple/horse_racing.png b/public/images/emoji/apple/horse_racing.png new file mode 100644 index 0000000000..e5cd7f5dfc Binary files /dev/null and b/public/images/emoji/apple/horse_racing.png differ diff --git a/public/images/emoji/apple/hospital.png b/public/images/emoji/apple/hospital.png new file mode 100644 index 0000000000..ba7a2a4216 Binary files /dev/null and b/public/images/emoji/apple/hospital.png differ diff --git a/public/images/emoji/apple/hotel.png b/public/images/emoji/apple/hotel.png new file mode 100644 index 0000000000..a37c5940c7 Binary files /dev/null and b/public/images/emoji/apple/hotel.png differ diff --git a/public/images/emoji/apple/hotsprings.png b/public/images/emoji/apple/hotsprings.png new file mode 100644 index 0000000000..7370ceb602 Binary files /dev/null and b/public/images/emoji/apple/hotsprings.png differ diff --git a/public/images/emoji/apple/hourglass.png b/public/images/emoji/apple/hourglass.png new file mode 100644 index 0000000000..37295ff816 Binary files /dev/null and b/public/images/emoji/apple/hourglass.png differ diff --git a/public/images/emoji/apple/hourglass_flowing_sand.png b/public/images/emoji/apple/hourglass_flowing_sand.png new file mode 100644 index 0000000000..12ce4252be Binary files /dev/null and b/public/images/emoji/apple/hourglass_flowing_sand.png differ diff --git a/public/images/emoji/apple/house.png b/public/images/emoji/apple/house.png new file mode 100644 index 0000000000..4e17b37a8b Binary files /dev/null and b/public/images/emoji/apple/house.png differ diff --git a/public/images/emoji/apple/house_with_garden.png b/public/images/emoji/apple/house_with_garden.png new file mode 100644 index 0000000000..0b0ada8f41 Binary files /dev/null and b/public/images/emoji/apple/house_with_garden.png differ diff --git a/public/images/emoji/apple/hushed.png b/public/images/emoji/apple/hushed.png new file mode 100644 index 0000000000..d91a51f634 Binary files /dev/null and b/public/images/emoji/apple/hushed.png differ diff --git a/public/images/emoji/apple/ice_cream.png b/public/images/emoji/apple/ice_cream.png new file mode 100644 index 0000000000..c729060593 Binary files /dev/null and b/public/images/emoji/apple/ice_cream.png differ diff --git a/public/images/emoji/apple/icecream.png b/public/images/emoji/apple/icecream.png new file mode 100644 index 0000000000..4af3cbbb10 Binary files /dev/null and b/public/images/emoji/apple/icecream.png differ diff --git a/public/images/emoji/apple/id.png b/public/images/emoji/apple/id.png new file mode 100644 index 0000000000..83f4c2bbe1 Binary files /dev/null and b/public/images/emoji/apple/id.png differ diff --git a/public/images/emoji/apple/ideograph_advantage.png b/public/images/emoji/apple/ideograph_advantage.png new file mode 100644 index 0000000000..9287867239 Binary files /dev/null and b/public/images/emoji/apple/ideograph_advantage.png differ diff --git a/public/images/emoji/apple/imp.png b/public/images/emoji/apple/imp.png new file mode 100644 index 0000000000..eb2e9ccad4 Binary files /dev/null and b/public/images/emoji/apple/imp.png differ diff --git a/public/images/emoji/apple/inbox_tray.png b/public/images/emoji/apple/inbox_tray.png new file mode 100644 index 0000000000..2a23106620 Binary files /dev/null and b/public/images/emoji/apple/inbox_tray.png differ diff --git a/public/images/emoji/apple/incoming_envelope.png b/public/images/emoji/apple/incoming_envelope.png new file mode 100644 index 0000000000..fb0a4c8041 Binary files /dev/null and b/public/images/emoji/apple/incoming_envelope.png differ diff --git a/public/images/emoji/apple/information_desk_person.png b/public/images/emoji/apple/information_desk_person.png new file mode 100644 index 0000000000..8804a07a3d Binary files /dev/null and b/public/images/emoji/apple/information_desk_person.png differ diff --git a/public/images/emoji/apple/information_source.png b/public/images/emoji/apple/information_source.png new file mode 100644 index 0000000000..47ba0cf16b Binary files /dev/null and b/public/images/emoji/apple/information_source.png differ diff --git a/public/images/emoji/apple/innocent.png b/public/images/emoji/apple/innocent.png new file mode 100644 index 0000000000..04911a92ad Binary files /dev/null and b/public/images/emoji/apple/innocent.png differ diff --git a/public/images/emoji/apple/interrobang.png b/public/images/emoji/apple/interrobang.png new file mode 100644 index 0000000000..23dc8549df Binary files /dev/null and b/public/images/emoji/apple/interrobang.png differ diff --git a/public/images/emoji/apple/iphone.png b/public/images/emoji/apple/iphone.png new file mode 100644 index 0000000000..615468a4b7 Binary files /dev/null and b/public/images/emoji/apple/iphone.png differ diff --git a/public/images/emoji/apple/it.png b/public/images/emoji/apple/it.png new file mode 100644 index 0000000000..d1dee29e89 Binary files /dev/null and b/public/images/emoji/apple/it.png differ diff --git a/public/images/emoji/apple/izakaya_lantern.png b/public/images/emoji/apple/izakaya_lantern.png new file mode 100644 index 0000000000..6fbe745103 Binary files /dev/null and b/public/images/emoji/apple/izakaya_lantern.png differ diff --git a/public/images/emoji/apple/jack_o_lantern.png b/public/images/emoji/apple/jack_o_lantern.png new file mode 100644 index 0000000000..1640d67f8c Binary files /dev/null and b/public/images/emoji/apple/jack_o_lantern.png differ diff --git a/public/images/emoji/apple/japan.png b/public/images/emoji/apple/japan.png new file mode 100644 index 0000000000..93e4c08221 Binary files /dev/null and b/public/images/emoji/apple/japan.png differ diff --git a/public/images/emoji/apple/japanese_castle.png b/public/images/emoji/apple/japanese_castle.png new file mode 100644 index 0000000000..547d8075e5 Binary files /dev/null and b/public/images/emoji/apple/japanese_castle.png differ diff --git a/public/images/emoji/apple/japanese_goblin.png b/public/images/emoji/apple/japanese_goblin.png new file mode 100644 index 0000000000..d00f9f3e83 Binary files /dev/null and b/public/images/emoji/apple/japanese_goblin.png differ diff --git a/public/images/emoji/apple/japanese_ogre.png b/public/images/emoji/apple/japanese_ogre.png new file mode 100644 index 0000000000..5ad27b9774 Binary files /dev/null and b/public/images/emoji/apple/japanese_ogre.png differ diff --git a/public/images/emoji/apple/jeans.png b/public/images/emoji/apple/jeans.png new file mode 100644 index 0000000000..46392d282b Binary files /dev/null and b/public/images/emoji/apple/jeans.png differ diff --git a/public/images/emoji/apple/joy.png b/public/images/emoji/apple/joy.png new file mode 100644 index 0000000000..9264720e70 Binary files /dev/null and b/public/images/emoji/apple/joy.png differ diff --git a/public/images/emoji/apple/joy_cat.png b/public/images/emoji/apple/joy_cat.png new file mode 100644 index 0000000000..b5cf7b6ca9 Binary files /dev/null and b/public/images/emoji/apple/joy_cat.png differ diff --git a/public/images/emoji/apple/jp.png b/public/images/emoji/apple/jp.png new file mode 100644 index 0000000000..8487914a71 Binary files /dev/null and b/public/images/emoji/apple/jp.png differ diff --git a/public/images/emoji/apple/key.png b/public/images/emoji/apple/key.png new file mode 100644 index 0000000000..65c4521520 Binary files /dev/null and b/public/images/emoji/apple/key.png differ diff --git a/public/images/emoji/apple/keycap_ten.png b/public/images/emoji/apple/keycap_ten.png new file mode 100644 index 0000000000..bbdec44dca Binary files /dev/null and b/public/images/emoji/apple/keycap_ten.png differ diff --git a/public/images/emoji/apple/kimono.png b/public/images/emoji/apple/kimono.png new file mode 100644 index 0000000000..6fc096bbd3 Binary files /dev/null and b/public/images/emoji/apple/kimono.png differ diff --git a/public/images/emoji/apple/kiss.png b/public/images/emoji/apple/kiss.png new file mode 100644 index 0000000000..93e24f30f4 Binary files /dev/null and b/public/images/emoji/apple/kiss.png differ diff --git a/public/images/emoji/apple/kissing.png b/public/images/emoji/apple/kissing.png new file mode 100644 index 0000000000..68ff8480e8 Binary files /dev/null and b/public/images/emoji/apple/kissing.png differ diff --git a/public/images/emoji/apple/kissing_cat.png b/public/images/emoji/apple/kissing_cat.png new file mode 100644 index 0000000000..1b1d0ca783 Binary files /dev/null and b/public/images/emoji/apple/kissing_cat.png differ diff --git a/public/images/emoji/apple/kissing_closed_eyes.png b/public/images/emoji/apple/kissing_closed_eyes.png new file mode 100644 index 0000000000..be4934b0c3 Binary files /dev/null and b/public/images/emoji/apple/kissing_closed_eyes.png differ diff --git a/public/images/emoji/apple/kissing_heart.png b/public/images/emoji/apple/kissing_heart.png new file mode 100644 index 0000000000..1df787f0e9 Binary files /dev/null and b/public/images/emoji/apple/kissing_heart.png differ diff --git a/public/images/emoji/apple/kissing_smiling_eyes.png b/public/images/emoji/apple/kissing_smiling_eyes.png new file mode 100644 index 0000000000..3410556c25 Binary files /dev/null and b/public/images/emoji/apple/kissing_smiling_eyes.png differ diff --git a/public/images/emoji/apple/knife.png b/public/images/emoji/apple/knife.png new file mode 100644 index 0000000000..630397cd39 Binary files /dev/null and b/public/images/emoji/apple/knife.png differ diff --git a/public/images/emoji/apple/koala.png b/public/images/emoji/apple/koala.png new file mode 100644 index 0000000000..63a4f04256 Binary files /dev/null and b/public/images/emoji/apple/koala.png differ diff --git a/public/images/emoji/apple/koko.png b/public/images/emoji/apple/koko.png new file mode 100644 index 0000000000..5f8c3cb90a Binary files /dev/null and b/public/images/emoji/apple/koko.png differ diff --git a/public/images/emoji/apple/kr.png b/public/images/emoji/apple/kr.png new file mode 100644 index 0000000000..5640546ef4 Binary files /dev/null and b/public/images/emoji/apple/kr.png differ diff --git a/public/images/emoji/apple/lantern.png b/public/images/emoji/apple/lantern.png new file mode 100644 index 0000000000..6fbe745103 Binary files /dev/null and b/public/images/emoji/apple/lantern.png differ diff --git a/public/images/emoji/apple/large_blue_circle.png b/public/images/emoji/apple/large_blue_circle.png new file mode 100644 index 0000000000..5df409a5b0 Binary files /dev/null and b/public/images/emoji/apple/large_blue_circle.png differ diff --git a/public/images/emoji/apple/large_blue_diamond.png b/public/images/emoji/apple/large_blue_diamond.png new file mode 100644 index 0000000000..70b7eb05d7 Binary files /dev/null and b/public/images/emoji/apple/large_blue_diamond.png differ diff --git a/public/images/emoji/apple/large_orange_diamond.png b/public/images/emoji/apple/large_orange_diamond.png new file mode 100644 index 0000000000..585da56008 Binary files /dev/null and b/public/images/emoji/apple/large_orange_diamond.png differ diff --git a/public/images/emoji/apple/last_quarter_moon.png b/public/images/emoji/apple/last_quarter_moon.png new file mode 100644 index 0000000000..235b715a8b Binary files /dev/null and b/public/images/emoji/apple/last_quarter_moon.png differ diff --git a/public/images/emoji/apple/last_quarter_moon_with_face.png b/public/images/emoji/apple/last_quarter_moon_with_face.png new file mode 100644 index 0000000000..2c9f57694c Binary files /dev/null and b/public/images/emoji/apple/last_quarter_moon_with_face.png differ diff --git a/public/images/emoji/apple/laughing.png b/public/images/emoji/apple/laughing.png new file mode 100644 index 0000000000..85605c7329 Binary files /dev/null and b/public/images/emoji/apple/laughing.png differ diff --git a/public/images/emoji/apple/leaves.png b/public/images/emoji/apple/leaves.png new file mode 100644 index 0000000000..85bc9d874a Binary files /dev/null and b/public/images/emoji/apple/leaves.png differ diff --git a/public/images/emoji/apple/ledger.png b/public/images/emoji/apple/ledger.png new file mode 100644 index 0000000000..85443655f7 Binary files /dev/null and b/public/images/emoji/apple/ledger.png differ diff --git a/public/images/emoji/apple/left_luggage.png b/public/images/emoji/apple/left_luggage.png new file mode 100644 index 0000000000..fa742b16a9 Binary files /dev/null and b/public/images/emoji/apple/left_luggage.png differ diff --git a/public/images/emoji/apple/left_right_arrow.png b/public/images/emoji/apple/left_right_arrow.png new file mode 100644 index 0000000000..bc198fc7ba Binary files /dev/null and b/public/images/emoji/apple/left_right_arrow.png differ diff --git a/public/images/emoji/apple/leftwards_arrow_with_hook.png b/public/images/emoji/apple/leftwards_arrow_with_hook.png new file mode 100644 index 0000000000..fb93082312 Binary files /dev/null and b/public/images/emoji/apple/leftwards_arrow_with_hook.png differ diff --git a/public/images/emoji/apple/lemon.png b/public/images/emoji/apple/lemon.png new file mode 100644 index 0000000000..e2754e6478 Binary files /dev/null and b/public/images/emoji/apple/lemon.png differ diff --git a/public/images/emoji/apple/leo.png b/public/images/emoji/apple/leo.png new file mode 100644 index 0000000000..7f61595816 Binary files /dev/null and b/public/images/emoji/apple/leo.png differ diff --git a/public/images/emoji/apple/leopard.png b/public/images/emoji/apple/leopard.png new file mode 100644 index 0000000000..8451b91a3a Binary files /dev/null and b/public/images/emoji/apple/leopard.png differ diff --git a/public/images/emoji/apple/libra.png b/public/images/emoji/apple/libra.png new file mode 100644 index 0000000000..95b0aa7fe9 Binary files /dev/null and b/public/images/emoji/apple/libra.png differ diff --git a/public/images/emoji/apple/light_rail.png b/public/images/emoji/apple/light_rail.png new file mode 100644 index 0000000000..2a4830d3d2 Binary files /dev/null and b/public/images/emoji/apple/light_rail.png differ diff --git a/public/images/emoji/apple/link.png b/public/images/emoji/apple/link.png new file mode 100644 index 0000000000..6519f66201 Binary files /dev/null and b/public/images/emoji/apple/link.png differ diff --git a/public/images/emoji/apple/lips.png b/public/images/emoji/apple/lips.png new file mode 100644 index 0000000000..a97856b47d Binary files /dev/null and b/public/images/emoji/apple/lips.png differ diff --git a/public/images/emoji/apple/lipstick.png b/public/images/emoji/apple/lipstick.png new file mode 100644 index 0000000000..e26b2a801d Binary files /dev/null and b/public/images/emoji/apple/lipstick.png differ diff --git a/public/images/emoji/apple/lock.png b/public/images/emoji/apple/lock.png new file mode 100644 index 0000000000..14c8773115 Binary files /dev/null and b/public/images/emoji/apple/lock.png differ diff --git a/public/images/emoji/apple/lock_with_ink_pen.png b/public/images/emoji/apple/lock_with_ink_pen.png new file mode 100644 index 0000000000..4490b88983 Binary files /dev/null and b/public/images/emoji/apple/lock_with_ink_pen.png differ diff --git a/public/images/emoji/apple/lollipop.png b/public/images/emoji/apple/lollipop.png new file mode 100644 index 0000000000..65c89afc42 Binary files /dev/null and b/public/images/emoji/apple/lollipop.png differ diff --git a/public/images/emoji/apple/loop.png b/public/images/emoji/apple/loop.png new file mode 100644 index 0000000000..c34cc067af Binary files /dev/null and b/public/images/emoji/apple/loop.png differ diff --git a/public/images/emoji/apple/loud_sound.png b/public/images/emoji/apple/loud_sound.png new file mode 100644 index 0000000000..291444e402 Binary files /dev/null and b/public/images/emoji/apple/loud_sound.png differ diff --git a/public/images/emoji/apple/loudspeaker.png b/public/images/emoji/apple/loudspeaker.png new file mode 100644 index 0000000000..4e5a409fcd Binary files /dev/null and b/public/images/emoji/apple/loudspeaker.png differ diff --git a/public/images/emoji/apple/love_hotel.png b/public/images/emoji/apple/love_hotel.png new file mode 100644 index 0000000000..eea9d5a6e1 Binary files /dev/null and b/public/images/emoji/apple/love_hotel.png differ diff --git a/public/images/emoji/apple/love_letter.png b/public/images/emoji/apple/love_letter.png new file mode 100644 index 0000000000..b2a395ac92 Binary files /dev/null and b/public/images/emoji/apple/love_letter.png differ diff --git a/public/images/emoji/apple/low_brightness.png b/public/images/emoji/apple/low_brightness.png new file mode 100644 index 0000000000..7fbe82ae64 Binary files /dev/null and b/public/images/emoji/apple/low_brightness.png differ diff --git a/public/images/emoji/apple/m.png b/public/images/emoji/apple/m.png new file mode 100644 index 0000000000..2ac1e8c97b Binary files /dev/null and b/public/images/emoji/apple/m.png differ diff --git a/public/images/emoji/apple/mag.png b/public/images/emoji/apple/mag.png new file mode 100644 index 0000000000..e523296872 Binary files /dev/null and b/public/images/emoji/apple/mag.png differ diff --git a/public/images/emoji/apple/mag_right.png b/public/images/emoji/apple/mag_right.png new file mode 100644 index 0000000000..1957b4f2f9 Binary files /dev/null and b/public/images/emoji/apple/mag_right.png differ diff --git a/public/images/emoji/apple/mahjong.png b/public/images/emoji/apple/mahjong.png new file mode 100644 index 0000000000..3e9a30d400 Binary files /dev/null and b/public/images/emoji/apple/mahjong.png differ diff --git a/public/images/emoji/apple/mailbox.png b/public/images/emoji/apple/mailbox.png new file mode 100644 index 0000000000..63d1cdc871 Binary files /dev/null and b/public/images/emoji/apple/mailbox.png differ diff --git a/public/images/emoji/apple/mailbox_closed.png b/public/images/emoji/apple/mailbox_closed.png new file mode 100644 index 0000000000..fd314b8d3b Binary files /dev/null and b/public/images/emoji/apple/mailbox_closed.png differ diff --git a/public/images/emoji/apple/mailbox_with_mail.png b/public/images/emoji/apple/mailbox_with_mail.png new file mode 100644 index 0000000000..2a4a15c4ed Binary files /dev/null and b/public/images/emoji/apple/mailbox_with_mail.png differ diff --git a/public/images/emoji/apple/mailbox_with_no_mail.png b/public/images/emoji/apple/mailbox_with_no_mail.png new file mode 100644 index 0000000000..c4bda7acfc Binary files /dev/null and b/public/images/emoji/apple/mailbox_with_no_mail.png differ diff --git a/public/images/emoji/apple/man.png b/public/images/emoji/apple/man.png new file mode 100644 index 0000000000..f9f182c891 Binary files /dev/null and b/public/images/emoji/apple/man.png differ diff --git a/public/images/emoji/apple/man_with_gua_pi_mao.png b/public/images/emoji/apple/man_with_gua_pi_mao.png new file mode 100644 index 0000000000..5e256981b1 Binary files /dev/null and b/public/images/emoji/apple/man_with_gua_pi_mao.png differ diff --git a/public/images/emoji/apple/man_with_turban.png b/public/images/emoji/apple/man_with_turban.png new file mode 100644 index 0000000000..64f06b0f2f Binary files /dev/null and b/public/images/emoji/apple/man_with_turban.png differ diff --git a/public/images/emoji/apple/mans_shoe.png b/public/images/emoji/apple/mans_shoe.png new file mode 100644 index 0000000000..e43ce6ee57 Binary files /dev/null and b/public/images/emoji/apple/mans_shoe.png differ diff --git a/public/images/emoji/apple/maple_leaf.png b/public/images/emoji/apple/maple_leaf.png new file mode 100644 index 0000000000..7ac4aaed6f Binary files /dev/null and b/public/images/emoji/apple/maple_leaf.png differ diff --git a/public/images/emoji/apple/mask.png b/public/images/emoji/apple/mask.png new file mode 100644 index 0000000000..21edb3c95a Binary files /dev/null and b/public/images/emoji/apple/mask.png differ diff --git a/public/images/emoji/apple/massage.png b/public/images/emoji/apple/massage.png new file mode 100644 index 0000000000..f6974e76b0 Binary files /dev/null and b/public/images/emoji/apple/massage.png differ diff --git a/public/images/emoji/apple/meat_on_bone.png b/public/images/emoji/apple/meat_on_bone.png new file mode 100644 index 0000000000..80050d4d11 Binary files /dev/null and b/public/images/emoji/apple/meat_on_bone.png differ diff --git a/public/images/emoji/apple/mega.png b/public/images/emoji/apple/mega.png new file mode 100644 index 0000000000..250c352748 Binary files /dev/null and b/public/images/emoji/apple/mega.png differ diff --git a/public/images/emoji/apple/melon.png b/public/images/emoji/apple/melon.png new file mode 100644 index 0000000000..1440d82857 Binary files /dev/null and b/public/images/emoji/apple/melon.png differ diff --git a/public/images/emoji/apple/memo.png b/public/images/emoji/apple/memo.png new file mode 100644 index 0000000000..57abb762d5 Binary files /dev/null and b/public/images/emoji/apple/memo.png differ diff --git a/public/images/emoji/apple/mens.png b/public/images/emoji/apple/mens.png new file mode 100644 index 0000000000..44c9351d03 Binary files /dev/null and b/public/images/emoji/apple/mens.png differ diff --git a/public/images/emoji/apple/metro.png b/public/images/emoji/apple/metro.png new file mode 100644 index 0000000000..4f73871969 Binary files /dev/null and b/public/images/emoji/apple/metro.png differ diff --git a/public/images/emoji/apple/microphone.png b/public/images/emoji/apple/microphone.png new file mode 100644 index 0000000000..31ade85df6 Binary files /dev/null and b/public/images/emoji/apple/microphone.png differ diff --git a/public/images/emoji/apple/microscope.png b/public/images/emoji/apple/microscope.png new file mode 100644 index 0000000000..ea527e7838 Binary files /dev/null and b/public/images/emoji/apple/microscope.png differ diff --git a/public/images/emoji/apple/milky_way.png b/public/images/emoji/apple/milky_way.png new file mode 100644 index 0000000000..fe0eaa6455 Binary files /dev/null and b/public/images/emoji/apple/milky_way.png differ diff --git a/public/images/emoji/apple/minibus.png b/public/images/emoji/apple/minibus.png new file mode 100644 index 0000000000..e6a17b0f89 Binary files /dev/null and b/public/images/emoji/apple/minibus.png differ diff --git a/public/images/emoji/apple/minidisc.png b/public/images/emoji/apple/minidisc.png new file mode 100644 index 0000000000..56af53b27f Binary files /dev/null and b/public/images/emoji/apple/minidisc.png differ diff --git a/public/images/emoji/apple/mobile_phone_off.png b/public/images/emoji/apple/mobile_phone_off.png new file mode 100644 index 0000000000..23a78d00d8 Binary files /dev/null and b/public/images/emoji/apple/mobile_phone_off.png differ diff --git a/public/images/emoji/apple/money_with_wings.png b/public/images/emoji/apple/money_with_wings.png new file mode 100644 index 0000000000..2940d1798a Binary files /dev/null and b/public/images/emoji/apple/money_with_wings.png differ diff --git a/public/images/emoji/apple/moneybag.png b/public/images/emoji/apple/moneybag.png new file mode 100644 index 0000000000..382fa2adc7 Binary files /dev/null and b/public/images/emoji/apple/moneybag.png differ diff --git a/public/images/emoji/apple/monkey.png b/public/images/emoji/apple/monkey.png new file mode 100644 index 0000000000..f90ef1317c Binary files /dev/null and b/public/images/emoji/apple/monkey.png differ diff --git a/public/images/emoji/apple/monkey_face.png b/public/images/emoji/apple/monkey_face.png new file mode 100644 index 0000000000..341e376bbf Binary files /dev/null and b/public/images/emoji/apple/monkey_face.png differ diff --git a/public/images/emoji/apple/monorail.png b/public/images/emoji/apple/monorail.png new file mode 100644 index 0000000000..b5d44f8e9d Binary files /dev/null and b/public/images/emoji/apple/monorail.png differ diff --git a/public/images/emoji/apple/moon.png b/public/images/emoji/apple/moon.png new file mode 100644 index 0000000000..03e9b3daba Binary files /dev/null and b/public/images/emoji/apple/moon.png differ diff --git a/public/images/emoji/apple/mortar_board.png b/public/images/emoji/apple/mortar_board.png new file mode 100644 index 0000000000..d17e8cf074 Binary files /dev/null and b/public/images/emoji/apple/mortar_board.png differ diff --git a/public/images/emoji/apple/mount_fuji.png b/public/images/emoji/apple/mount_fuji.png new file mode 100644 index 0000000000..7bc05512f8 Binary files /dev/null and b/public/images/emoji/apple/mount_fuji.png differ diff --git a/public/images/emoji/apple/mountain_bicyclist.png b/public/images/emoji/apple/mountain_bicyclist.png new file mode 100644 index 0000000000..761dde0d9e Binary files /dev/null and b/public/images/emoji/apple/mountain_bicyclist.png differ diff --git a/public/images/emoji/apple/mountain_cableway.png b/public/images/emoji/apple/mountain_cableway.png new file mode 100644 index 0000000000..29ff1b5845 Binary files /dev/null and b/public/images/emoji/apple/mountain_cableway.png differ diff --git a/public/images/emoji/apple/mountain_railway.png b/public/images/emoji/apple/mountain_railway.png new file mode 100644 index 0000000000..41ad0c94fc Binary files /dev/null and b/public/images/emoji/apple/mountain_railway.png differ diff --git a/public/images/emoji/apple/mouse.png b/public/images/emoji/apple/mouse.png new file mode 100644 index 0000000000..7cd289d40d Binary files /dev/null and b/public/images/emoji/apple/mouse.png differ diff --git a/public/images/emoji/apple/mouse2.png b/public/images/emoji/apple/mouse2.png new file mode 100644 index 0000000000..a6c6816b39 Binary files /dev/null and b/public/images/emoji/apple/mouse2.png differ diff --git a/public/images/emoji/apple/movie_camera.png b/public/images/emoji/apple/movie_camera.png new file mode 100644 index 0000000000..86af960c98 Binary files /dev/null and b/public/images/emoji/apple/movie_camera.png differ diff --git a/public/images/emoji/apple/moyai.png b/public/images/emoji/apple/moyai.png new file mode 100644 index 0000000000..4eb8362da1 Binary files /dev/null and b/public/images/emoji/apple/moyai.png differ diff --git a/public/images/emoji/apple/muscle.png b/public/images/emoji/apple/muscle.png new file mode 100644 index 0000000000..5778d0df7d Binary files /dev/null and b/public/images/emoji/apple/muscle.png differ diff --git a/public/images/emoji/apple/mushroom.png b/public/images/emoji/apple/mushroom.png new file mode 100644 index 0000000000..5ed54b62da Binary files /dev/null and b/public/images/emoji/apple/mushroom.png differ diff --git a/public/images/emoji/apple/musical_keyboard.png b/public/images/emoji/apple/musical_keyboard.png new file mode 100644 index 0000000000..af01fde7bb Binary files /dev/null and b/public/images/emoji/apple/musical_keyboard.png differ diff --git a/public/images/emoji/apple/musical_note.png b/public/images/emoji/apple/musical_note.png new file mode 100644 index 0000000000..83fc05faf9 Binary files /dev/null and b/public/images/emoji/apple/musical_note.png differ diff --git a/public/images/emoji/apple/musical_score.png b/public/images/emoji/apple/musical_score.png new file mode 100644 index 0000000000..5cf7032eba Binary files /dev/null and b/public/images/emoji/apple/musical_score.png differ diff --git a/public/images/emoji/apple/mute.png b/public/images/emoji/apple/mute.png new file mode 100644 index 0000000000..25a413be69 Binary files /dev/null and b/public/images/emoji/apple/mute.png differ diff --git a/public/images/emoji/apple/nail_care.png b/public/images/emoji/apple/nail_care.png new file mode 100644 index 0000000000..9d25f9f739 Binary files /dev/null and b/public/images/emoji/apple/nail_care.png differ diff --git a/public/images/emoji/apple/name_badge.png b/public/images/emoji/apple/name_badge.png new file mode 100644 index 0000000000..09f397a30d Binary files /dev/null and b/public/images/emoji/apple/name_badge.png differ diff --git a/public/images/emoji/apple/necktie.png b/public/images/emoji/apple/necktie.png new file mode 100644 index 0000000000..0097e34e2d Binary files /dev/null and b/public/images/emoji/apple/necktie.png differ diff --git a/public/images/emoji/apple/negative_squared_cross_mark.png b/public/images/emoji/apple/negative_squared_cross_mark.png new file mode 100644 index 0000000000..75f1c61b65 Binary files /dev/null and b/public/images/emoji/apple/negative_squared_cross_mark.png differ diff --git a/public/images/emoji/apple/neutral_face.png b/public/images/emoji/apple/neutral_face.png new file mode 100644 index 0000000000..54d7226501 Binary files /dev/null and b/public/images/emoji/apple/neutral_face.png differ diff --git a/public/images/emoji/apple/new.png b/public/images/emoji/apple/new.png new file mode 100644 index 0000000000..5831405db5 Binary files /dev/null and b/public/images/emoji/apple/new.png differ diff --git a/public/images/emoji/apple/new_moon.png b/public/images/emoji/apple/new_moon.png new file mode 100644 index 0000000000..3009b19fa5 Binary files /dev/null and b/public/images/emoji/apple/new_moon.png differ diff --git a/public/images/emoji/apple/new_moon_with_face.png b/public/images/emoji/apple/new_moon_with_face.png new file mode 100644 index 0000000000..e3ed1487b9 Binary files /dev/null and b/public/images/emoji/apple/new_moon_with_face.png differ diff --git a/public/images/emoji/apple/newspaper.png b/public/images/emoji/apple/newspaper.png new file mode 100644 index 0000000000..dae3495d3e Binary files /dev/null and b/public/images/emoji/apple/newspaper.png differ diff --git a/public/images/emoji/apple/ng.png b/public/images/emoji/apple/ng.png new file mode 100644 index 0000000000..88db02bb1b Binary files /dev/null and b/public/images/emoji/apple/ng.png differ diff --git a/public/images/emoji/apple/night_with_stars.png b/public/images/emoji/apple/night_with_stars.png new file mode 100644 index 0000000000..be505a6927 Binary files /dev/null and b/public/images/emoji/apple/night_with_stars.png differ diff --git a/public/images/emoji/apple/nine.png b/public/images/emoji/apple/nine.png new file mode 100644 index 0000000000..99f04160cb Binary files /dev/null and b/public/images/emoji/apple/nine.png differ diff --git a/public/images/emoji/apple/no_bell.png b/public/images/emoji/apple/no_bell.png new file mode 100644 index 0000000000..09ba559fb4 Binary files /dev/null and b/public/images/emoji/apple/no_bell.png differ diff --git a/public/images/emoji/apple/no_bicycles.png b/public/images/emoji/apple/no_bicycles.png new file mode 100644 index 0000000000..24a2adad0c Binary files /dev/null and b/public/images/emoji/apple/no_bicycles.png differ diff --git a/public/images/emoji/apple/no_entry.png b/public/images/emoji/apple/no_entry.png new file mode 100644 index 0000000000..c36c84e288 Binary files /dev/null and b/public/images/emoji/apple/no_entry.png differ diff --git a/public/images/emoji/apple/no_entry_sign.png b/public/images/emoji/apple/no_entry_sign.png new file mode 100644 index 0000000000..e84384f49d Binary files /dev/null and b/public/images/emoji/apple/no_entry_sign.png differ diff --git a/public/images/emoji/apple/no_good.png b/public/images/emoji/apple/no_good.png new file mode 100644 index 0000000000..883fa8a847 Binary files /dev/null and b/public/images/emoji/apple/no_good.png differ diff --git a/public/images/emoji/apple/no_mobile_phones.png b/public/images/emoji/apple/no_mobile_phones.png new file mode 100644 index 0000000000..8f6e56574d Binary files /dev/null and b/public/images/emoji/apple/no_mobile_phones.png differ diff --git a/public/images/emoji/apple/no_mouth.png b/public/images/emoji/apple/no_mouth.png new file mode 100644 index 0000000000..aa22aac320 Binary files /dev/null and b/public/images/emoji/apple/no_mouth.png differ diff --git a/public/images/emoji/apple/no_pedestrians.png b/public/images/emoji/apple/no_pedestrians.png new file mode 100644 index 0000000000..851f55df9e Binary files /dev/null and b/public/images/emoji/apple/no_pedestrians.png differ diff --git a/public/images/emoji/apple/no_smoking.png b/public/images/emoji/apple/no_smoking.png new file mode 100644 index 0000000000..b6bd6309de Binary files /dev/null and b/public/images/emoji/apple/no_smoking.png differ diff --git a/public/images/emoji/apple/non-potable_water.png b/public/images/emoji/apple/non-potable_water.png new file mode 100644 index 0000000000..25521b4afb Binary files /dev/null and b/public/images/emoji/apple/non-potable_water.png differ diff --git a/public/images/emoji/apple/nose.png b/public/images/emoji/apple/nose.png new file mode 100644 index 0000000000..403f6aacae Binary files /dev/null and b/public/images/emoji/apple/nose.png differ diff --git a/public/images/emoji/apple/notebook.png b/public/images/emoji/apple/notebook.png new file mode 100644 index 0000000000..357871772f Binary files /dev/null and b/public/images/emoji/apple/notebook.png differ diff --git a/public/images/emoji/apple/notebook_with_decorative_cover.png b/public/images/emoji/apple/notebook_with_decorative_cover.png new file mode 100644 index 0000000000..b9898b224e Binary files /dev/null and b/public/images/emoji/apple/notebook_with_decorative_cover.png differ diff --git a/public/images/emoji/apple/notes.png b/public/images/emoji/apple/notes.png new file mode 100644 index 0000000000..e55a56600c Binary files /dev/null and b/public/images/emoji/apple/notes.png differ diff --git a/public/images/emoji/apple/nut_and_bolt.png b/public/images/emoji/apple/nut_and_bolt.png new file mode 100644 index 0000000000..00dcf8b216 Binary files /dev/null and b/public/images/emoji/apple/nut_and_bolt.png differ diff --git a/public/images/emoji/apple/o.png b/public/images/emoji/apple/o.png new file mode 100644 index 0000000000..fcf887e960 Binary files /dev/null and b/public/images/emoji/apple/o.png differ diff --git a/public/images/emoji/apple/o2.png b/public/images/emoji/apple/o2.png new file mode 100644 index 0000000000..c8ca117ad2 Binary files /dev/null and b/public/images/emoji/apple/o2.png differ diff --git a/public/images/emoji/apple/ocean.png b/public/images/emoji/apple/ocean.png new file mode 100644 index 0000000000..35798e4f39 Binary files /dev/null and b/public/images/emoji/apple/ocean.png differ diff --git a/public/images/emoji/apple/octopus.png b/public/images/emoji/apple/octopus.png new file mode 100644 index 0000000000..16727ff286 Binary files /dev/null and b/public/images/emoji/apple/octopus.png differ diff --git a/public/images/emoji/apple/oden.png b/public/images/emoji/apple/oden.png new file mode 100644 index 0000000000..1268494dfc Binary files /dev/null and b/public/images/emoji/apple/oden.png differ diff --git a/public/images/emoji/apple/office.png b/public/images/emoji/apple/office.png new file mode 100644 index 0000000000..ea025210a1 Binary files /dev/null and b/public/images/emoji/apple/office.png differ diff --git a/public/images/emoji/apple/ok.png b/public/images/emoji/apple/ok.png new file mode 100644 index 0000000000..0377d26da3 Binary files /dev/null and b/public/images/emoji/apple/ok.png differ diff --git a/public/images/emoji/apple/ok_hand.png b/public/images/emoji/apple/ok_hand.png new file mode 100644 index 0000000000..53533a42d6 Binary files /dev/null and b/public/images/emoji/apple/ok_hand.png differ diff --git a/public/images/emoji/apple/ok_woman.png b/public/images/emoji/apple/ok_woman.png new file mode 100644 index 0000000000..ba4a27660c Binary files /dev/null and b/public/images/emoji/apple/ok_woman.png differ diff --git a/public/images/emoji/apple/older_man.png b/public/images/emoji/apple/older_man.png new file mode 100644 index 0000000000..e6e295553f Binary files /dev/null and b/public/images/emoji/apple/older_man.png differ diff --git a/public/images/emoji/apple/older_woman.png b/public/images/emoji/apple/older_woman.png new file mode 100644 index 0000000000..e57546b330 Binary files /dev/null and b/public/images/emoji/apple/older_woman.png differ diff --git a/public/images/emoji/apple/on.png b/public/images/emoji/apple/on.png new file mode 100644 index 0000000000..ad28ba63c2 Binary files /dev/null and b/public/images/emoji/apple/on.png differ diff --git a/public/images/emoji/apple/oncoming_automobile.png b/public/images/emoji/apple/oncoming_automobile.png new file mode 100644 index 0000000000..2361bf1537 Binary files /dev/null and b/public/images/emoji/apple/oncoming_automobile.png differ diff --git a/public/images/emoji/apple/oncoming_bus.png b/public/images/emoji/apple/oncoming_bus.png new file mode 100644 index 0000000000..f74afbbaa1 Binary files /dev/null and b/public/images/emoji/apple/oncoming_bus.png differ diff --git a/public/images/emoji/apple/oncoming_police_car.png b/public/images/emoji/apple/oncoming_police_car.png new file mode 100644 index 0000000000..9daf28ef11 Binary files /dev/null and b/public/images/emoji/apple/oncoming_police_car.png differ diff --git a/public/images/emoji/apple/oncoming_taxi.png b/public/images/emoji/apple/oncoming_taxi.png new file mode 100644 index 0000000000..944299846e Binary files /dev/null and b/public/images/emoji/apple/oncoming_taxi.png differ diff --git a/public/images/emoji/apple/one.png b/public/images/emoji/apple/one.png new file mode 100644 index 0000000000..75f447d75c Binary files /dev/null and b/public/images/emoji/apple/one.png differ diff --git a/public/images/emoji/apple/open_book.png b/public/images/emoji/apple/open_book.png new file mode 100644 index 0000000000..548c1f4f12 Binary files /dev/null and b/public/images/emoji/apple/open_book.png differ diff --git a/public/images/emoji/apple/open_file_folder.png b/public/images/emoji/apple/open_file_folder.png new file mode 100644 index 0000000000..f248186181 Binary files /dev/null and b/public/images/emoji/apple/open_file_folder.png differ diff --git a/public/images/emoji/apple/open_hands.png b/public/images/emoji/apple/open_hands.png new file mode 100644 index 0000000000..e082fc6b00 Binary files /dev/null and b/public/images/emoji/apple/open_hands.png differ diff --git a/public/images/emoji/apple/open_mouth.png b/public/images/emoji/apple/open_mouth.png new file mode 100644 index 0000000000..738b8dfd05 Binary files /dev/null and b/public/images/emoji/apple/open_mouth.png differ diff --git a/public/images/emoji/apple/ophiuchus.png b/public/images/emoji/apple/ophiuchus.png new file mode 100644 index 0000000000..d4e41835c3 Binary files /dev/null and b/public/images/emoji/apple/ophiuchus.png differ diff --git a/public/images/emoji/apple/orange_book.png b/public/images/emoji/apple/orange_book.png new file mode 100644 index 0000000000..b36771d6d8 Binary files /dev/null and b/public/images/emoji/apple/orange_book.png differ diff --git a/public/images/emoji/apple/outbox_tray.png b/public/images/emoji/apple/outbox_tray.png new file mode 100644 index 0000000000..39bd5731f2 Binary files /dev/null and b/public/images/emoji/apple/outbox_tray.png differ diff --git a/public/images/emoji/apple/ox.png b/public/images/emoji/apple/ox.png new file mode 100644 index 0000000000..e102a5b923 Binary files /dev/null and b/public/images/emoji/apple/ox.png differ diff --git a/public/images/emoji/apple/package.png b/public/images/emoji/apple/package.png new file mode 100644 index 0000000000..23c1eadb8c Binary files /dev/null and b/public/images/emoji/apple/package.png differ diff --git a/public/images/emoji/apple/page_facing_up.png b/public/images/emoji/apple/page_facing_up.png new file mode 100644 index 0000000000..89f7b77c99 Binary files /dev/null and b/public/images/emoji/apple/page_facing_up.png differ diff --git a/public/images/emoji/apple/page_with_curl.png b/public/images/emoji/apple/page_with_curl.png new file mode 100644 index 0000000000..bfb32cbdac Binary files /dev/null and b/public/images/emoji/apple/page_with_curl.png differ diff --git a/public/images/emoji/apple/pager.png b/public/images/emoji/apple/pager.png new file mode 100644 index 0000000000..f2d430749e Binary files /dev/null and b/public/images/emoji/apple/pager.png differ diff --git a/public/images/emoji/apple/palm_tree.png b/public/images/emoji/apple/palm_tree.png new file mode 100644 index 0000000000..970875c59b Binary files /dev/null and b/public/images/emoji/apple/palm_tree.png differ diff --git a/public/images/emoji/apple/panda_face.png b/public/images/emoji/apple/panda_face.png new file mode 100644 index 0000000000..55d72b53fa Binary files /dev/null and b/public/images/emoji/apple/panda_face.png differ diff --git a/public/images/emoji/apple/paperclip.png b/public/images/emoji/apple/paperclip.png new file mode 100644 index 0000000000..aeeeb73602 Binary files /dev/null and b/public/images/emoji/apple/paperclip.png differ diff --git a/public/images/emoji/apple/parking.png b/public/images/emoji/apple/parking.png new file mode 100644 index 0000000000..77702630f4 Binary files /dev/null and b/public/images/emoji/apple/parking.png differ diff --git a/public/images/emoji/apple/part_alternation_mark.png b/public/images/emoji/apple/part_alternation_mark.png new file mode 100644 index 0000000000..8bc91217cb Binary files /dev/null and b/public/images/emoji/apple/part_alternation_mark.png differ diff --git a/public/images/emoji/apple/partly_sunny.png b/public/images/emoji/apple/partly_sunny.png new file mode 100644 index 0000000000..8884406873 Binary files /dev/null and b/public/images/emoji/apple/partly_sunny.png differ diff --git a/public/images/emoji/apple/passport_control.png b/public/images/emoji/apple/passport_control.png new file mode 100644 index 0000000000..90a1fdc216 Binary files /dev/null and b/public/images/emoji/apple/passport_control.png differ diff --git a/public/images/emoji/apple/paw_prints.png b/public/images/emoji/apple/paw_prints.png new file mode 100644 index 0000000000..f80ff2e67a Binary files /dev/null and b/public/images/emoji/apple/paw_prints.png differ diff --git a/public/images/emoji/apple/peach.png b/public/images/emoji/apple/peach.png new file mode 100644 index 0000000000..5d507ac300 Binary files /dev/null and b/public/images/emoji/apple/peach.png differ diff --git a/public/images/emoji/apple/pear.png b/public/images/emoji/apple/pear.png new file mode 100644 index 0000000000..45206b156c Binary files /dev/null and b/public/images/emoji/apple/pear.png differ diff --git a/public/images/emoji/apple/pencil.png b/public/images/emoji/apple/pencil.png new file mode 100644 index 0000000000..57abb762d5 Binary files /dev/null and b/public/images/emoji/apple/pencil.png differ diff --git a/public/images/emoji/apple/pencil2.png b/public/images/emoji/apple/pencil2.png new file mode 100644 index 0000000000..2b89e3ae0f Binary files /dev/null and b/public/images/emoji/apple/pencil2.png differ diff --git a/public/images/emoji/apple/penguin.png b/public/images/emoji/apple/penguin.png new file mode 100644 index 0000000000..bcb20f5505 Binary files /dev/null and b/public/images/emoji/apple/penguin.png differ diff --git a/public/images/emoji/apple/pensive.png b/public/images/emoji/apple/pensive.png new file mode 100644 index 0000000000..bd7ddd3b17 Binary files /dev/null and b/public/images/emoji/apple/pensive.png differ diff --git a/public/images/emoji/apple/performing_arts.png b/public/images/emoji/apple/performing_arts.png new file mode 100644 index 0000000000..ec6058de67 Binary files /dev/null and b/public/images/emoji/apple/performing_arts.png differ diff --git a/public/images/emoji/apple/persevere.png b/public/images/emoji/apple/persevere.png new file mode 100644 index 0000000000..6914cd67d3 Binary files /dev/null and b/public/images/emoji/apple/persevere.png differ diff --git a/public/images/emoji/apple/person_frowning.png b/public/images/emoji/apple/person_frowning.png new file mode 100644 index 0000000000..a1425d60a1 Binary files /dev/null and b/public/images/emoji/apple/person_frowning.png differ diff --git a/public/images/emoji/apple/person_with_blond_hair.png b/public/images/emoji/apple/person_with_blond_hair.png new file mode 100644 index 0000000000..60e3a00d44 Binary files /dev/null and b/public/images/emoji/apple/person_with_blond_hair.png differ diff --git a/public/images/emoji/apple/person_with_pouting_face.png b/public/images/emoji/apple/person_with_pouting_face.png new file mode 100644 index 0000000000..5f1a366801 Binary files /dev/null and b/public/images/emoji/apple/person_with_pouting_face.png differ diff --git a/public/images/emoji/apple/phone.png b/public/images/emoji/apple/phone.png new file mode 100644 index 0000000000..fb1d51cdbc Binary files /dev/null and b/public/images/emoji/apple/phone.png differ diff --git a/public/images/emoji/apple/pig.png b/public/images/emoji/apple/pig.png new file mode 100644 index 0000000000..adbbff8b85 Binary files /dev/null and b/public/images/emoji/apple/pig.png differ diff --git a/public/images/emoji/apple/pig2.png b/public/images/emoji/apple/pig2.png new file mode 100644 index 0000000000..b88c155503 Binary files /dev/null and b/public/images/emoji/apple/pig2.png differ diff --git a/public/images/emoji/apple/pig_nose.png b/public/images/emoji/apple/pig_nose.png new file mode 100644 index 0000000000..668e869923 Binary files /dev/null and b/public/images/emoji/apple/pig_nose.png differ diff --git a/public/images/emoji/apple/pill.png b/public/images/emoji/apple/pill.png new file mode 100644 index 0000000000..d0e1463bf9 Binary files /dev/null and b/public/images/emoji/apple/pill.png differ diff --git a/public/images/emoji/apple/pineapple.png b/public/images/emoji/apple/pineapple.png new file mode 100644 index 0000000000..054a60e2e9 Binary files /dev/null and b/public/images/emoji/apple/pineapple.png differ diff --git a/public/images/emoji/apple/pisces.png b/public/images/emoji/apple/pisces.png new file mode 100644 index 0000000000..2f56acdb24 Binary files /dev/null and b/public/images/emoji/apple/pisces.png differ diff --git a/public/images/emoji/apple/pizza.png b/public/images/emoji/apple/pizza.png new file mode 100644 index 0000000000..fb17a2b46e Binary files /dev/null and b/public/images/emoji/apple/pizza.png differ diff --git a/public/images/emoji/apple/point_down.png b/public/images/emoji/apple/point_down.png new file mode 100644 index 0000000000..a0187533c0 Binary files /dev/null and b/public/images/emoji/apple/point_down.png differ diff --git a/public/images/emoji/apple/point_left.png b/public/images/emoji/apple/point_left.png new file mode 100644 index 0000000000..e6a0f0f38e Binary files /dev/null and b/public/images/emoji/apple/point_left.png differ diff --git a/public/images/emoji/apple/point_right.png b/public/images/emoji/apple/point_right.png new file mode 100644 index 0000000000..5d3bb36955 Binary files /dev/null and b/public/images/emoji/apple/point_right.png differ diff --git a/public/images/emoji/apple/point_up.png b/public/images/emoji/apple/point_up.png new file mode 100644 index 0000000000..fe4b03fd38 Binary files /dev/null and b/public/images/emoji/apple/point_up.png differ diff --git a/public/images/emoji/apple/point_up_2.png b/public/images/emoji/apple/point_up_2.png new file mode 100644 index 0000000000..531273376e Binary files /dev/null and b/public/images/emoji/apple/point_up_2.png differ diff --git a/public/images/emoji/apple/police_car.png b/public/images/emoji/apple/police_car.png new file mode 100644 index 0000000000..84527e1254 Binary files /dev/null and b/public/images/emoji/apple/police_car.png differ diff --git a/public/images/emoji/apple/poodle.png b/public/images/emoji/apple/poodle.png new file mode 100644 index 0000000000..2c750091a0 Binary files /dev/null and b/public/images/emoji/apple/poodle.png differ diff --git a/public/images/emoji/apple/poop.png b/public/images/emoji/apple/poop.png new file mode 100644 index 0000000000..75544655b3 Binary files /dev/null and b/public/images/emoji/apple/poop.png differ diff --git a/public/images/emoji/apple/post_office.png b/public/images/emoji/apple/post_office.png new file mode 100644 index 0000000000..2975788a77 Binary files /dev/null and b/public/images/emoji/apple/post_office.png differ diff --git a/public/images/emoji/apple/postal_horn.png b/public/images/emoji/apple/postal_horn.png new file mode 100644 index 0000000000..d2dc714ded Binary files /dev/null and b/public/images/emoji/apple/postal_horn.png differ diff --git a/public/images/emoji/apple/postbox.png b/public/images/emoji/apple/postbox.png new file mode 100644 index 0000000000..ddad85cd85 Binary files /dev/null and b/public/images/emoji/apple/postbox.png differ diff --git a/public/images/emoji/apple/potable_water.png b/public/images/emoji/apple/potable_water.png new file mode 100644 index 0000000000..66c599369c Binary files /dev/null and b/public/images/emoji/apple/potable_water.png differ diff --git a/public/images/emoji/apple/pouch.png b/public/images/emoji/apple/pouch.png new file mode 100644 index 0000000000..765b285fc7 Binary files /dev/null and b/public/images/emoji/apple/pouch.png differ diff --git a/public/images/emoji/apple/poultry_leg.png b/public/images/emoji/apple/poultry_leg.png new file mode 100644 index 0000000000..70251a8e94 Binary files /dev/null and b/public/images/emoji/apple/poultry_leg.png differ diff --git a/public/images/emoji/apple/pound.png b/public/images/emoji/apple/pound.png new file mode 100644 index 0000000000..a479ce6e46 Binary files /dev/null and b/public/images/emoji/apple/pound.png differ diff --git a/public/images/emoji/apple/pouting_cat.png b/public/images/emoji/apple/pouting_cat.png new file mode 100644 index 0000000000..101540cc26 Binary files /dev/null and b/public/images/emoji/apple/pouting_cat.png differ diff --git a/public/images/emoji/apple/pray.png b/public/images/emoji/apple/pray.png new file mode 100644 index 0000000000..3fefd5ad59 Binary files /dev/null and b/public/images/emoji/apple/pray.png differ diff --git a/public/images/emoji/apple/princess.png b/public/images/emoji/apple/princess.png new file mode 100644 index 0000000000..ee0213d194 Binary files /dev/null and b/public/images/emoji/apple/princess.png differ diff --git a/public/images/emoji/apple/punch.png b/public/images/emoji/apple/punch.png new file mode 100644 index 0000000000..3a76b98e78 Binary files /dev/null and b/public/images/emoji/apple/punch.png differ diff --git a/public/images/emoji/apple/purple_heart.png b/public/images/emoji/apple/purple_heart.png new file mode 100644 index 0000000000..cc9f2b4686 Binary files /dev/null and b/public/images/emoji/apple/purple_heart.png differ diff --git a/public/images/emoji/apple/purse.png b/public/images/emoji/apple/purse.png new file mode 100644 index 0000000000..41ab614a12 Binary files /dev/null and b/public/images/emoji/apple/purse.png differ diff --git a/public/images/emoji/apple/pushpin.png b/public/images/emoji/apple/pushpin.png new file mode 100644 index 0000000000..cda2c126c2 Binary files /dev/null and b/public/images/emoji/apple/pushpin.png differ diff --git a/public/images/emoji/apple/put_litter_in_its_place.png b/public/images/emoji/apple/put_litter_in_its_place.png new file mode 100644 index 0000000000..3a2e7caffc Binary files /dev/null and b/public/images/emoji/apple/put_litter_in_its_place.png differ diff --git a/public/images/emoji/apple/question.png b/public/images/emoji/apple/question.png new file mode 100644 index 0000000000..13ebaed743 Binary files /dev/null and b/public/images/emoji/apple/question.png differ diff --git a/public/images/emoji/apple/rabbit.png b/public/images/emoji/apple/rabbit.png new file mode 100644 index 0000000000..b683884410 Binary files /dev/null and b/public/images/emoji/apple/rabbit.png differ diff --git a/public/images/emoji/apple/rabbit2.png b/public/images/emoji/apple/rabbit2.png new file mode 100644 index 0000000000..7e9469f376 Binary files /dev/null and b/public/images/emoji/apple/rabbit2.png differ diff --git a/public/images/emoji/apple/racehorse.png b/public/images/emoji/apple/racehorse.png new file mode 100644 index 0000000000..5a3e1566f2 Binary files /dev/null and b/public/images/emoji/apple/racehorse.png differ diff --git a/public/images/emoji/apple/radio.png b/public/images/emoji/apple/radio.png new file mode 100644 index 0000000000..dcdeeff223 Binary files /dev/null and b/public/images/emoji/apple/radio.png differ diff --git a/public/images/emoji/apple/radio_button.png b/public/images/emoji/apple/radio_button.png new file mode 100644 index 0000000000..1a9c6722e7 Binary files /dev/null and b/public/images/emoji/apple/radio_button.png differ diff --git a/public/images/emoji/apple/rage.png b/public/images/emoji/apple/rage.png new file mode 100644 index 0000000000..84ef537606 Binary files /dev/null and b/public/images/emoji/apple/rage.png differ diff --git a/public/images/emoji/apple/railway_car.png b/public/images/emoji/apple/railway_car.png new file mode 100644 index 0000000000..2464ff0c0d Binary files /dev/null and b/public/images/emoji/apple/railway_car.png differ diff --git a/public/images/emoji/apple/rainbow.png b/public/images/emoji/apple/rainbow.png new file mode 100644 index 0000000000..7bce80f7b9 Binary files /dev/null and b/public/images/emoji/apple/rainbow.png differ diff --git a/public/images/emoji/apple/raised_hand.png b/public/images/emoji/apple/raised_hand.png new file mode 100644 index 0000000000..dea3153758 Binary files /dev/null and b/public/images/emoji/apple/raised_hand.png differ diff --git a/public/images/emoji/apple/raised_hands.png b/public/images/emoji/apple/raised_hands.png new file mode 100644 index 0000000000..06d4933ab7 Binary files /dev/null and b/public/images/emoji/apple/raised_hands.png differ diff --git a/public/images/emoji/apple/raising_hand.png b/public/images/emoji/apple/raising_hand.png new file mode 100644 index 0000000000..9ede5836cb Binary files /dev/null and b/public/images/emoji/apple/raising_hand.png differ diff --git a/public/images/emoji/apple/ram.png b/public/images/emoji/apple/ram.png new file mode 100644 index 0000000000..00eb2e75dd Binary files /dev/null and b/public/images/emoji/apple/ram.png differ diff --git a/public/images/emoji/apple/ramen.png b/public/images/emoji/apple/ramen.png new file mode 100644 index 0000000000..76cafae015 Binary files /dev/null and b/public/images/emoji/apple/ramen.png differ diff --git a/public/images/emoji/apple/rat.png b/public/images/emoji/apple/rat.png new file mode 100644 index 0000000000..987d30c0a1 Binary files /dev/null and b/public/images/emoji/apple/rat.png differ diff --git a/public/images/emoji/apple/recycle.png b/public/images/emoji/apple/recycle.png new file mode 100644 index 0000000000..a58af6e26d Binary files /dev/null and b/public/images/emoji/apple/recycle.png differ diff --git a/public/images/emoji/apple/red_car.png b/public/images/emoji/apple/red_car.png new file mode 100644 index 0000000000..d959c48e60 Binary files /dev/null and b/public/images/emoji/apple/red_car.png differ diff --git a/public/images/emoji/apple/red_circle.png b/public/images/emoji/apple/red_circle.png new file mode 100644 index 0000000000..2734885bbd Binary files /dev/null and b/public/images/emoji/apple/red_circle.png differ diff --git a/public/images/emoji/apple/registered.png b/public/images/emoji/apple/registered.png new file mode 100644 index 0000000000..258d1dc3ee Binary files /dev/null and b/public/images/emoji/apple/registered.png differ diff --git a/public/images/emoji/apple/relaxed.png b/public/images/emoji/apple/relaxed.png new file mode 100644 index 0000000000..ef7aa7e22a Binary files /dev/null and b/public/images/emoji/apple/relaxed.png differ diff --git a/public/images/emoji/apple/relieved.png b/public/images/emoji/apple/relieved.png new file mode 100644 index 0000000000..4d2fa6d206 Binary files /dev/null and b/public/images/emoji/apple/relieved.png differ diff --git a/public/images/emoji/apple/repeat.png b/public/images/emoji/apple/repeat.png new file mode 100644 index 0000000000..a92d559020 Binary files /dev/null and b/public/images/emoji/apple/repeat.png differ diff --git a/public/images/emoji/apple/repeat_one.png b/public/images/emoji/apple/repeat_one.png new file mode 100644 index 0000000000..2fa085cc57 Binary files /dev/null and b/public/images/emoji/apple/repeat_one.png differ diff --git a/public/images/emoji/apple/restroom.png b/public/images/emoji/apple/restroom.png new file mode 100644 index 0000000000..2ab7e08692 Binary files /dev/null and b/public/images/emoji/apple/restroom.png differ diff --git a/public/images/emoji/apple/revolving_hearts.png b/public/images/emoji/apple/revolving_hearts.png new file mode 100644 index 0000000000..15a77f5e8e Binary files /dev/null and b/public/images/emoji/apple/revolving_hearts.png differ diff --git a/public/images/emoji/apple/rewind.png b/public/images/emoji/apple/rewind.png new file mode 100644 index 0000000000..989fa59b23 Binary files /dev/null and b/public/images/emoji/apple/rewind.png differ diff --git a/public/images/emoji/apple/ribbon.png b/public/images/emoji/apple/ribbon.png new file mode 100644 index 0000000000..ae261ad25e Binary files /dev/null and b/public/images/emoji/apple/ribbon.png differ diff --git a/public/images/emoji/apple/rice.png b/public/images/emoji/apple/rice.png new file mode 100644 index 0000000000..c8bcbb013a Binary files /dev/null and b/public/images/emoji/apple/rice.png differ diff --git a/public/images/emoji/apple/rice_ball.png b/public/images/emoji/apple/rice_ball.png new file mode 100644 index 0000000000..eeb9ee68ee Binary files /dev/null and b/public/images/emoji/apple/rice_ball.png differ diff --git a/public/images/emoji/apple/rice_cracker.png b/public/images/emoji/apple/rice_cracker.png new file mode 100644 index 0000000000..1f10e6e372 Binary files /dev/null and b/public/images/emoji/apple/rice_cracker.png differ diff --git a/public/images/emoji/apple/rice_scene.png b/public/images/emoji/apple/rice_scene.png new file mode 100644 index 0000000000..eedbe51531 Binary files /dev/null and b/public/images/emoji/apple/rice_scene.png differ diff --git a/public/images/emoji/apple/ring.png b/public/images/emoji/apple/ring.png new file mode 100644 index 0000000000..18207efff2 Binary files /dev/null and b/public/images/emoji/apple/ring.png differ diff --git a/public/images/emoji/apple/rocket.png b/public/images/emoji/apple/rocket.png new file mode 100644 index 0000000000..465bf55a31 Binary files /dev/null and b/public/images/emoji/apple/rocket.png differ diff --git a/public/images/emoji/apple/roller_coaster.png b/public/images/emoji/apple/roller_coaster.png new file mode 100644 index 0000000000..a578a7f26c Binary files /dev/null and b/public/images/emoji/apple/roller_coaster.png differ diff --git a/public/images/emoji/apple/rooster.png b/public/images/emoji/apple/rooster.png new file mode 100644 index 0000000000..d9c396d130 Binary files /dev/null and b/public/images/emoji/apple/rooster.png differ diff --git a/public/images/emoji/apple/rose.png b/public/images/emoji/apple/rose.png new file mode 100644 index 0000000000..ab83159a9e Binary files /dev/null and b/public/images/emoji/apple/rose.png differ diff --git a/public/images/emoji/apple/rotating_light.png b/public/images/emoji/apple/rotating_light.png new file mode 100644 index 0000000000..95c6e5f1db Binary files /dev/null and b/public/images/emoji/apple/rotating_light.png differ diff --git a/public/images/emoji/apple/round_pushpin.png b/public/images/emoji/apple/round_pushpin.png new file mode 100644 index 0000000000..77517be3d0 Binary files /dev/null and b/public/images/emoji/apple/round_pushpin.png differ diff --git a/public/images/emoji/apple/rowboat.png b/public/images/emoji/apple/rowboat.png new file mode 100644 index 0000000000..f9182401fb Binary files /dev/null and b/public/images/emoji/apple/rowboat.png differ diff --git a/public/images/emoji/apple/ru.png b/public/images/emoji/apple/ru.png new file mode 100644 index 0000000000..9895c1c0da Binary files /dev/null and b/public/images/emoji/apple/ru.png differ diff --git a/public/images/emoji/apple/rugby_football.png b/public/images/emoji/apple/rugby_football.png new file mode 100644 index 0000000000..9db7220e79 Binary files /dev/null and b/public/images/emoji/apple/rugby_football.png differ diff --git a/public/images/emoji/apple/runner.png b/public/images/emoji/apple/runner.png new file mode 100644 index 0000000000..5aa40befed Binary files /dev/null and b/public/images/emoji/apple/runner.png differ diff --git a/public/images/emoji/apple/running.png b/public/images/emoji/apple/running.png new file mode 100644 index 0000000000..5aa40befed Binary files /dev/null and b/public/images/emoji/apple/running.png differ diff --git a/public/images/emoji/apple/running_shirt_with_sash.png b/public/images/emoji/apple/running_shirt_with_sash.png new file mode 100644 index 0000000000..d75e999a0a Binary files /dev/null and b/public/images/emoji/apple/running_shirt_with_sash.png differ diff --git a/public/images/emoji/apple/sa.png b/public/images/emoji/apple/sa.png new file mode 100644 index 0000000000..e8b67ab926 Binary files /dev/null and b/public/images/emoji/apple/sa.png differ diff --git a/public/images/emoji/apple/sagittarius.png b/public/images/emoji/apple/sagittarius.png new file mode 100644 index 0000000000..87904cb4d7 Binary files /dev/null and b/public/images/emoji/apple/sagittarius.png differ diff --git a/public/images/emoji/apple/sailboat.png b/public/images/emoji/apple/sailboat.png new file mode 100644 index 0000000000..a175599015 Binary files /dev/null and b/public/images/emoji/apple/sailboat.png differ diff --git a/public/images/emoji/apple/sake.png b/public/images/emoji/apple/sake.png new file mode 100644 index 0000000000..955724a116 Binary files /dev/null and b/public/images/emoji/apple/sake.png differ diff --git a/public/images/emoji/apple/sandal.png b/public/images/emoji/apple/sandal.png new file mode 100644 index 0000000000..5ec65cda60 Binary files /dev/null and b/public/images/emoji/apple/sandal.png differ diff --git a/public/images/emoji/apple/santa.png b/public/images/emoji/apple/santa.png new file mode 100644 index 0000000000..ab5a8db5c0 Binary files /dev/null and b/public/images/emoji/apple/santa.png differ diff --git a/public/images/emoji/apple/satellite.png b/public/images/emoji/apple/satellite.png new file mode 100644 index 0000000000..bc140cdda5 Binary files /dev/null and b/public/images/emoji/apple/satellite.png differ diff --git a/public/images/emoji/apple/satisfied.png b/public/images/emoji/apple/satisfied.png new file mode 100644 index 0000000000..85605c7329 Binary files /dev/null and b/public/images/emoji/apple/satisfied.png differ diff --git a/public/images/emoji/apple/saxophone.png b/public/images/emoji/apple/saxophone.png new file mode 100644 index 0000000000..4efc3eaf32 Binary files /dev/null and b/public/images/emoji/apple/saxophone.png differ diff --git a/public/images/emoji/apple/school.png b/public/images/emoji/apple/school.png new file mode 100644 index 0000000000..a80f2f7590 Binary files /dev/null and b/public/images/emoji/apple/school.png differ diff --git a/public/images/emoji/apple/school_satchel.png b/public/images/emoji/apple/school_satchel.png new file mode 100644 index 0000000000..042dc2e9a5 Binary files /dev/null and b/public/images/emoji/apple/school_satchel.png differ diff --git a/public/images/emoji/apple/scissors.png b/public/images/emoji/apple/scissors.png new file mode 100644 index 0000000000..5673b36f2a Binary files /dev/null and b/public/images/emoji/apple/scissors.png differ diff --git a/public/images/emoji/apple/scorpius.png b/public/images/emoji/apple/scorpius.png new file mode 100644 index 0000000000..029e51ccc4 Binary files /dev/null and b/public/images/emoji/apple/scorpius.png differ diff --git a/public/images/emoji/apple/scream.png b/public/images/emoji/apple/scream.png new file mode 100644 index 0000000000..225b74bd4b Binary files /dev/null and b/public/images/emoji/apple/scream.png differ diff --git a/public/images/emoji/apple/scream_cat.png b/public/images/emoji/apple/scream_cat.png new file mode 100644 index 0000000000..2624d8cc1a Binary files /dev/null and b/public/images/emoji/apple/scream_cat.png differ diff --git a/public/images/emoji/apple/scroll.png b/public/images/emoji/apple/scroll.png new file mode 100644 index 0000000000..79e7f10c81 Binary files /dev/null and b/public/images/emoji/apple/scroll.png differ diff --git a/public/images/emoji/apple/seat.png b/public/images/emoji/apple/seat.png new file mode 100644 index 0000000000..dbe5b3d6cd Binary files /dev/null and b/public/images/emoji/apple/seat.png differ diff --git a/public/images/emoji/apple/secret.png b/public/images/emoji/apple/secret.png new file mode 100644 index 0000000000..1afd76e6f2 Binary files /dev/null and b/public/images/emoji/apple/secret.png differ diff --git a/public/images/emoji/apple/see_no_evil.png b/public/images/emoji/apple/see_no_evil.png new file mode 100644 index 0000000000..2adaa35e76 Binary files /dev/null and b/public/images/emoji/apple/see_no_evil.png differ diff --git a/public/images/emoji/apple/seedling.png b/public/images/emoji/apple/seedling.png new file mode 100644 index 0000000000..a3a3c0d533 Binary files /dev/null and b/public/images/emoji/apple/seedling.png differ diff --git a/public/images/emoji/apple/seven.png b/public/images/emoji/apple/seven.png new file mode 100644 index 0000000000..2698a004fc Binary files /dev/null and b/public/images/emoji/apple/seven.png differ diff --git a/public/images/emoji/apple/shaved_ice.png b/public/images/emoji/apple/shaved_ice.png new file mode 100644 index 0000000000..05ada7bacc Binary files /dev/null and b/public/images/emoji/apple/shaved_ice.png differ diff --git a/public/images/emoji/apple/sheep.png b/public/images/emoji/apple/sheep.png new file mode 100644 index 0000000000..e7bc2c5e87 Binary files /dev/null and b/public/images/emoji/apple/sheep.png differ diff --git a/public/images/emoji/apple/shell.png b/public/images/emoji/apple/shell.png new file mode 100644 index 0000000000..b1d01023d2 Binary files /dev/null and b/public/images/emoji/apple/shell.png differ diff --git a/public/images/emoji/apple/ship.png b/public/images/emoji/apple/ship.png new file mode 100644 index 0000000000..e31478286e Binary files /dev/null and b/public/images/emoji/apple/ship.png differ diff --git a/public/images/emoji/apple/shirt.png b/public/images/emoji/apple/shirt.png new file mode 100644 index 0000000000..5ac2001128 Binary files /dev/null and b/public/images/emoji/apple/shirt.png differ diff --git a/public/images/emoji/apple/shit.png b/public/images/emoji/apple/shit.png new file mode 100644 index 0000000000..75544655b3 Binary files /dev/null and b/public/images/emoji/apple/shit.png differ diff --git a/public/images/emoji/apple/shoe.png b/public/images/emoji/apple/shoe.png new file mode 100644 index 0000000000..710a276ecb Binary files /dev/null and b/public/images/emoji/apple/shoe.png differ diff --git a/public/images/emoji/apple/shower.png b/public/images/emoji/apple/shower.png new file mode 100644 index 0000000000..72e0e7586a Binary files /dev/null and b/public/images/emoji/apple/shower.png differ diff --git a/public/images/emoji/apple/signal_strength.png b/public/images/emoji/apple/signal_strength.png new file mode 100644 index 0000000000..24eaacd1d3 Binary files /dev/null and b/public/images/emoji/apple/signal_strength.png differ diff --git a/public/images/emoji/apple/six.png b/public/images/emoji/apple/six.png new file mode 100644 index 0000000000..d3b01a5ec3 Binary files /dev/null and b/public/images/emoji/apple/six.png differ diff --git a/public/images/emoji/apple/six_pointed_star.png b/public/images/emoji/apple/six_pointed_star.png new file mode 100644 index 0000000000..96ad5657db Binary files /dev/null and b/public/images/emoji/apple/six_pointed_star.png differ diff --git a/public/images/emoji/apple/ski.png b/public/images/emoji/apple/ski.png new file mode 100644 index 0000000000..c1c83ed16e Binary files /dev/null and b/public/images/emoji/apple/ski.png differ diff --git a/public/images/emoji/apple/skull.png b/public/images/emoji/apple/skull.png new file mode 100644 index 0000000000..01f7beed22 Binary files /dev/null and b/public/images/emoji/apple/skull.png differ diff --git a/public/images/emoji/apple/sleeping.png b/public/images/emoji/apple/sleeping.png new file mode 100644 index 0000000000..c6e61cc947 Binary files /dev/null and b/public/images/emoji/apple/sleeping.png differ diff --git a/public/images/emoji/apple/sleepy.png b/public/images/emoji/apple/sleepy.png new file mode 100644 index 0000000000..167f1ca4b4 Binary files /dev/null and b/public/images/emoji/apple/sleepy.png differ diff --git a/public/images/emoji/apple/slot_machine.png b/public/images/emoji/apple/slot_machine.png new file mode 100644 index 0000000000..92f2284d4d Binary files /dev/null and b/public/images/emoji/apple/slot_machine.png differ diff --git a/public/images/emoji/apple/small_blue_diamond.png b/public/images/emoji/apple/small_blue_diamond.png new file mode 100644 index 0000000000..043f0eb9e2 Binary files /dev/null and b/public/images/emoji/apple/small_blue_diamond.png differ diff --git a/public/images/emoji/apple/small_orange_diamond.png b/public/images/emoji/apple/small_orange_diamond.png new file mode 100644 index 0000000000..289cec5470 Binary files /dev/null and b/public/images/emoji/apple/small_orange_diamond.png differ diff --git a/public/images/emoji/apple/small_red_triangle.png b/public/images/emoji/apple/small_red_triangle.png new file mode 100644 index 0000000000..2a89c9c2e7 Binary files /dev/null and b/public/images/emoji/apple/small_red_triangle.png differ diff --git a/public/images/emoji/apple/small_red_triangle_down.png b/public/images/emoji/apple/small_red_triangle_down.png new file mode 100644 index 0000000000..27720b68e4 Binary files /dev/null and b/public/images/emoji/apple/small_red_triangle_down.png differ diff --git a/public/images/emoji/apple/smile.png b/public/images/emoji/apple/smile.png new file mode 100644 index 0000000000..2818e79119 Binary files /dev/null and b/public/images/emoji/apple/smile.png differ diff --git a/public/images/emoji/apple/smile_cat.png b/public/images/emoji/apple/smile_cat.png new file mode 100644 index 0000000000..f0181741f9 Binary files /dev/null and b/public/images/emoji/apple/smile_cat.png differ diff --git a/public/images/emoji/apple/smiley.png b/public/images/emoji/apple/smiley.png new file mode 100644 index 0000000000..b4abd8111a Binary files /dev/null and b/public/images/emoji/apple/smiley.png differ diff --git a/public/images/emoji/apple/smiley_cat.png b/public/images/emoji/apple/smiley_cat.png new file mode 100644 index 0000000000..0100e20466 Binary files /dev/null and b/public/images/emoji/apple/smiley_cat.png differ diff --git a/public/images/emoji/apple/smiling_imp.png b/public/images/emoji/apple/smiling_imp.png new file mode 100644 index 0000000000..504b746324 Binary files /dev/null and b/public/images/emoji/apple/smiling_imp.png differ diff --git a/public/images/emoji/apple/smirk.png b/public/images/emoji/apple/smirk.png new file mode 100644 index 0000000000..94c2a21b69 Binary files /dev/null and b/public/images/emoji/apple/smirk.png differ diff --git a/public/images/emoji/apple/smirk_cat.png b/public/images/emoji/apple/smirk_cat.png new file mode 100644 index 0000000000..6644e66bf4 Binary files /dev/null and b/public/images/emoji/apple/smirk_cat.png differ diff --git a/public/images/emoji/apple/smoking.png b/public/images/emoji/apple/smoking.png new file mode 100644 index 0000000000..cc50b385b5 Binary files /dev/null and b/public/images/emoji/apple/smoking.png differ diff --git a/public/images/emoji/apple/snail.png b/public/images/emoji/apple/snail.png new file mode 100644 index 0000000000..916f02d206 Binary files /dev/null and b/public/images/emoji/apple/snail.png differ diff --git a/public/images/emoji/apple/snake.png b/public/images/emoji/apple/snake.png new file mode 100644 index 0000000000..1ecc42bbca Binary files /dev/null and b/public/images/emoji/apple/snake.png differ diff --git a/public/images/emoji/apple/snowboarder.png b/public/images/emoji/apple/snowboarder.png new file mode 100644 index 0000000000..0e723eeaf5 Binary files /dev/null and b/public/images/emoji/apple/snowboarder.png differ diff --git a/public/images/emoji/apple/snowflake.png b/public/images/emoji/apple/snowflake.png new file mode 100644 index 0000000000..116e4c9856 Binary files /dev/null and b/public/images/emoji/apple/snowflake.png differ diff --git a/public/images/emoji/apple/snowman.png b/public/images/emoji/apple/snowman.png new file mode 100644 index 0000000000..0fc6dd4984 Binary files /dev/null and b/public/images/emoji/apple/snowman.png differ diff --git a/public/images/emoji/apple/sob.png b/public/images/emoji/apple/sob.png new file mode 100644 index 0000000000..6ccdfa9f2d Binary files /dev/null and b/public/images/emoji/apple/sob.png differ diff --git a/public/images/emoji/apple/soccer.png b/public/images/emoji/apple/soccer.png new file mode 100644 index 0000000000..168422a0dc Binary files /dev/null and b/public/images/emoji/apple/soccer.png differ diff --git a/public/images/emoji/apple/soon.png b/public/images/emoji/apple/soon.png new file mode 100644 index 0000000000..f5f4c31f97 Binary files /dev/null and b/public/images/emoji/apple/soon.png differ diff --git a/public/images/emoji/apple/sos.png b/public/images/emoji/apple/sos.png new file mode 100644 index 0000000000..e9ecad91dc Binary files /dev/null and b/public/images/emoji/apple/sos.png differ diff --git a/public/images/emoji/apple/sound.png b/public/images/emoji/apple/sound.png new file mode 100644 index 0000000000..a2bb894f6c Binary files /dev/null and b/public/images/emoji/apple/sound.png differ diff --git a/public/images/emoji/apple/space_invader.png b/public/images/emoji/apple/space_invader.png new file mode 100644 index 0000000000..bde1b85754 Binary files /dev/null and b/public/images/emoji/apple/space_invader.png differ diff --git a/public/images/emoji/apple/spades.png b/public/images/emoji/apple/spades.png new file mode 100644 index 0000000000..2551e77ee9 Binary files /dev/null and b/public/images/emoji/apple/spades.png differ diff --git a/public/images/emoji/apple/spaghetti.png b/public/images/emoji/apple/spaghetti.png new file mode 100644 index 0000000000..be166e778c Binary files /dev/null and b/public/images/emoji/apple/spaghetti.png differ diff --git a/public/images/emoji/apple/sparkle.png b/public/images/emoji/apple/sparkle.png new file mode 100644 index 0000000000..39e29ddf97 Binary files /dev/null and b/public/images/emoji/apple/sparkle.png differ diff --git a/public/images/emoji/apple/sparkler.png b/public/images/emoji/apple/sparkler.png new file mode 100644 index 0000000000..5c87ad16e4 Binary files /dev/null and b/public/images/emoji/apple/sparkler.png differ diff --git a/public/images/emoji/apple/sparkles.png b/public/images/emoji/apple/sparkles.png new file mode 100644 index 0000000000..e86b72a296 Binary files /dev/null and b/public/images/emoji/apple/sparkles.png differ diff --git a/public/images/emoji/apple/sparkling_heart.png b/public/images/emoji/apple/sparkling_heart.png new file mode 100644 index 0000000000..b6584c20d6 Binary files /dev/null and b/public/images/emoji/apple/sparkling_heart.png differ diff --git a/public/images/emoji/apple/speak_no_evil.png b/public/images/emoji/apple/speak_no_evil.png new file mode 100644 index 0000000000..cedb6892ad Binary files /dev/null and b/public/images/emoji/apple/speak_no_evil.png differ diff --git a/public/images/emoji/apple/speaker.png b/public/images/emoji/apple/speaker.png new file mode 100644 index 0000000000..f9dad6af57 Binary files /dev/null and b/public/images/emoji/apple/speaker.png differ diff --git a/public/images/emoji/apple/speech_balloon.png b/public/images/emoji/apple/speech_balloon.png new file mode 100644 index 0000000000..39821d58a2 Binary files /dev/null and b/public/images/emoji/apple/speech_balloon.png differ diff --git a/public/images/emoji/apple/speedboat.png b/public/images/emoji/apple/speedboat.png new file mode 100644 index 0000000000..48f62e8e1c Binary files /dev/null and b/public/images/emoji/apple/speedboat.png differ diff --git a/public/images/emoji/apple/star.png b/public/images/emoji/apple/star.png new file mode 100644 index 0000000000..ddbfb4ca73 Binary files /dev/null and b/public/images/emoji/apple/star.png differ diff --git a/public/images/emoji/apple/star2.png b/public/images/emoji/apple/star2.png new file mode 100644 index 0000000000..c76d820460 Binary files /dev/null and b/public/images/emoji/apple/star2.png differ diff --git a/public/images/emoji/apple/stars.png b/public/images/emoji/apple/stars.png new file mode 100644 index 0000000000..ff8568a158 Binary files /dev/null and b/public/images/emoji/apple/stars.png differ diff --git a/public/images/emoji/apple/station.png b/public/images/emoji/apple/station.png new file mode 100644 index 0000000000..3bb66d1b73 Binary files /dev/null and b/public/images/emoji/apple/station.png differ diff --git a/public/images/emoji/apple/statue_of_liberty.png b/public/images/emoji/apple/statue_of_liberty.png new file mode 100644 index 0000000000..f94f3bf066 Binary files /dev/null and b/public/images/emoji/apple/statue_of_liberty.png differ diff --git a/public/images/emoji/apple/steam_locomotive.png b/public/images/emoji/apple/steam_locomotive.png new file mode 100644 index 0000000000..9f36c7803d Binary files /dev/null and b/public/images/emoji/apple/steam_locomotive.png differ diff --git a/public/images/emoji/apple/stew.png b/public/images/emoji/apple/stew.png new file mode 100644 index 0000000000..17c68daddf Binary files /dev/null and b/public/images/emoji/apple/stew.png differ diff --git a/public/images/emoji/apple/straight_ruler.png b/public/images/emoji/apple/straight_ruler.png new file mode 100644 index 0000000000..64dbdc3ab0 Binary files /dev/null and b/public/images/emoji/apple/straight_ruler.png differ diff --git a/public/images/emoji/apple/strawberry.png b/public/images/emoji/apple/strawberry.png new file mode 100644 index 0000000000..4fcde96c68 Binary files /dev/null and b/public/images/emoji/apple/strawberry.png differ diff --git a/public/images/emoji/apple/stuck_out_tongue.png b/public/images/emoji/apple/stuck_out_tongue.png new file mode 100644 index 0000000000..e3dd39e4d2 Binary files /dev/null and b/public/images/emoji/apple/stuck_out_tongue.png differ diff --git a/public/images/emoji/apple/stuck_out_tongue_closed_eyes.png b/public/images/emoji/apple/stuck_out_tongue_closed_eyes.png new file mode 100644 index 0000000000..974eb23b01 Binary files /dev/null and b/public/images/emoji/apple/stuck_out_tongue_closed_eyes.png differ diff --git a/public/images/emoji/apple/stuck_out_tongue_winking_eye.png b/public/images/emoji/apple/stuck_out_tongue_winking_eye.png new file mode 100644 index 0000000000..6f7b54d7fa Binary files /dev/null and b/public/images/emoji/apple/stuck_out_tongue_winking_eye.png differ diff --git a/public/images/emoji/apple/sun_with_face.png b/public/images/emoji/apple/sun_with_face.png new file mode 100644 index 0000000000..573963ddf3 Binary files /dev/null and b/public/images/emoji/apple/sun_with_face.png differ diff --git a/public/images/emoji/apple/sunflower.png b/public/images/emoji/apple/sunflower.png new file mode 100644 index 0000000000..cb9445fd52 Binary files /dev/null and b/public/images/emoji/apple/sunflower.png differ diff --git a/public/images/emoji/apple/sunglasses.png b/public/images/emoji/apple/sunglasses.png new file mode 100644 index 0000000000..018e7a9e1b Binary files /dev/null and b/public/images/emoji/apple/sunglasses.png differ diff --git a/public/images/emoji/apple/sunny.png b/public/images/emoji/apple/sunny.png new file mode 100644 index 0000000000..8581935a3f Binary files /dev/null and b/public/images/emoji/apple/sunny.png differ diff --git a/public/images/emoji/apple/sunrise.png b/public/images/emoji/apple/sunrise.png new file mode 100644 index 0000000000..63b1016d83 Binary files /dev/null and b/public/images/emoji/apple/sunrise.png differ diff --git a/public/images/emoji/apple/sunrise_over_mountains.png b/public/images/emoji/apple/sunrise_over_mountains.png new file mode 100644 index 0000000000..cf69bee42d Binary files /dev/null and b/public/images/emoji/apple/sunrise_over_mountains.png differ diff --git a/public/images/emoji/apple/surfer.png b/public/images/emoji/apple/surfer.png new file mode 100644 index 0000000000..44d7686a25 Binary files /dev/null and b/public/images/emoji/apple/surfer.png differ diff --git a/public/images/emoji/apple/sushi.png b/public/images/emoji/apple/sushi.png new file mode 100644 index 0000000000..9d9ae82420 Binary files /dev/null and b/public/images/emoji/apple/sushi.png differ diff --git a/public/images/emoji/apple/suspension_railway.png b/public/images/emoji/apple/suspension_railway.png new file mode 100644 index 0000000000..d7bcb9efa0 Binary files /dev/null and b/public/images/emoji/apple/suspension_railway.png differ diff --git a/public/images/emoji/apple/sweat.png b/public/images/emoji/apple/sweat.png new file mode 100644 index 0000000000..b0aa7aba9e Binary files /dev/null and b/public/images/emoji/apple/sweat.png differ diff --git a/public/images/emoji/apple/sweat_drops.png b/public/images/emoji/apple/sweat_drops.png new file mode 100644 index 0000000000..b828bf7d50 Binary files /dev/null and b/public/images/emoji/apple/sweat_drops.png differ diff --git a/public/images/emoji/apple/sweat_smile.png b/public/images/emoji/apple/sweat_smile.png new file mode 100644 index 0000000000..d7fe48fdac Binary files /dev/null and b/public/images/emoji/apple/sweat_smile.png differ diff --git a/public/images/emoji/apple/sweet_potato.png b/public/images/emoji/apple/sweet_potato.png new file mode 100644 index 0000000000..5f74aa2e4f Binary files /dev/null and b/public/images/emoji/apple/sweet_potato.png differ diff --git a/public/images/emoji/apple/swimmer.png b/public/images/emoji/apple/swimmer.png new file mode 100644 index 0000000000..72abc88e92 Binary files /dev/null and b/public/images/emoji/apple/swimmer.png differ diff --git a/public/images/emoji/apple/symbols.png b/public/images/emoji/apple/symbols.png new file mode 100644 index 0000000000..0b04e4aeaa Binary files /dev/null and b/public/images/emoji/apple/symbols.png differ diff --git a/public/images/emoji/apple/syringe.png b/public/images/emoji/apple/syringe.png new file mode 100644 index 0000000000..ee04531dca Binary files /dev/null and b/public/images/emoji/apple/syringe.png differ diff --git a/public/images/emoji/apple/tada.png b/public/images/emoji/apple/tada.png new file mode 100644 index 0000000000..c8ad686851 Binary files /dev/null and b/public/images/emoji/apple/tada.png differ diff --git a/public/images/emoji/apple/tanabata_tree.png b/public/images/emoji/apple/tanabata_tree.png new file mode 100644 index 0000000000..3f0cf9824f Binary files /dev/null and b/public/images/emoji/apple/tanabata_tree.png differ diff --git a/public/images/emoji/apple/tangerine.png b/public/images/emoji/apple/tangerine.png new file mode 100644 index 0000000000..f14f05f04d Binary files /dev/null and b/public/images/emoji/apple/tangerine.png differ diff --git a/public/images/emoji/apple/taurus.png b/public/images/emoji/apple/taurus.png new file mode 100644 index 0000000000..bcef07381e Binary files /dev/null and b/public/images/emoji/apple/taurus.png differ diff --git a/public/images/emoji/apple/taxi.png b/public/images/emoji/apple/taxi.png new file mode 100644 index 0000000000..75bd300570 Binary files /dev/null and b/public/images/emoji/apple/taxi.png differ diff --git a/public/images/emoji/apple/tea.png b/public/images/emoji/apple/tea.png new file mode 100644 index 0000000000..871be1fb35 Binary files /dev/null and b/public/images/emoji/apple/tea.png differ diff --git a/public/images/emoji/apple/telephone.png b/public/images/emoji/apple/telephone.png new file mode 100644 index 0000000000..fb1d51cdbc Binary files /dev/null and b/public/images/emoji/apple/telephone.png differ diff --git a/public/images/emoji/apple/telephone_receiver.png b/public/images/emoji/apple/telephone_receiver.png new file mode 100644 index 0000000000..c1f0d40ddf Binary files /dev/null and b/public/images/emoji/apple/telephone_receiver.png differ diff --git a/public/images/emoji/apple/telescope.png b/public/images/emoji/apple/telescope.png new file mode 100644 index 0000000000..10998259a5 Binary files /dev/null and b/public/images/emoji/apple/telescope.png differ diff --git a/public/images/emoji/apple/tennis.png b/public/images/emoji/apple/tennis.png new file mode 100644 index 0000000000..b07a7f4d2f Binary files /dev/null and b/public/images/emoji/apple/tennis.png differ diff --git a/public/images/emoji/apple/tent.png b/public/images/emoji/apple/tent.png new file mode 100644 index 0000000000..c87edc4b40 Binary files /dev/null and b/public/images/emoji/apple/tent.png differ diff --git a/public/images/emoji/apple/thought_balloon.png b/public/images/emoji/apple/thought_balloon.png new file mode 100644 index 0000000000..3ddecff684 Binary files /dev/null and b/public/images/emoji/apple/thought_balloon.png differ diff --git a/public/images/emoji/apple/three.png b/public/images/emoji/apple/three.png new file mode 100644 index 0000000000..8a381d9ce9 Binary files /dev/null and b/public/images/emoji/apple/three.png differ diff --git a/public/images/emoji/apple/thumbsdown.png b/public/images/emoji/apple/thumbsdown.png new file mode 100644 index 0000000000..c7fb4c0649 Binary files /dev/null and b/public/images/emoji/apple/thumbsdown.png differ diff --git a/public/images/emoji/apple/thumbsup.png b/public/images/emoji/apple/thumbsup.png new file mode 100644 index 0000000000..e1d89aada6 Binary files /dev/null and b/public/images/emoji/apple/thumbsup.png differ diff --git a/public/images/emoji/apple/ticket.png b/public/images/emoji/apple/ticket.png new file mode 100644 index 0000000000..0b4cf7eff1 Binary files /dev/null and b/public/images/emoji/apple/ticket.png differ diff --git a/public/images/emoji/apple/tiger.png b/public/images/emoji/apple/tiger.png new file mode 100644 index 0000000000..570abedef5 Binary files /dev/null and b/public/images/emoji/apple/tiger.png differ diff --git a/public/images/emoji/apple/tiger2.png b/public/images/emoji/apple/tiger2.png new file mode 100644 index 0000000000..846aee7b5f Binary files /dev/null and b/public/images/emoji/apple/tiger2.png differ diff --git a/public/images/emoji/apple/tired_face.png b/public/images/emoji/apple/tired_face.png new file mode 100644 index 0000000000..1b7871819c Binary files /dev/null and b/public/images/emoji/apple/tired_face.png differ diff --git a/public/images/emoji/apple/tm.png b/public/images/emoji/apple/tm.png new file mode 100644 index 0000000000..569a83941b Binary files /dev/null and b/public/images/emoji/apple/tm.png differ diff --git a/public/images/emoji/apple/toilet.png b/public/images/emoji/apple/toilet.png new file mode 100644 index 0000000000..f3cf352434 Binary files /dev/null and b/public/images/emoji/apple/toilet.png differ diff --git a/public/images/emoji/apple/tokyo_tower.png b/public/images/emoji/apple/tokyo_tower.png new file mode 100644 index 0000000000..253dfd5aec Binary files /dev/null and b/public/images/emoji/apple/tokyo_tower.png differ diff --git a/public/images/emoji/apple/tomato.png b/public/images/emoji/apple/tomato.png new file mode 100644 index 0000000000..a50c40daf5 Binary files /dev/null and b/public/images/emoji/apple/tomato.png differ diff --git a/public/images/emoji/apple/tongue.png b/public/images/emoji/apple/tongue.png new file mode 100644 index 0000000000..e4b6c28674 Binary files /dev/null and b/public/images/emoji/apple/tongue.png differ diff --git a/public/images/emoji/apple/top.png b/public/images/emoji/apple/top.png new file mode 100644 index 0000000000..44bde1c3d2 Binary files /dev/null and b/public/images/emoji/apple/top.png differ diff --git a/public/images/emoji/apple/tophat.png b/public/images/emoji/apple/tophat.png new file mode 100644 index 0000000000..e6813d9b6f Binary files /dev/null and b/public/images/emoji/apple/tophat.png differ diff --git a/public/images/emoji/apple/tractor.png b/public/images/emoji/apple/tractor.png new file mode 100644 index 0000000000..5ffb549230 Binary files /dev/null and b/public/images/emoji/apple/tractor.png differ diff --git a/public/images/emoji/apple/traffic_light.png b/public/images/emoji/apple/traffic_light.png new file mode 100644 index 0000000000..e639866cfe Binary files /dev/null and b/public/images/emoji/apple/traffic_light.png differ diff --git a/public/images/emoji/apple/train.png b/public/images/emoji/apple/train.png new file mode 100644 index 0000000000..58720cf4ff Binary files /dev/null and b/public/images/emoji/apple/train.png differ diff --git a/public/images/emoji/apple/train2.png b/public/images/emoji/apple/train2.png new file mode 100644 index 0000000000..cdb6fdb7a1 Binary files /dev/null and b/public/images/emoji/apple/train2.png differ diff --git a/public/images/emoji/apple/tram.png b/public/images/emoji/apple/tram.png new file mode 100644 index 0000000000..17824fd52c Binary files /dev/null and b/public/images/emoji/apple/tram.png differ diff --git a/public/images/emoji/apple/triangular_flag_on_post.png b/public/images/emoji/apple/triangular_flag_on_post.png new file mode 100644 index 0000000000..d938209f4d Binary files /dev/null and b/public/images/emoji/apple/triangular_flag_on_post.png differ diff --git a/public/images/emoji/apple/triangular_ruler.png b/public/images/emoji/apple/triangular_ruler.png new file mode 100644 index 0000000000..b3cfa6d238 Binary files /dev/null and b/public/images/emoji/apple/triangular_ruler.png differ diff --git a/public/images/emoji/apple/trident.png b/public/images/emoji/apple/trident.png new file mode 100644 index 0000000000..1f6c82a316 Binary files /dev/null and b/public/images/emoji/apple/trident.png differ diff --git a/public/images/emoji/apple/triumph.png b/public/images/emoji/apple/triumph.png new file mode 100644 index 0000000000..da654216ce Binary files /dev/null and b/public/images/emoji/apple/triumph.png differ diff --git a/public/images/emoji/apple/trolleybus.png b/public/images/emoji/apple/trolleybus.png new file mode 100644 index 0000000000..7a7d20510a Binary files /dev/null and b/public/images/emoji/apple/trolleybus.png differ diff --git a/public/images/emoji/apple/trophy.png b/public/images/emoji/apple/trophy.png new file mode 100644 index 0000000000..8d9a91dbf1 Binary files /dev/null and b/public/images/emoji/apple/trophy.png differ diff --git a/public/images/emoji/apple/tropical_drink.png b/public/images/emoji/apple/tropical_drink.png new file mode 100644 index 0000000000..108950ed90 Binary files /dev/null and b/public/images/emoji/apple/tropical_drink.png differ diff --git a/public/images/emoji/apple/tropical_fish.png b/public/images/emoji/apple/tropical_fish.png new file mode 100644 index 0000000000..d6604096fa Binary files /dev/null and b/public/images/emoji/apple/tropical_fish.png differ diff --git a/public/images/emoji/apple/truck.png b/public/images/emoji/apple/truck.png new file mode 100644 index 0000000000..62aa3a7429 Binary files /dev/null and b/public/images/emoji/apple/truck.png differ diff --git a/public/images/emoji/apple/trumpet.png b/public/images/emoji/apple/trumpet.png new file mode 100644 index 0000000000..e623887d8d Binary files /dev/null and b/public/images/emoji/apple/trumpet.png differ diff --git a/public/images/emoji/apple/tshirt.png b/public/images/emoji/apple/tshirt.png new file mode 100644 index 0000000000..725a4a7a4b Binary files /dev/null and b/public/images/emoji/apple/tshirt.png differ diff --git a/public/images/emoji/apple/tulip.png b/public/images/emoji/apple/tulip.png new file mode 100644 index 0000000000..fe36a90530 Binary files /dev/null and b/public/images/emoji/apple/tulip.png differ diff --git a/public/images/emoji/apple/turtle.png b/public/images/emoji/apple/turtle.png new file mode 100644 index 0000000000..997a19b1bd Binary files /dev/null and b/public/images/emoji/apple/turtle.png differ diff --git a/public/images/emoji/apple/tv.png b/public/images/emoji/apple/tv.png new file mode 100644 index 0000000000..5bb841038b Binary files /dev/null and b/public/images/emoji/apple/tv.png differ diff --git a/public/images/emoji/apple/twisted_rightwards_arrows.png b/public/images/emoji/apple/twisted_rightwards_arrows.png new file mode 100644 index 0000000000..6c1df96d69 Binary files /dev/null and b/public/images/emoji/apple/twisted_rightwards_arrows.png differ diff --git a/public/images/emoji/apple/two.png b/public/images/emoji/apple/two.png new file mode 100644 index 0000000000..f7f745f927 Binary files /dev/null and b/public/images/emoji/apple/two.png differ diff --git a/public/images/emoji/apple/two_hearts.png b/public/images/emoji/apple/two_hearts.png new file mode 100644 index 0000000000..e3d18215c6 Binary files /dev/null and b/public/images/emoji/apple/two_hearts.png differ diff --git a/public/images/emoji/apple/two_men_holding_hands.png b/public/images/emoji/apple/two_men_holding_hands.png new file mode 100644 index 0000000000..1fe5d323e1 Binary files /dev/null and b/public/images/emoji/apple/two_men_holding_hands.png differ diff --git a/public/images/emoji/apple/two_women_holding_hands.png b/public/images/emoji/apple/two_women_holding_hands.png new file mode 100644 index 0000000000..7fc9f038a5 Binary files /dev/null and b/public/images/emoji/apple/two_women_holding_hands.png differ diff --git a/public/images/emoji/apple/u5272.png b/public/images/emoji/apple/u5272.png new file mode 100644 index 0000000000..619b23dad4 Binary files /dev/null and b/public/images/emoji/apple/u5272.png differ diff --git a/public/images/emoji/apple/u5408.png b/public/images/emoji/apple/u5408.png new file mode 100644 index 0000000000..408a32ba23 Binary files /dev/null and b/public/images/emoji/apple/u5408.png differ diff --git a/public/images/emoji/apple/u55b6.png b/public/images/emoji/apple/u55b6.png new file mode 100644 index 0000000000..0b25a8d6b9 Binary files /dev/null and b/public/images/emoji/apple/u55b6.png differ diff --git a/public/images/emoji/apple/u6307.png b/public/images/emoji/apple/u6307.png new file mode 100644 index 0000000000..acea4049f3 Binary files /dev/null and b/public/images/emoji/apple/u6307.png differ diff --git a/public/images/emoji/apple/u6708.png b/public/images/emoji/apple/u6708.png new file mode 100644 index 0000000000..9c96b1b7f4 Binary files /dev/null and b/public/images/emoji/apple/u6708.png differ diff --git a/public/images/emoji/apple/u6709.png b/public/images/emoji/apple/u6709.png new file mode 100644 index 0000000000..d792be751f Binary files /dev/null and b/public/images/emoji/apple/u6709.png differ diff --git a/public/images/emoji/apple/u6e80.png b/public/images/emoji/apple/u6e80.png new file mode 100644 index 0000000000..afe620cd4b Binary files /dev/null and b/public/images/emoji/apple/u6e80.png differ diff --git a/public/images/emoji/apple/u7121.png b/public/images/emoji/apple/u7121.png new file mode 100644 index 0000000000..5f6eacc455 Binary files /dev/null and b/public/images/emoji/apple/u7121.png differ diff --git a/public/images/emoji/apple/u7533.png b/public/images/emoji/apple/u7533.png new file mode 100644 index 0000000000..d2898eedc9 Binary files /dev/null and b/public/images/emoji/apple/u7533.png differ diff --git a/public/images/emoji/apple/u7981.png b/public/images/emoji/apple/u7981.png new file mode 100644 index 0000000000..daab0c2ebb Binary files /dev/null and b/public/images/emoji/apple/u7981.png differ diff --git a/public/images/emoji/apple/u7a7a.png b/public/images/emoji/apple/u7a7a.png new file mode 100644 index 0000000000..2b94c1804a Binary files /dev/null and b/public/images/emoji/apple/u7a7a.png differ diff --git a/public/images/emoji/apple/uk.png b/public/images/emoji/apple/uk.png new file mode 100644 index 0000000000..6baa662eb8 Binary files /dev/null and b/public/images/emoji/apple/uk.png differ diff --git a/public/images/emoji/apple/umbrella.png b/public/images/emoji/apple/umbrella.png new file mode 100644 index 0000000000..690e5f20bf Binary files /dev/null and b/public/images/emoji/apple/umbrella.png differ diff --git a/public/images/emoji/apple/unamused.png b/public/images/emoji/apple/unamused.png new file mode 100644 index 0000000000..9d4e1592e4 Binary files /dev/null and b/public/images/emoji/apple/unamused.png differ diff --git a/public/images/emoji/apple/underage.png b/public/images/emoji/apple/underage.png new file mode 100644 index 0000000000..79a3bdb4ef Binary files /dev/null and b/public/images/emoji/apple/underage.png differ diff --git a/public/images/emoji/apple/unlock.png b/public/images/emoji/apple/unlock.png new file mode 100644 index 0000000000..cc1e9a20f2 Binary files /dev/null and b/public/images/emoji/apple/unlock.png differ diff --git a/public/images/emoji/apple/up.png b/public/images/emoji/apple/up.png new file mode 100644 index 0000000000..18b903b3b9 Binary files /dev/null and b/public/images/emoji/apple/up.png differ diff --git a/public/images/emoji/apple/us.png b/public/images/emoji/apple/us.png new file mode 100644 index 0000000000..6c09be9b38 Binary files /dev/null and b/public/images/emoji/apple/us.png differ diff --git a/public/images/emoji/apple/v.png b/public/images/emoji/apple/v.png new file mode 100644 index 0000000000..03ce0681a5 Binary files /dev/null and b/public/images/emoji/apple/v.png differ diff --git a/public/images/emoji/apple/vertical_traffic_light.png b/public/images/emoji/apple/vertical_traffic_light.png new file mode 100644 index 0000000000..62b6f793ed Binary files /dev/null and b/public/images/emoji/apple/vertical_traffic_light.png differ diff --git a/public/images/emoji/apple/vhs.png b/public/images/emoji/apple/vhs.png new file mode 100644 index 0000000000..134989b4dd Binary files /dev/null and b/public/images/emoji/apple/vhs.png differ diff --git a/public/images/emoji/apple/vibration_mode.png b/public/images/emoji/apple/vibration_mode.png new file mode 100644 index 0000000000..c9966afd49 Binary files /dev/null and b/public/images/emoji/apple/vibration_mode.png differ diff --git a/public/images/emoji/apple/video_camera.png b/public/images/emoji/apple/video_camera.png new file mode 100644 index 0000000000..974872c306 Binary files /dev/null and b/public/images/emoji/apple/video_camera.png differ diff --git a/public/images/emoji/apple/video_game.png b/public/images/emoji/apple/video_game.png new file mode 100644 index 0000000000..0742cee842 Binary files /dev/null and b/public/images/emoji/apple/video_game.png differ diff --git a/public/images/emoji/apple/violin.png b/public/images/emoji/apple/violin.png new file mode 100644 index 0000000000..6a1cf6cdbd Binary files /dev/null and b/public/images/emoji/apple/violin.png differ diff --git a/public/images/emoji/apple/virgo.png b/public/images/emoji/apple/virgo.png new file mode 100644 index 0000000000..65e7514201 Binary files /dev/null and b/public/images/emoji/apple/virgo.png differ diff --git a/public/images/emoji/apple/volcano.png b/public/images/emoji/apple/volcano.png new file mode 100644 index 0000000000..2768967a9a Binary files /dev/null and b/public/images/emoji/apple/volcano.png differ diff --git a/public/images/emoji/apple/vs.png b/public/images/emoji/apple/vs.png new file mode 100644 index 0000000000..07ce4b5dbe Binary files /dev/null and b/public/images/emoji/apple/vs.png differ diff --git a/public/images/emoji/apple/walking.png b/public/images/emoji/apple/walking.png new file mode 100644 index 0000000000..25d9635e3e Binary files /dev/null and b/public/images/emoji/apple/walking.png differ diff --git a/public/images/emoji/apple/waning_crescent_moon.png b/public/images/emoji/apple/waning_crescent_moon.png new file mode 100644 index 0000000000..d6873a6653 Binary files /dev/null and b/public/images/emoji/apple/waning_crescent_moon.png differ diff --git a/public/images/emoji/apple/waning_gibbous_moon.png b/public/images/emoji/apple/waning_gibbous_moon.png new file mode 100644 index 0000000000..1ae88a5e92 Binary files /dev/null and b/public/images/emoji/apple/waning_gibbous_moon.png differ diff --git a/public/images/emoji/apple/warning.png b/public/images/emoji/apple/warning.png new file mode 100644 index 0000000000..9e51d2b78b Binary files /dev/null and b/public/images/emoji/apple/warning.png differ diff --git a/public/images/emoji/apple/watch.png b/public/images/emoji/apple/watch.png new file mode 100644 index 0000000000..a8e60eee74 Binary files /dev/null and b/public/images/emoji/apple/watch.png differ diff --git a/public/images/emoji/apple/water_buffalo.png b/public/images/emoji/apple/water_buffalo.png new file mode 100644 index 0000000000..0f67aff6b4 Binary files /dev/null and b/public/images/emoji/apple/water_buffalo.png differ diff --git a/public/images/emoji/apple/watermelon.png b/public/images/emoji/apple/watermelon.png new file mode 100644 index 0000000000..dd60d2de38 Binary files /dev/null and b/public/images/emoji/apple/watermelon.png differ diff --git a/public/images/emoji/apple/wave.png b/public/images/emoji/apple/wave.png new file mode 100644 index 0000000000..cb4a618f56 Binary files /dev/null and b/public/images/emoji/apple/wave.png differ diff --git a/public/images/emoji/apple/wavy_dash.png b/public/images/emoji/apple/wavy_dash.png new file mode 100644 index 0000000000..0908acbc3b Binary files /dev/null and b/public/images/emoji/apple/wavy_dash.png differ diff --git a/public/images/emoji/apple/waxing_crescent_moon.png b/public/images/emoji/apple/waxing_crescent_moon.png new file mode 100644 index 0000000000..11c43fba42 Binary files /dev/null and b/public/images/emoji/apple/waxing_crescent_moon.png differ diff --git a/public/images/emoji/apple/waxing_gibbous_moon.png b/public/images/emoji/apple/waxing_gibbous_moon.png new file mode 100644 index 0000000000..03e9b3daba Binary files /dev/null and b/public/images/emoji/apple/waxing_gibbous_moon.png differ diff --git a/public/images/emoji/apple/wc.png b/public/images/emoji/apple/wc.png new file mode 100644 index 0000000000..8ddf680c1d Binary files /dev/null and b/public/images/emoji/apple/wc.png differ diff --git a/public/images/emoji/apple/weary.png b/public/images/emoji/apple/weary.png new file mode 100644 index 0000000000..51f4cebb55 Binary files /dev/null and b/public/images/emoji/apple/weary.png differ diff --git a/public/images/emoji/apple/wedding.png b/public/images/emoji/apple/wedding.png new file mode 100644 index 0000000000..b104a68c34 Binary files /dev/null and b/public/images/emoji/apple/wedding.png differ diff --git a/public/images/emoji/apple/whale.png b/public/images/emoji/apple/whale.png new file mode 100644 index 0000000000..45ed0a7705 Binary files /dev/null and b/public/images/emoji/apple/whale.png differ diff --git a/public/images/emoji/apple/whale2.png b/public/images/emoji/apple/whale2.png new file mode 100644 index 0000000000..b25d44c209 Binary files /dev/null and b/public/images/emoji/apple/whale2.png differ diff --git a/public/images/emoji/apple/wheelchair.png b/public/images/emoji/apple/wheelchair.png new file mode 100644 index 0000000000..4091fd2747 Binary files /dev/null and b/public/images/emoji/apple/wheelchair.png differ diff --git a/public/images/emoji/apple/white_check_mark.png b/public/images/emoji/apple/white_check_mark.png new file mode 100644 index 0000000000..30d5b01ab0 Binary files /dev/null and b/public/images/emoji/apple/white_check_mark.png differ diff --git a/public/images/emoji/apple/white_circle.png b/public/images/emoji/apple/white_circle.png new file mode 100644 index 0000000000..ac1a3908e3 Binary files /dev/null and b/public/images/emoji/apple/white_circle.png differ diff --git a/public/images/emoji/apple/white_flower.png b/public/images/emoji/apple/white_flower.png new file mode 100644 index 0000000000..0687f0835e Binary files /dev/null and b/public/images/emoji/apple/white_flower.png differ diff --git a/public/images/emoji/apple/white_large_square.png b/public/images/emoji/apple/white_large_square.png new file mode 100644 index 0000000000..c9b6916a28 Binary files /dev/null and b/public/images/emoji/apple/white_large_square.png differ diff --git a/public/images/emoji/apple/white_medium_small_square.png b/public/images/emoji/apple/white_medium_small_square.png new file mode 100644 index 0000000000..ce959faecd Binary files /dev/null and b/public/images/emoji/apple/white_medium_small_square.png differ diff --git a/public/images/emoji/apple/white_medium_square.png b/public/images/emoji/apple/white_medium_square.png new file mode 100644 index 0000000000..11d576bd99 Binary files /dev/null and b/public/images/emoji/apple/white_medium_square.png differ diff --git a/public/images/emoji/apple/white_small_square.png b/public/images/emoji/apple/white_small_square.png new file mode 100644 index 0000000000..8f5b2fb366 Binary files /dev/null and b/public/images/emoji/apple/white_small_square.png differ diff --git a/public/images/emoji/apple/white_square_button.png b/public/images/emoji/apple/white_square_button.png new file mode 100644 index 0000000000..3500c14be4 Binary files /dev/null and b/public/images/emoji/apple/white_square_button.png differ diff --git a/public/images/emoji/apple/wind_chime.png b/public/images/emoji/apple/wind_chime.png new file mode 100644 index 0000000000..856f00ed0d Binary files /dev/null and b/public/images/emoji/apple/wind_chime.png differ diff --git a/public/images/emoji/apple/wine_glass.png b/public/images/emoji/apple/wine_glass.png new file mode 100644 index 0000000000..1b890898bd Binary files /dev/null and b/public/images/emoji/apple/wine_glass.png differ diff --git a/public/images/emoji/apple/wink.png b/public/images/emoji/apple/wink.png new file mode 100644 index 0000000000..1f092b9cf9 Binary files /dev/null and b/public/images/emoji/apple/wink.png differ diff --git a/public/images/emoji/apple/wolf.png b/public/images/emoji/apple/wolf.png new file mode 100644 index 0000000000..c7953eed91 Binary files /dev/null and b/public/images/emoji/apple/wolf.png differ diff --git a/public/images/emoji/apple/woman.png b/public/images/emoji/apple/woman.png new file mode 100644 index 0000000000..dc6434a673 Binary files /dev/null and b/public/images/emoji/apple/woman.png differ diff --git a/public/images/emoji/apple/womans_clothes.png b/public/images/emoji/apple/womans_clothes.png new file mode 100644 index 0000000000..f4250ecbe7 Binary files /dev/null and b/public/images/emoji/apple/womans_clothes.png differ diff --git a/public/images/emoji/apple/womans_hat.png b/public/images/emoji/apple/womans_hat.png new file mode 100644 index 0000000000..f5a1e8f747 Binary files /dev/null and b/public/images/emoji/apple/womans_hat.png differ diff --git a/public/images/emoji/apple/womens.png b/public/images/emoji/apple/womens.png new file mode 100644 index 0000000000..3843da3807 Binary files /dev/null and b/public/images/emoji/apple/womens.png differ diff --git a/public/images/emoji/apple/worried.png b/public/images/emoji/apple/worried.png new file mode 100644 index 0000000000..f5f1b598ed Binary files /dev/null and b/public/images/emoji/apple/worried.png differ diff --git a/public/images/emoji/apple/wrench.png b/public/images/emoji/apple/wrench.png new file mode 100644 index 0000000000..838d39f32d Binary files /dev/null and b/public/images/emoji/apple/wrench.png differ diff --git a/public/images/emoji/apple/x.png b/public/images/emoji/apple/x.png new file mode 100644 index 0000000000..14df4723aa Binary files /dev/null and b/public/images/emoji/apple/x.png differ diff --git a/public/images/emoji/apple/yellow_heart.png b/public/images/emoji/apple/yellow_heart.png new file mode 100644 index 0000000000..ed7f153cd7 Binary files /dev/null and b/public/images/emoji/apple/yellow_heart.png differ diff --git a/public/images/emoji/apple/yen.png b/public/images/emoji/apple/yen.png new file mode 100644 index 0000000000..c695a78ecd Binary files /dev/null and b/public/images/emoji/apple/yen.png differ diff --git a/public/images/emoji/apple/yum.png b/public/images/emoji/apple/yum.png new file mode 100644 index 0000000000..b7de2fe729 Binary files /dev/null and b/public/images/emoji/apple/yum.png differ diff --git a/public/images/emoji/apple/zap.png b/public/images/emoji/apple/zap.png new file mode 100644 index 0000000000..3b894b345d Binary files /dev/null and b/public/images/emoji/apple/zap.png differ diff --git a/public/images/emoji/apple/zero.png b/public/images/emoji/apple/zero.png new file mode 100644 index 0000000000..34445165de Binary files /dev/null and b/public/images/emoji/apple/zero.png differ diff --git a/public/images/emoji/apple/zzz.png b/public/images/emoji/apple/zzz.png new file mode 100644 index 0000000000..32bfe76cf8 Binary files /dev/null and b/public/images/emoji/apple/zzz.png differ diff --git a/plugins/emoji/public/images/unicode/1f44d.png b/public/images/emoji/emoji_one/+1.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f44d.png rename to public/images/emoji/emoji_one/+1.png diff --git a/public/images/emoji/emoji_one/-1.png b/public/images/emoji/emoji_one/-1.png new file mode 100644 index 0000000000..9b6d251b54 Binary files /dev/null and b/public/images/emoji/emoji_one/-1.png differ diff --git a/plugins/emoji/public/images/unicode/1f4af.png b/public/images/emoji/emoji_one/100.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f4af.png rename to public/images/emoji/emoji_one/100.png diff --git a/public/images/emoji/emoji_one/1234.png b/public/images/emoji/emoji_one/1234.png new file mode 100644 index 0000000000..af756e561f Binary files /dev/null and b/public/images/emoji/emoji_one/1234.png differ diff --git a/public/images/emoji/emoji_one/8ball.png b/public/images/emoji/emoji_one/8ball.png new file mode 100644 index 0000000000..f713ad2523 Binary files /dev/null and b/public/images/emoji/emoji_one/8ball.png differ diff --git a/public/images/emoji/emoji_one/a.png b/public/images/emoji/emoji_one/a.png new file mode 100644 index 0000000000..92e42f6310 Binary files /dev/null and b/public/images/emoji/emoji_one/a.png differ diff --git a/public/images/emoji/emoji_one/ab.png b/public/images/emoji/emoji_one/ab.png new file mode 100644 index 0000000000..b18b5d0597 Binary files /dev/null and b/public/images/emoji/emoji_one/ab.png differ diff --git a/public/images/emoji/emoji_one/abc.png b/public/images/emoji/emoji_one/abc.png new file mode 100644 index 0000000000..3834df5f54 Binary files /dev/null and b/public/images/emoji/emoji_one/abc.png differ diff --git a/public/images/emoji/emoji_one/abcd.png b/public/images/emoji/emoji_one/abcd.png new file mode 100644 index 0000000000..2af477d3b6 Binary files /dev/null and b/public/images/emoji/emoji_one/abcd.png differ diff --git a/public/images/emoji/emoji_one/accept.png b/public/images/emoji/emoji_one/accept.png new file mode 100644 index 0000000000..80ccc814ff Binary files /dev/null and b/public/images/emoji/emoji_one/accept.png differ diff --git a/plugins/emoji/public/images/unicode/1f6a1.png b/public/images/emoji/emoji_one/aerial_tramway.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f6a1.png rename to public/images/emoji/emoji_one/aerial_tramway.png diff --git a/public/images/emoji/emoji_one/airplane.png b/public/images/emoji/emoji_one/airplane.png new file mode 100644 index 0000000000..bf17867176 Binary files /dev/null and b/public/images/emoji/emoji_one/airplane.png differ diff --git a/public/images/emoji/emoji_one/alarm_clock.png b/public/images/emoji/emoji_one/alarm_clock.png new file mode 100644 index 0000000000..3f466f3a7b Binary files /dev/null and b/public/images/emoji/emoji_one/alarm_clock.png differ diff --git a/public/images/emoji/emoji_one/alien.png b/public/images/emoji/emoji_one/alien.png new file mode 100644 index 0000000000..b3c1e4d4bf Binary files /dev/null and b/public/images/emoji/emoji_one/alien.png differ diff --git a/public/images/emoji/emoji_one/ambulance.png b/public/images/emoji/emoji_one/ambulance.png new file mode 100644 index 0000000000..9df138d9cd Binary files /dev/null and b/public/images/emoji/emoji_one/ambulance.png differ diff --git a/public/images/emoji/emoji_one/anchor.png b/public/images/emoji/emoji_one/anchor.png new file mode 100644 index 0000000000..f2a8200f0f Binary files /dev/null and b/public/images/emoji/emoji_one/anchor.png differ diff --git a/public/images/emoji/emoji_one/angel.png b/public/images/emoji/emoji_one/angel.png new file mode 100644 index 0000000000..9a56a9e84d Binary files /dev/null and b/public/images/emoji/emoji_one/angel.png differ diff --git a/public/images/emoji/emoji_one/anger.png b/public/images/emoji/emoji_one/anger.png new file mode 100644 index 0000000000..bf623d9fc9 Binary files /dev/null and b/public/images/emoji/emoji_one/anger.png differ diff --git a/public/images/emoji/emoji_one/angry.png b/public/images/emoji/emoji_one/angry.png new file mode 100644 index 0000000000..a2e9201427 Binary files /dev/null and b/public/images/emoji/emoji_one/angry.png differ diff --git a/public/images/emoji/emoji_one/anguished.png b/public/images/emoji/emoji_one/anguished.png new file mode 100644 index 0000000000..08c558ef12 Binary files /dev/null and b/public/images/emoji/emoji_one/anguished.png differ diff --git a/public/images/emoji/emoji_one/ant.png b/public/images/emoji/emoji_one/ant.png new file mode 100644 index 0000000000..1b6ba80c0e Binary files /dev/null and b/public/images/emoji/emoji_one/ant.png differ diff --git a/public/images/emoji/emoji_one/apple.png b/public/images/emoji/emoji_one/apple.png new file mode 100644 index 0000000000..bcc5c94aa2 Binary files /dev/null and b/public/images/emoji/emoji_one/apple.png differ diff --git a/public/images/emoji/emoji_one/aquarius.png b/public/images/emoji/emoji_one/aquarius.png new file mode 100644 index 0000000000..d5d8380bef Binary files /dev/null and b/public/images/emoji/emoji_one/aquarius.png differ diff --git a/public/images/emoji/emoji_one/aries.png b/public/images/emoji/emoji_one/aries.png new file mode 100644 index 0000000000..3c63b0102d Binary files /dev/null and b/public/images/emoji/emoji_one/aries.png differ diff --git a/public/images/emoji/emoji_one/arrow_backward.png b/public/images/emoji/emoji_one/arrow_backward.png new file mode 100644 index 0000000000..afd5f1076c Binary files /dev/null and b/public/images/emoji/emoji_one/arrow_backward.png differ diff --git a/public/images/emoji/emoji_one/arrow_double_down.png b/public/images/emoji/emoji_one/arrow_double_down.png new file mode 100644 index 0000000000..5cc2a9faa2 Binary files /dev/null and b/public/images/emoji/emoji_one/arrow_double_down.png differ diff --git a/public/images/emoji/emoji_one/arrow_double_up.png b/public/images/emoji/emoji_one/arrow_double_up.png new file mode 100644 index 0000000000..897f028d63 Binary files /dev/null and b/public/images/emoji/emoji_one/arrow_double_up.png differ diff --git a/public/images/emoji/emoji_one/arrow_down.png b/public/images/emoji/emoji_one/arrow_down.png new file mode 100644 index 0000000000..dc030c6327 Binary files /dev/null and b/public/images/emoji/emoji_one/arrow_down.png differ diff --git a/public/images/emoji/emoji_one/arrow_down_small.png b/public/images/emoji/emoji_one/arrow_down_small.png new file mode 100644 index 0000000000..98ebcb06e1 Binary files /dev/null and b/public/images/emoji/emoji_one/arrow_down_small.png differ diff --git a/public/images/emoji/emoji_one/arrow_forward.png b/public/images/emoji/emoji_one/arrow_forward.png new file mode 100644 index 0000000000..22a50786e1 Binary files /dev/null and b/public/images/emoji/emoji_one/arrow_forward.png differ diff --git a/public/images/emoji/emoji_one/arrow_heading_down.png b/public/images/emoji/emoji_one/arrow_heading_down.png new file mode 100644 index 0000000000..bde867702f Binary files /dev/null and b/public/images/emoji/emoji_one/arrow_heading_down.png differ diff --git a/public/images/emoji/emoji_one/arrow_heading_up.png b/public/images/emoji/emoji_one/arrow_heading_up.png new file mode 100644 index 0000000000..11afe80d26 Binary files /dev/null and b/public/images/emoji/emoji_one/arrow_heading_up.png differ diff --git a/public/images/emoji/emoji_one/arrow_left.png b/public/images/emoji/emoji_one/arrow_left.png new file mode 100644 index 0000000000..5213afc79c Binary files /dev/null and b/public/images/emoji/emoji_one/arrow_left.png differ diff --git a/public/images/emoji/emoji_one/arrow_lower_left.png b/public/images/emoji/emoji_one/arrow_lower_left.png new file mode 100644 index 0000000000..25db5d971c Binary files /dev/null and b/public/images/emoji/emoji_one/arrow_lower_left.png differ diff --git a/public/images/emoji/emoji_one/arrow_lower_right.png b/public/images/emoji/emoji_one/arrow_lower_right.png new file mode 100644 index 0000000000..2ff483fed8 Binary files /dev/null and b/public/images/emoji/emoji_one/arrow_lower_right.png differ diff --git a/public/images/emoji/emoji_one/arrow_right.png b/public/images/emoji/emoji_one/arrow_right.png new file mode 100644 index 0000000000..6c5318defa Binary files /dev/null and b/public/images/emoji/emoji_one/arrow_right.png differ diff --git a/public/images/emoji/emoji_one/arrow_right_hook.png b/public/images/emoji/emoji_one/arrow_right_hook.png new file mode 100644 index 0000000000..ccf2a2183c Binary files /dev/null and b/public/images/emoji/emoji_one/arrow_right_hook.png differ diff --git a/public/images/emoji/emoji_one/arrow_up.png b/public/images/emoji/emoji_one/arrow_up.png new file mode 100644 index 0000000000..4220d45f1c Binary files /dev/null and b/public/images/emoji/emoji_one/arrow_up.png differ diff --git a/public/images/emoji/emoji_one/arrow_up_down.png b/public/images/emoji/emoji_one/arrow_up_down.png new file mode 100644 index 0000000000..a420d6ed38 Binary files /dev/null and b/public/images/emoji/emoji_one/arrow_up_down.png differ diff --git a/public/images/emoji/emoji_one/arrow_up_small.png b/public/images/emoji/emoji_one/arrow_up_small.png new file mode 100644 index 0000000000..37a768ed91 Binary files /dev/null and b/public/images/emoji/emoji_one/arrow_up_small.png differ diff --git a/public/images/emoji/emoji_one/arrow_upper_left.png b/public/images/emoji/emoji_one/arrow_upper_left.png new file mode 100644 index 0000000000..7425c8db1b Binary files /dev/null and b/public/images/emoji/emoji_one/arrow_upper_left.png differ diff --git a/public/images/emoji/emoji_one/arrow_upper_right.png b/public/images/emoji/emoji_one/arrow_upper_right.png new file mode 100644 index 0000000000..faccd5353e Binary files /dev/null and b/public/images/emoji/emoji_one/arrow_upper_right.png differ diff --git a/plugins/emoji/public/images/unicode/1f503.png b/public/images/emoji/emoji_one/arrows_clockwise.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f503.png rename to public/images/emoji/emoji_one/arrows_clockwise.png diff --git a/public/images/emoji/emoji_one/arrows_counterclockwise.png b/public/images/emoji/emoji_one/arrows_counterclockwise.png new file mode 100644 index 0000000000..f89672b417 Binary files /dev/null and b/public/images/emoji/emoji_one/arrows_counterclockwise.png differ diff --git a/public/images/emoji/emoji_one/art.png b/public/images/emoji/emoji_one/art.png new file mode 100644 index 0000000000..a9499e692b Binary files /dev/null and b/public/images/emoji/emoji_one/art.png differ diff --git a/public/images/emoji/emoji_one/articulated_lorry.png b/public/images/emoji/emoji_one/articulated_lorry.png new file mode 100644 index 0000000000..4447dcfb52 Binary files /dev/null and b/public/images/emoji/emoji_one/articulated_lorry.png differ diff --git a/public/images/emoji/emoji_one/astonished.png b/public/images/emoji/emoji_one/astonished.png new file mode 100644 index 0000000000..a1ffe4c28a Binary files /dev/null and b/public/images/emoji/emoji_one/astonished.png differ diff --git a/public/images/emoji/emoji_one/athletic_shoe.png b/public/images/emoji/emoji_one/athletic_shoe.png new file mode 100644 index 0000000000..e2cd2d3d46 Binary files /dev/null and b/public/images/emoji/emoji_one/athletic_shoe.png differ diff --git a/public/images/emoji/emoji_one/atm.png b/public/images/emoji/emoji_one/atm.png new file mode 100644 index 0000000000..904fef4683 Binary files /dev/null and b/public/images/emoji/emoji_one/atm.png differ diff --git a/public/images/emoji/emoji_one/b.png b/public/images/emoji/emoji_one/b.png new file mode 100644 index 0000000000..2b5bfa11ae Binary files /dev/null and b/public/images/emoji/emoji_one/b.png differ diff --git a/public/images/emoji/emoji_one/baby.png b/public/images/emoji/emoji_one/baby.png new file mode 100644 index 0000000000..077d76dce9 Binary files /dev/null and b/public/images/emoji/emoji_one/baby.png differ diff --git a/public/images/emoji/emoji_one/baby_bottle.png b/public/images/emoji/emoji_one/baby_bottle.png new file mode 100644 index 0000000000..64e9edee3f Binary files /dev/null and b/public/images/emoji/emoji_one/baby_bottle.png differ diff --git a/public/images/emoji/emoji_one/baby_chick.png b/public/images/emoji/emoji_one/baby_chick.png new file mode 100644 index 0000000000..66785c8801 Binary files /dev/null and b/public/images/emoji/emoji_one/baby_chick.png differ diff --git a/public/images/emoji/emoji_one/baby_symbol.png b/public/images/emoji/emoji_one/baby_symbol.png new file mode 100644 index 0000000000..7e9ea96300 Binary files /dev/null and b/public/images/emoji/emoji_one/baby_symbol.png differ diff --git a/public/images/emoji/emoji_one/back.png b/public/images/emoji/emoji_one/back.png new file mode 100644 index 0000000000..ee67604c74 Binary files /dev/null and b/public/images/emoji/emoji_one/back.png differ diff --git a/public/images/emoji/emoji_one/baggage_claim.png b/public/images/emoji/emoji_one/baggage_claim.png new file mode 100644 index 0000000000..a81efc8b84 Binary files /dev/null and b/public/images/emoji/emoji_one/baggage_claim.png differ diff --git a/public/images/emoji/emoji_one/balloon.png b/public/images/emoji/emoji_one/balloon.png new file mode 100644 index 0000000000..3cea35eba4 Binary files /dev/null and b/public/images/emoji/emoji_one/balloon.png differ diff --git a/public/images/emoji/emoji_one/ballot_box_with_check.png b/public/images/emoji/emoji_one/ballot_box_with_check.png new file mode 100644 index 0000000000..3a3763f492 Binary files /dev/null and b/public/images/emoji/emoji_one/ballot_box_with_check.png differ diff --git a/public/images/emoji/emoji_one/bamboo.png b/public/images/emoji/emoji_one/bamboo.png new file mode 100644 index 0000000000..a03cc8c4bf Binary files /dev/null and b/public/images/emoji/emoji_one/bamboo.png differ diff --git a/public/images/emoji/emoji_one/banana.png b/public/images/emoji/emoji_one/banana.png new file mode 100644 index 0000000000..5e68af7706 Binary files /dev/null and b/public/images/emoji/emoji_one/banana.png differ diff --git a/public/images/emoji/emoji_one/bangbang.png b/public/images/emoji/emoji_one/bangbang.png new file mode 100644 index 0000000000..1fd0186af2 Binary files /dev/null and b/public/images/emoji/emoji_one/bangbang.png differ diff --git a/public/images/emoji/emoji_one/bank.png b/public/images/emoji/emoji_one/bank.png new file mode 100644 index 0000000000..c324cc9696 Binary files /dev/null and b/public/images/emoji/emoji_one/bank.png differ diff --git a/plugins/emoji/public/images/unicode/1f4ca.png b/public/images/emoji/emoji_one/bar_chart.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f4ca.png rename to public/images/emoji/emoji_one/bar_chart.png diff --git a/plugins/emoji/public/images/unicode/1f488.png b/public/images/emoji/emoji_one/barber.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f488.png rename to public/images/emoji/emoji_one/barber.png diff --git a/public/images/emoji/emoji_one/baseball.png b/public/images/emoji/emoji_one/baseball.png new file mode 100644 index 0000000000..f4c8cdea45 Binary files /dev/null and b/public/images/emoji/emoji_one/baseball.png differ diff --git a/public/images/emoji/emoji_one/basketball.png b/public/images/emoji/emoji_one/basketball.png new file mode 100644 index 0000000000..d0b5a7186c Binary files /dev/null and b/public/images/emoji/emoji_one/basketball.png differ diff --git a/public/images/emoji/emoji_one/bath.png b/public/images/emoji/emoji_one/bath.png new file mode 100644 index 0000000000..49c81b5fcc Binary files /dev/null and b/public/images/emoji/emoji_one/bath.png differ diff --git a/public/images/emoji/emoji_one/bathtub.png b/public/images/emoji/emoji_one/bathtub.png new file mode 100644 index 0000000000..86b2a26296 Binary files /dev/null and b/public/images/emoji/emoji_one/bathtub.png differ diff --git a/public/images/emoji/emoji_one/battery.png b/public/images/emoji/emoji_one/battery.png new file mode 100644 index 0000000000..afbf11eef7 Binary files /dev/null and b/public/images/emoji/emoji_one/battery.png differ diff --git a/public/images/emoji/emoji_one/bear.png b/public/images/emoji/emoji_one/bear.png new file mode 100644 index 0000000000..32387186f0 Binary files /dev/null and b/public/images/emoji/emoji_one/bear.png differ diff --git a/public/images/emoji/emoji_one/bee.png b/public/images/emoji/emoji_one/bee.png new file mode 100644 index 0000000000..e67b7703bf Binary files /dev/null and b/public/images/emoji/emoji_one/bee.png differ diff --git a/public/images/emoji/emoji_one/beer.png b/public/images/emoji/emoji_one/beer.png new file mode 100644 index 0000000000..de0c3ddd9d Binary files /dev/null and b/public/images/emoji/emoji_one/beer.png differ diff --git a/public/images/emoji/emoji_one/beers.png b/public/images/emoji/emoji_one/beers.png new file mode 100644 index 0000000000..04ba4d9f46 Binary files /dev/null and b/public/images/emoji/emoji_one/beers.png differ diff --git a/public/images/emoji/emoji_one/beetle.png b/public/images/emoji/emoji_one/beetle.png new file mode 100644 index 0000000000..c35380926e Binary files /dev/null and b/public/images/emoji/emoji_one/beetle.png differ diff --git a/public/images/emoji/emoji_one/beginner.png b/public/images/emoji/emoji_one/beginner.png new file mode 100644 index 0000000000..1d9cb259b1 Binary files /dev/null and b/public/images/emoji/emoji_one/beginner.png differ diff --git a/public/images/emoji/emoji_one/bell.png b/public/images/emoji/emoji_one/bell.png new file mode 100644 index 0000000000..d1473557ea Binary files /dev/null and b/public/images/emoji/emoji_one/bell.png differ diff --git a/public/images/emoji/emoji_one/bento.png b/public/images/emoji/emoji_one/bento.png new file mode 100644 index 0000000000..c129f41222 Binary files /dev/null and b/public/images/emoji/emoji_one/bento.png differ diff --git a/public/images/emoji/emoji_one/bicyclist.png b/public/images/emoji/emoji_one/bicyclist.png new file mode 100644 index 0000000000..6f96b2c439 Binary files /dev/null and b/public/images/emoji/emoji_one/bicyclist.png differ diff --git a/public/images/emoji/emoji_one/bike.png b/public/images/emoji/emoji_one/bike.png new file mode 100644 index 0000000000..a37d602072 Binary files /dev/null and b/public/images/emoji/emoji_one/bike.png differ diff --git a/public/images/emoji/emoji_one/bikini.png b/public/images/emoji/emoji_one/bikini.png new file mode 100644 index 0000000000..62d142f311 Binary files /dev/null and b/public/images/emoji/emoji_one/bikini.png differ diff --git a/public/images/emoji/emoji_one/bird.png b/public/images/emoji/emoji_one/bird.png new file mode 100644 index 0000000000..077178a421 Binary files /dev/null and b/public/images/emoji/emoji_one/bird.png differ diff --git a/public/images/emoji/emoji_one/birthday.png b/public/images/emoji/emoji_one/birthday.png new file mode 100644 index 0000000000..c5253b0998 Binary files /dev/null and b/public/images/emoji/emoji_one/birthday.png differ diff --git a/public/images/emoji/emoji_one/black_circle.png b/public/images/emoji/emoji_one/black_circle.png new file mode 100644 index 0000000000..f1cd7189b5 Binary files /dev/null and b/public/images/emoji/emoji_one/black_circle.png differ diff --git a/public/images/emoji/emoji_one/black_joker.png b/public/images/emoji/emoji_one/black_joker.png new file mode 100644 index 0000000000..5c770fb278 Binary files /dev/null and b/public/images/emoji/emoji_one/black_joker.png differ diff --git a/plugins/emoji/public/images/unicode/2b1b.png b/public/images/emoji/emoji_one/black_large_square.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/2b1b.png rename to public/images/emoji/emoji_one/black_large_square.png diff --git a/plugins/emoji/public/images/unicode/25fe.png b/public/images/emoji/emoji_one/black_medium_small_square.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/25fe.png rename to public/images/emoji/emoji_one/black_medium_small_square.png diff --git a/plugins/emoji/public/images/unicode/25fc.png b/public/images/emoji/emoji_one/black_medium_square.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/25fc.png rename to public/images/emoji/emoji_one/black_medium_square.png diff --git a/public/images/emoji/emoji_one/black_nib.png b/public/images/emoji/emoji_one/black_nib.png new file mode 100644 index 0000000000..6f1a76cde5 Binary files /dev/null and b/public/images/emoji/emoji_one/black_nib.png differ diff --git a/public/images/emoji/emoji_one/black_small_square.png b/public/images/emoji/emoji_one/black_small_square.png new file mode 100644 index 0000000000..1c5a354d56 Binary files /dev/null and b/public/images/emoji/emoji_one/black_small_square.png differ diff --git a/public/images/emoji/emoji_one/black_square_button.png b/public/images/emoji/emoji_one/black_square_button.png new file mode 100644 index 0000000000..dfad369f53 Binary files /dev/null and b/public/images/emoji/emoji_one/black_square_button.png differ diff --git a/public/images/emoji/emoji_one/blossom.png b/public/images/emoji/emoji_one/blossom.png new file mode 100644 index 0000000000..ffc25f2523 Binary files /dev/null and b/public/images/emoji/emoji_one/blossom.png differ diff --git a/public/images/emoji/emoji_one/blowfish.png b/public/images/emoji/emoji_one/blowfish.png new file mode 100644 index 0000000000..7529feff75 Binary files /dev/null and b/public/images/emoji/emoji_one/blowfish.png differ diff --git a/public/images/emoji/emoji_one/blue_book.png b/public/images/emoji/emoji_one/blue_book.png new file mode 100644 index 0000000000..bf13d1cd61 Binary files /dev/null and b/public/images/emoji/emoji_one/blue_book.png differ diff --git a/public/images/emoji/emoji_one/blue_car.png b/public/images/emoji/emoji_one/blue_car.png new file mode 100644 index 0000000000..0c2e833fff Binary files /dev/null and b/public/images/emoji/emoji_one/blue_car.png differ diff --git a/plugins/emoji/public/images/unicode/1f499.png b/public/images/emoji/emoji_one/blue_heart.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f499.png rename to public/images/emoji/emoji_one/blue_heart.png diff --git a/public/images/emoji/emoji_one/blush.png b/public/images/emoji/emoji_one/blush.png new file mode 100644 index 0000000000..5aecc2948c Binary files /dev/null and b/public/images/emoji/emoji_one/blush.png differ diff --git a/public/images/emoji/emoji_one/boar.png b/public/images/emoji/emoji_one/boar.png new file mode 100644 index 0000000000..2ccb92fd15 Binary files /dev/null and b/public/images/emoji/emoji_one/boar.png differ diff --git a/public/images/emoji/emoji_one/boat.png b/public/images/emoji/emoji_one/boat.png new file mode 100644 index 0000000000..9e862f831f Binary files /dev/null and b/public/images/emoji/emoji_one/boat.png differ diff --git a/public/images/emoji/emoji_one/bomb.png b/public/images/emoji/emoji_one/bomb.png new file mode 100644 index 0000000000..774c5c91e0 Binary files /dev/null and b/public/images/emoji/emoji_one/bomb.png differ diff --git a/public/images/emoji/emoji_one/book.png b/public/images/emoji/emoji_one/book.png new file mode 100644 index 0000000000..158ba4965d Binary files /dev/null and b/public/images/emoji/emoji_one/book.png differ diff --git a/public/images/emoji/emoji_one/bookmark.png b/public/images/emoji/emoji_one/bookmark.png new file mode 100644 index 0000000000..5ccb5ed0be Binary files /dev/null and b/public/images/emoji/emoji_one/bookmark.png differ diff --git a/public/images/emoji/emoji_one/bookmark_tabs.png b/public/images/emoji/emoji_one/bookmark_tabs.png new file mode 100644 index 0000000000..b6532cda52 Binary files /dev/null and b/public/images/emoji/emoji_one/bookmark_tabs.png differ diff --git a/public/images/emoji/emoji_one/books.png b/public/images/emoji/emoji_one/books.png new file mode 100644 index 0000000000..f69bfad763 Binary files /dev/null and b/public/images/emoji/emoji_one/books.png differ diff --git a/public/images/emoji/emoji_one/boom.png b/public/images/emoji/emoji_one/boom.png new file mode 100644 index 0000000000..4480b19814 Binary files /dev/null and b/public/images/emoji/emoji_one/boom.png differ diff --git a/public/images/emoji/emoji_one/boot.png b/public/images/emoji/emoji_one/boot.png new file mode 100644 index 0000000000..856256d5be Binary files /dev/null and b/public/images/emoji/emoji_one/boot.png differ diff --git a/public/images/emoji/emoji_one/bouquet.png b/public/images/emoji/emoji_one/bouquet.png new file mode 100644 index 0000000000..e42118f7ff Binary files /dev/null and b/public/images/emoji/emoji_one/bouquet.png differ diff --git a/public/images/emoji/emoji_one/bow.png b/public/images/emoji/emoji_one/bow.png new file mode 100644 index 0000000000..1674427e90 Binary files /dev/null and b/public/images/emoji/emoji_one/bow.png differ diff --git a/public/images/emoji/emoji_one/bowling.png b/public/images/emoji/emoji_one/bowling.png new file mode 100644 index 0000000000..e41edfbdbe Binary files /dev/null and b/public/images/emoji/emoji_one/bowling.png differ diff --git a/public/images/emoji/emoji_one/boy.png b/public/images/emoji/emoji_one/boy.png new file mode 100644 index 0000000000..0bc4897220 Binary files /dev/null and b/public/images/emoji/emoji_one/boy.png differ diff --git a/public/images/emoji/emoji_one/bread.png b/public/images/emoji/emoji_one/bread.png new file mode 100644 index 0000000000..3ca73ebc78 Binary files /dev/null and b/public/images/emoji/emoji_one/bread.png differ diff --git a/public/images/emoji/emoji_one/bride_with_veil.png b/public/images/emoji/emoji_one/bride_with_veil.png new file mode 100644 index 0000000000..9ec5867e10 Binary files /dev/null and b/public/images/emoji/emoji_one/bride_with_veil.png differ diff --git a/public/images/emoji/emoji_one/bridge_at_night.png b/public/images/emoji/emoji_one/bridge_at_night.png new file mode 100644 index 0000000000..32d4a53c7d Binary files /dev/null and b/public/images/emoji/emoji_one/bridge_at_night.png differ diff --git a/public/images/emoji/emoji_one/briefcase.png b/public/images/emoji/emoji_one/briefcase.png new file mode 100644 index 0000000000..a7df5ea5aa Binary files /dev/null and b/public/images/emoji/emoji_one/briefcase.png differ diff --git a/plugins/emoji/public/images/unicode/1f494.png b/public/images/emoji/emoji_one/broken_heart.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f494.png rename to public/images/emoji/emoji_one/broken_heart.png diff --git a/public/images/emoji/emoji_one/bug.png b/public/images/emoji/emoji_one/bug.png new file mode 100644 index 0000000000..06b35cf5a8 Binary files /dev/null and b/public/images/emoji/emoji_one/bug.png differ diff --git a/public/images/emoji/emoji_one/bulb.png b/public/images/emoji/emoji_one/bulb.png new file mode 100644 index 0000000000..7596c82a70 Binary files /dev/null and b/public/images/emoji/emoji_one/bulb.png differ diff --git a/public/images/emoji/emoji_one/bullettrain_front.png b/public/images/emoji/emoji_one/bullettrain_front.png new file mode 100644 index 0000000000..4d35e5e4be Binary files /dev/null and b/public/images/emoji/emoji_one/bullettrain_front.png differ diff --git a/public/images/emoji/emoji_one/bullettrain_side.png b/public/images/emoji/emoji_one/bullettrain_side.png new file mode 100644 index 0000000000..af64d86f05 Binary files /dev/null and b/public/images/emoji/emoji_one/bullettrain_side.png differ diff --git a/public/images/emoji/emoji_one/bus.png b/public/images/emoji/emoji_one/bus.png new file mode 100644 index 0000000000..1457a84df3 Binary files /dev/null and b/public/images/emoji/emoji_one/bus.png differ diff --git a/public/images/emoji/emoji_one/busstop.png b/public/images/emoji/emoji_one/busstop.png new file mode 100644 index 0000000000..3e14a30b68 Binary files /dev/null and b/public/images/emoji/emoji_one/busstop.png differ diff --git a/plugins/emoji/public/images/unicode/1f464.png b/public/images/emoji/emoji_one/bust_in_silhouette.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f464.png rename to public/images/emoji/emoji_one/bust_in_silhouette.png diff --git a/public/images/emoji/emoji_one/busts_in_silhouette.png b/public/images/emoji/emoji_one/busts_in_silhouette.png new file mode 100644 index 0000000000..24751b2e23 Binary files /dev/null and b/public/images/emoji/emoji_one/busts_in_silhouette.png differ diff --git a/public/images/emoji/emoji_one/cactus.png b/public/images/emoji/emoji_one/cactus.png new file mode 100644 index 0000000000..0204fd3436 Binary files /dev/null and b/public/images/emoji/emoji_one/cactus.png differ diff --git a/public/images/emoji/emoji_one/cake.png b/public/images/emoji/emoji_one/cake.png new file mode 100644 index 0000000000..0be915e5bd Binary files /dev/null and b/public/images/emoji/emoji_one/cake.png differ diff --git a/public/images/emoji/emoji_one/calendar.png b/public/images/emoji/emoji_one/calendar.png new file mode 100644 index 0000000000..2f0fb41c4d Binary files /dev/null and b/public/images/emoji/emoji_one/calendar.png differ diff --git a/public/images/emoji/emoji_one/calling.png b/public/images/emoji/emoji_one/calling.png new file mode 100644 index 0000000000..5544405c10 Binary files /dev/null and b/public/images/emoji/emoji_one/calling.png differ diff --git a/public/images/emoji/emoji_one/camel.png b/public/images/emoji/emoji_one/camel.png new file mode 100644 index 0000000000..f332bf3335 Binary files /dev/null and b/public/images/emoji/emoji_one/camel.png differ diff --git a/public/images/emoji/emoji_one/camera.png b/public/images/emoji/emoji_one/camera.png new file mode 100644 index 0000000000..7f8c588baf Binary files /dev/null and b/public/images/emoji/emoji_one/camera.png differ diff --git a/public/images/emoji/emoji_one/cancer.png b/public/images/emoji/emoji_one/cancer.png new file mode 100644 index 0000000000..6e6d2ca75a Binary files /dev/null and b/public/images/emoji/emoji_one/cancer.png differ diff --git a/public/images/emoji/emoji_one/candy.png b/public/images/emoji/emoji_one/candy.png new file mode 100644 index 0000000000..c067807c11 Binary files /dev/null and b/public/images/emoji/emoji_one/candy.png differ diff --git a/public/images/emoji/emoji_one/capital_abcd.png b/public/images/emoji/emoji_one/capital_abcd.png new file mode 100644 index 0000000000..53243beb33 Binary files /dev/null and b/public/images/emoji/emoji_one/capital_abcd.png differ diff --git a/public/images/emoji/emoji_one/capricorn.png b/public/images/emoji/emoji_one/capricorn.png new file mode 100644 index 0000000000..a814e7f843 Binary files /dev/null and b/public/images/emoji/emoji_one/capricorn.png differ diff --git a/public/images/emoji/emoji_one/car.png b/public/images/emoji/emoji_one/car.png new file mode 100644 index 0000000000..e266bd21e5 Binary files /dev/null and b/public/images/emoji/emoji_one/car.png differ diff --git a/public/images/emoji/emoji_one/card_index.png b/public/images/emoji/emoji_one/card_index.png new file mode 100644 index 0000000000..daafd9917f Binary files /dev/null and b/public/images/emoji/emoji_one/card_index.png differ diff --git a/public/images/emoji/emoji_one/carousel_horse.png b/public/images/emoji/emoji_one/carousel_horse.png new file mode 100644 index 0000000000..9c735112aa Binary files /dev/null and b/public/images/emoji/emoji_one/carousel_horse.png differ diff --git a/public/images/emoji/emoji_one/cat.png b/public/images/emoji/emoji_one/cat.png new file mode 100644 index 0000000000..126d2ef79d Binary files /dev/null and b/public/images/emoji/emoji_one/cat.png differ diff --git a/public/images/emoji/emoji_one/cat2.png b/public/images/emoji/emoji_one/cat2.png new file mode 100644 index 0000000000..79f06af483 Binary files /dev/null and b/public/images/emoji/emoji_one/cat2.png differ diff --git a/public/images/emoji/emoji_one/cd.png b/public/images/emoji/emoji_one/cd.png new file mode 100644 index 0000000000..2998423964 Binary files /dev/null and b/public/images/emoji/emoji_one/cd.png differ diff --git a/public/images/emoji/emoji_one/chart.png b/public/images/emoji/emoji_one/chart.png new file mode 100644 index 0000000000..4a31138f55 Binary files /dev/null and b/public/images/emoji/emoji_one/chart.png differ diff --git a/public/images/emoji/emoji_one/chart_with_downwards_trend.png b/public/images/emoji/emoji_one/chart_with_downwards_trend.png new file mode 100644 index 0000000000..2c1d69e7c3 Binary files /dev/null and b/public/images/emoji/emoji_one/chart_with_downwards_trend.png differ diff --git a/public/images/emoji/emoji_one/chart_with_upwards_trend.png b/public/images/emoji/emoji_one/chart_with_upwards_trend.png new file mode 100644 index 0000000000..20293a17cb Binary files /dev/null and b/public/images/emoji/emoji_one/chart_with_upwards_trend.png differ diff --git a/public/images/emoji/emoji_one/checkered_flag.png b/public/images/emoji/emoji_one/checkered_flag.png new file mode 100644 index 0000000000..9f1d564f66 Binary files /dev/null and b/public/images/emoji/emoji_one/checkered_flag.png differ diff --git a/public/images/emoji/emoji_one/cherries.png b/public/images/emoji/emoji_one/cherries.png new file mode 100644 index 0000000000..982292e959 Binary files /dev/null and b/public/images/emoji/emoji_one/cherries.png differ diff --git a/public/images/emoji/emoji_one/cherry_blossom.png b/public/images/emoji/emoji_one/cherry_blossom.png new file mode 100644 index 0000000000..7cd8a22e78 Binary files /dev/null and b/public/images/emoji/emoji_one/cherry_blossom.png differ diff --git a/public/images/emoji/emoji_one/chestnut.png b/public/images/emoji/emoji_one/chestnut.png new file mode 100644 index 0000000000..e850361510 Binary files /dev/null and b/public/images/emoji/emoji_one/chestnut.png differ diff --git a/public/images/emoji/emoji_one/chicken.png b/public/images/emoji/emoji_one/chicken.png new file mode 100644 index 0000000000..182ffb343f Binary files /dev/null and b/public/images/emoji/emoji_one/chicken.png differ diff --git a/public/images/emoji/emoji_one/children_crossing.png b/public/images/emoji/emoji_one/children_crossing.png new file mode 100644 index 0000000000..4bc75ab7bd Binary files /dev/null and b/public/images/emoji/emoji_one/children_crossing.png differ diff --git a/public/images/emoji/emoji_one/chocolate_bar.png b/public/images/emoji/emoji_one/chocolate_bar.png new file mode 100644 index 0000000000..f76d0902ed Binary files /dev/null and b/public/images/emoji/emoji_one/chocolate_bar.png differ diff --git a/public/images/emoji/emoji_one/christmas_tree.png b/public/images/emoji/emoji_one/christmas_tree.png new file mode 100644 index 0000000000..da5fdf85a3 Binary files /dev/null and b/public/images/emoji/emoji_one/christmas_tree.png differ diff --git a/public/images/emoji/emoji_one/church.png b/public/images/emoji/emoji_one/church.png new file mode 100644 index 0000000000..e1f25e25a7 Binary files /dev/null and b/public/images/emoji/emoji_one/church.png differ diff --git a/public/images/emoji/emoji_one/cinema.png b/public/images/emoji/emoji_one/cinema.png new file mode 100644 index 0000000000..768f1991ab Binary files /dev/null and b/public/images/emoji/emoji_one/cinema.png differ diff --git a/public/images/emoji/emoji_one/circus_tent.png b/public/images/emoji/emoji_one/circus_tent.png new file mode 100644 index 0000000000..713b4f085d Binary files /dev/null and b/public/images/emoji/emoji_one/circus_tent.png differ diff --git a/public/images/emoji/emoji_one/city_sunrise.png b/public/images/emoji/emoji_one/city_sunrise.png new file mode 100644 index 0000000000..1e05afbb2f Binary files /dev/null and b/public/images/emoji/emoji_one/city_sunrise.png differ diff --git a/public/images/emoji/emoji_one/city_sunset.png b/public/images/emoji/emoji_one/city_sunset.png new file mode 100644 index 0000000000..ed0694d442 Binary files /dev/null and b/public/images/emoji/emoji_one/city_sunset.png differ diff --git a/public/images/emoji/emoji_one/cl.png b/public/images/emoji/emoji_one/cl.png new file mode 100644 index 0000000000..cf6682ee4b Binary files /dev/null and b/public/images/emoji/emoji_one/cl.png differ diff --git a/public/images/emoji/emoji_one/clap.png b/public/images/emoji/emoji_one/clap.png new file mode 100644 index 0000000000..ccb725e96c Binary files /dev/null and b/public/images/emoji/emoji_one/clap.png differ diff --git a/public/images/emoji/emoji_one/clapper.png b/public/images/emoji/emoji_one/clapper.png new file mode 100644 index 0000000000..54bbba8098 Binary files /dev/null and b/public/images/emoji/emoji_one/clapper.png differ diff --git a/public/images/emoji/emoji_one/clipboard.png b/public/images/emoji/emoji_one/clipboard.png new file mode 100644 index 0000000000..6120c84dc6 Binary files /dev/null and b/public/images/emoji/emoji_one/clipboard.png differ diff --git a/public/images/emoji/emoji_one/clock1.png b/public/images/emoji/emoji_one/clock1.png new file mode 100644 index 0000000000..520c461e9e Binary files /dev/null and b/public/images/emoji/emoji_one/clock1.png differ diff --git a/public/images/emoji/emoji_one/clock10.png b/public/images/emoji/emoji_one/clock10.png new file mode 100644 index 0000000000..895ab56756 Binary files /dev/null and b/public/images/emoji/emoji_one/clock10.png differ diff --git a/public/images/emoji/emoji_one/clock1030.png b/public/images/emoji/emoji_one/clock1030.png new file mode 100644 index 0000000000..0e4c55319e Binary files /dev/null and b/public/images/emoji/emoji_one/clock1030.png differ diff --git a/public/images/emoji/emoji_one/clock11.png b/public/images/emoji/emoji_one/clock11.png new file mode 100644 index 0000000000..692c60bf4f Binary files /dev/null and b/public/images/emoji/emoji_one/clock11.png differ diff --git a/public/images/emoji/emoji_one/clock1130.png b/public/images/emoji/emoji_one/clock1130.png new file mode 100644 index 0000000000..8ae2bb3632 Binary files /dev/null and b/public/images/emoji/emoji_one/clock1130.png differ diff --git a/public/images/emoji/emoji_one/clock12.png b/public/images/emoji/emoji_one/clock12.png new file mode 100644 index 0000000000..45407cede5 Binary files /dev/null and b/public/images/emoji/emoji_one/clock12.png differ diff --git a/public/images/emoji/emoji_one/clock1230.png b/public/images/emoji/emoji_one/clock1230.png new file mode 100644 index 0000000000..fce2ad6195 Binary files /dev/null and b/public/images/emoji/emoji_one/clock1230.png differ diff --git a/public/images/emoji/emoji_one/clock130.png b/public/images/emoji/emoji_one/clock130.png new file mode 100644 index 0000000000..d0ae7cb16c Binary files /dev/null and b/public/images/emoji/emoji_one/clock130.png differ diff --git a/public/images/emoji/emoji_one/clock2.png b/public/images/emoji/emoji_one/clock2.png new file mode 100644 index 0000000000..e8f5884edc Binary files /dev/null and b/public/images/emoji/emoji_one/clock2.png differ diff --git a/public/images/emoji/emoji_one/clock230.png b/public/images/emoji/emoji_one/clock230.png new file mode 100644 index 0000000000..05217180ae Binary files /dev/null and b/public/images/emoji/emoji_one/clock230.png differ diff --git a/public/images/emoji/emoji_one/clock3.png b/public/images/emoji/emoji_one/clock3.png new file mode 100644 index 0000000000..a124fedefc Binary files /dev/null and b/public/images/emoji/emoji_one/clock3.png differ diff --git a/public/images/emoji/emoji_one/clock330.png b/public/images/emoji/emoji_one/clock330.png new file mode 100644 index 0000000000..50a9a6cc0d Binary files /dev/null and b/public/images/emoji/emoji_one/clock330.png differ diff --git a/public/images/emoji/emoji_one/clock4.png b/public/images/emoji/emoji_one/clock4.png new file mode 100644 index 0000000000..59d5af5291 Binary files /dev/null and b/public/images/emoji/emoji_one/clock4.png differ diff --git a/public/images/emoji/emoji_one/clock430.png b/public/images/emoji/emoji_one/clock430.png new file mode 100644 index 0000000000..904266c85b Binary files /dev/null and b/public/images/emoji/emoji_one/clock430.png differ diff --git a/public/images/emoji/emoji_one/clock5.png b/public/images/emoji/emoji_one/clock5.png new file mode 100644 index 0000000000..dea07610e3 Binary files /dev/null and b/public/images/emoji/emoji_one/clock5.png differ diff --git a/public/images/emoji/emoji_one/clock530.png b/public/images/emoji/emoji_one/clock530.png new file mode 100644 index 0000000000..a278a9fb0b Binary files /dev/null and b/public/images/emoji/emoji_one/clock530.png differ diff --git a/public/images/emoji/emoji_one/clock6.png b/public/images/emoji/emoji_one/clock6.png new file mode 100644 index 0000000000..e978093553 Binary files /dev/null and b/public/images/emoji/emoji_one/clock6.png differ diff --git a/public/images/emoji/emoji_one/clock630.png b/public/images/emoji/emoji_one/clock630.png new file mode 100644 index 0000000000..a1f98a9f33 Binary files /dev/null and b/public/images/emoji/emoji_one/clock630.png differ diff --git a/public/images/emoji/emoji_one/clock7.png b/public/images/emoji/emoji_one/clock7.png new file mode 100644 index 0000000000..93ef6c92c0 Binary files /dev/null and b/public/images/emoji/emoji_one/clock7.png differ diff --git a/public/images/emoji/emoji_one/clock730.png b/public/images/emoji/emoji_one/clock730.png new file mode 100644 index 0000000000..373bac8b5d Binary files /dev/null and b/public/images/emoji/emoji_one/clock730.png differ diff --git a/public/images/emoji/emoji_one/clock8.png b/public/images/emoji/emoji_one/clock8.png new file mode 100644 index 0000000000..4faacac336 Binary files /dev/null and b/public/images/emoji/emoji_one/clock8.png differ diff --git a/public/images/emoji/emoji_one/clock830.png b/public/images/emoji/emoji_one/clock830.png new file mode 100644 index 0000000000..e33946fc90 Binary files /dev/null and b/public/images/emoji/emoji_one/clock830.png differ diff --git a/public/images/emoji/emoji_one/clock9.png b/public/images/emoji/emoji_one/clock9.png new file mode 100644 index 0000000000..ee94562b06 Binary files /dev/null and b/public/images/emoji/emoji_one/clock9.png differ diff --git a/public/images/emoji/emoji_one/clock930.png b/public/images/emoji/emoji_one/clock930.png new file mode 100644 index 0000000000..9934e99ee7 Binary files /dev/null and b/public/images/emoji/emoji_one/clock930.png differ diff --git a/public/images/emoji/emoji_one/closed_book.png b/public/images/emoji/emoji_one/closed_book.png new file mode 100644 index 0000000000..63912b3f5b Binary files /dev/null and b/public/images/emoji/emoji_one/closed_book.png differ diff --git a/public/images/emoji/emoji_one/closed_lock_with_key.png b/public/images/emoji/emoji_one/closed_lock_with_key.png new file mode 100644 index 0000000000..bdffbda870 Binary files /dev/null and b/public/images/emoji/emoji_one/closed_lock_with_key.png differ diff --git a/public/images/emoji/emoji_one/closed_umbrella.png b/public/images/emoji/emoji_one/closed_umbrella.png new file mode 100644 index 0000000000..00afbfb409 Binary files /dev/null and b/public/images/emoji/emoji_one/closed_umbrella.png differ diff --git a/public/images/emoji/emoji_one/cloud.png b/public/images/emoji/emoji_one/cloud.png new file mode 100644 index 0000000000..559c52f760 Binary files /dev/null and b/public/images/emoji/emoji_one/cloud.png differ diff --git a/plugins/emoji/public/images/unicode/2663.png b/public/images/emoji/emoji_one/clubs.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/2663.png rename to public/images/emoji/emoji_one/clubs.png diff --git a/public/images/emoji/emoji_one/cn.png b/public/images/emoji/emoji_one/cn.png new file mode 100644 index 0000000000..0151923f35 Binary files /dev/null and b/public/images/emoji/emoji_one/cn.png differ diff --git a/public/images/emoji/emoji_one/cocktail.png b/public/images/emoji/emoji_one/cocktail.png new file mode 100644 index 0000000000..d03b174fa8 Binary files /dev/null and b/public/images/emoji/emoji_one/cocktail.png differ diff --git a/public/images/emoji/emoji_one/coffee.png b/public/images/emoji/emoji_one/coffee.png new file mode 100644 index 0000000000..6f5a85282a Binary files /dev/null and b/public/images/emoji/emoji_one/coffee.png differ diff --git a/public/images/emoji/emoji_one/cold_sweat.png b/public/images/emoji/emoji_one/cold_sweat.png new file mode 100644 index 0000000000..286f494a35 Binary files /dev/null and b/public/images/emoji/emoji_one/cold_sweat.png differ diff --git a/public/images/emoji/emoji_one/collision.png b/public/images/emoji/emoji_one/collision.png new file mode 100644 index 0000000000..4480b19814 Binary files /dev/null and b/public/images/emoji/emoji_one/collision.png differ diff --git a/public/images/emoji/emoji_one/computer.png b/public/images/emoji/emoji_one/computer.png new file mode 100644 index 0000000000..24ae29886e Binary files /dev/null and b/public/images/emoji/emoji_one/computer.png differ diff --git a/public/images/emoji/emoji_one/confetti_ball.png b/public/images/emoji/emoji_one/confetti_ball.png new file mode 100644 index 0000000000..74c61e4344 Binary files /dev/null and b/public/images/emoji/emoji_one/confetti_ball.png differ diff --git a/public/images/emoji/emoji_one/confounded.png b/public/images/emoji/emoji_one/confounded.png new file mode 100644 index 0000000000..d64e2a534d Binary files /dev/null and b/public/images/emoji/emoji_one/confounded.png differ diff --git a/public/images/emoji/emoji_one/confused.png b/public/images/emoji/emoji_one/confused.png new file mode 100644 index 0000000000..d3a23eb8bb Binary files /dev/null and b/public/images/emoji/emoji_one/confused.png differ diff --git a/public/images/emoji/emoji_one/congratulations.png b/public/images/emoji/emoji_one/congratulations.png new file mode 100644 index 0000000000..687b65ce5c Binary files /dev/null and b/public/images/emoji/emoji_one/congratulations.png differ diff --git a/public/images/emoji/emoji_one/construction.png b/public/images/emoji/emoji_one/construction.png new file mode 100644 index 0000000000..917317b462 Binary files /dev/null and b/public/images/emoji/emoji_one/construction.png differ diff --git a/public/images/emoji/emoji_one/construction_worker.png b/public/images/emoji/emoji_one/construction_worker.png new file mode 100644 index 0000000000..f772004ca5 Binary files /dev/null and b/public/images/emoji/emoji_one/construction_worker.png differ diff --git a/public/images/emoji/emoji_one/convenience_store.png b/public/images/emoji/emoji_one/convenience_store.png new file mode 100644 index 0000000000..675c233dfd Binary files /dev/null and b/public/images/emoji/emoji_one/convenience_store.png differ diff --git a/public/images/emoji/emoji_one/cookie.png b/public/images/emoji/emoji_one/cookie.png new file mode 100644 index 0000000000..8eb640e8d9 Binary files /dev/null and b/public/images/emoji/emoji_one/cookie.png differ diff --git a/public/images/emoji/emoji_one/cool.png b/public/images/emoji/emoji_one/cool.png new file mode 100644 index 0000000000..aaf3c16459 Binary files /dev/null and b/public/images/emoji/emoji_one/cool.png differ diff --git a/public/images/emoji/emoji_one/cop.png b/public/images/emoji/emoji_one/cop.png new file mode 100644 index 0000000000..be1b553cf2 Binary files /dev/null and b/public/images/emoji/emoji_one/cop.png differ diff --git a/plugins/emoji/public/images/unicode/00a9.png b/public/images/emoji/emoji_one/copyright.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/00a9.png rename to public/images/emoji/emoji_one/copyright.png diff --git a/public/images/emoji/emoji_one/corn.png b/public/images/emoji/emoji_one/corn.png new file mode 100644 index 0000000000..e5f2304bd2 Binary files /dev/null and b/public/images/emoji/emoji_one/corn.png differ diff --git a/public/images/emoji/emoji_one/couple.png b/public/images/emoji/emoji_one/couple.png new file mode 100644 index 0000000000..877fe39014 Binary files /dev/null and b/public/images/emoji/emoji_one/couple.png differ diff --git a/public/images/emoji/emoji_one/couple_with_heart.png b/public/images/emoji/emoji_one/couple_with_heart.png new file mode 100644 index 0000000000..88bd694a85 Binary files /dev/null and b/public/images/emoji/emoji_one/couple_with_heart.png differ diff --git a/public/images/emoji/emoji_one/couplekiss.png b/public/images/emoji/emoji_one/couplekiss.png new file mode 100644 index 0000000000..505398e983 Binary files /dev/null and b/public/images/emoji/emoji_one/couplekiss.png differ diff --git a/public/images/emoji/emoji_one/cow.png b/public/images/emoji/emoji_one/cow.png new file mode 100644 index 0000000000..49513b9945 Binary files /dev/null and b/public/images/emoji/emoji_one/cow.png differ diff --git a/public/images/emoji/emoji_one/cow2.png b/public/images/emoji/emoji_one/cow2.png new file mode 100644 index 0000000000..984b786bef Binary files /dev/null and b/public/images/emoji/emoji_one/cow2.png differ diff --git a/public/images/emoji/emoji_one/credit_card.png b/public/images/emoji/emoji_one/credit_card.png new file mode 100644 index 0000000000..7037fdb29b Binary files /dev/null and b/public/images/emoji/emoji_one/credit_card.png differ diff --git a/public/images/emoji/emoji_one/crescent_moon.png b/public/images/emoji/emoji_one/crescent_moon.png new file mode 100644 index 0000000000..75f7ed56d9 Binary files /dev/null and b/public/images/emoji/emoji_one/crescent_moon.png differ diff --git a/public/images/emoji/emoji_one/crocodile.png b/public/images/emoji/emoji_one/crocodile.png new file mode 100644 index 0000000000..3b53cacf8a Binary files /dev/null and b/public/images/emoji/emoji_one/crocodile.png differ diff --git a/public/images/emoji/emoji_one/crossed_flags.png b/public/images/emoji/emoji_one/crossed_flags.png new file mode 100644 index 0000000000..5d88b0ad0e Binary files /dev/null and b/public/images/emoji/emoji_one/crossed_flags.png differ diff --git a/public/images/emoji/emoji_one/crown.png b/public/images/emoji/emoji_one/crown.png new file mode 100644 index 0000000000..54ca7998cf Binary files /dev/null and b/public/images/emoji/emoji_one/crown.png differ diff --git a/public/images/emoji/emoji_one/cry.png b/public/images/emoji/emoji_one/cry.png new file mode 100644 index 0000000000..dd3f9028fa Binary files /dev/null and b/public/images/emoji/emoji_one/cry.png differ diff --git a/public/images/emoji/emoji_one/crying_cat_face.png b/public/images/emoji/emoji_one/crying_cat_face.png new file mode 100644 index 0000000000..58979048e1 Binary files /dev/null and b/public/images/emoji/emoji_one/crying_cat_face.png differ diff --git a/public/images/emoji/emoji_one/crystal_ball.png b/public/images/emoji/emoji_one/crystal_ball.png new file mode 100644 index 0000000000..d9ee3b4f36 Binary files /dev/null and b/public/images/emoji/emoji_one/crystal_ball.png differ diff --git a/public/images/emoji/emoji_one/cupid.png b/public/images/emoji/emoji_one/cupid.png new file mode 100644 index 0000000000..49260219c7 Binary files /dev/null and b/public/images/emoji/emoji_one/cupid.png differ diff --git a/plugins/emoji/public/images/unicode/27b0.png b/public/images/emoji/emoji_one/curly_loop.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/27b0.png rename to public/images/emoji/emoji_one/curly_loop.png diff --git a/public/images/emoji/emoji_one/currency_exchange.png b/public/images/emoji/emoji_one/currency_exchange.png new file mode 100644 index 0000000000..76a814ee2d Binary files /dev/null and b/public/images/emoji/emoji_one/currency_exchange.png differ diff --git a/public/images/emoji/emoji_one/curry.png b/public/images/emoji/emoji_one/curry.png new file mode 100644 index 0000000000..f97d0e0f9e Binary files /dev/null and b/public/images/emoji/emoji_one/curry.png differ diff --git a/public/images/emoji/emoji_one/custard.png b/public/images/emoji/emoji_one/custard.png new file mode 100644 index 0000000000..44af92a57d Binary files /dev/null and b/public/images/emoji/emoji_one/custard.png differ diff --git a/public/images/emoji/emoji_one/customs.png b/public/images/emoji/emoji_one/customs.png new file mode 100644 index 0000000000..c0b596b074 Binary files /dev/null and b/public/images/emoji/emoji_one/customs.png differ diff --git a/public/images/emoji/emoji_one/cyclone.png b/public/images/emoji/emoji_one/cyclone.png new file mode 100644 index 0000000000..b245f84172 Binary files /dev/null and b/public/images/emoji/emoji_one/cyclone.png differ diff --git a/public/images/emoji/emoji_one/dancer.png b/public/images/emoji/emoji_one/dancer.png new file mode 100644 index 0000000000..705ecf100c Binary files /dev/null and b/public/images/emoji/emoji_one/dancer.png differ diff --git a/public/images/emoji/emoji_one/dancers.png b/public/images/emoji/emoji_one/dancers.png new file mode 100644 index 0000000000..498d83d972 Binary files /dev/null and b/public/images/emoji/emoji_one/dancers.png differ diff --git a/public/images/emoji/emoji_one/dango.png b/public/images/emoji/emoji_one/dango.png new file mode 100644 index 0000000000..d679ebc023 Binary files /dev/null and b/public/images/emoji/emoji_one/dango.png differ diff --git a/public/images/emoji/emoji_one/dart.png b/public/images/emoji/emoji_one/dart.png new file mode 100644 index 0000000000..527b6a94e0 Binary files /dev/null and b/public/images/emoji/emoji_one/dart.png differ diff --git a/public/images/emoji/emoji_one/dash.png b/public/images/emoji/emoji_one/dash.png new file mode 100644 index 0000000000..d58c222815 Binary files /dev/null and b/public/images/emoji/emoji_one/dash.png differ diff --git a/public/images/emoji/emoji_one/date.png b/public/images/emoji/emoji_one/date.png new file mode 100644 index 0000000000..b6ddf24e37 Binary files /dev/null and b/public/images/emoji/emoji_one/date.png differ diff --git a/public/images/emoji/emoji_one/de.png b/public/images/emoji/emoji_one/de.png new file mode 100644 index 0000000000..36b3290dc0 Binary files /dev/null and b/public/images/emoji/emoji_one/de.png differ diff --git a/public/images/emoji/emoji_one/deciduous_tree.png b/public/images/emoji/emoji_one/deciduous_tree.png new file mode 100644 index 0000000000..1a54ec34b0 Binary files /dev/null and b/public/images/emoji/emoji_one/deciduous_tree.png differ diff --git a/public/images/emoji/emoji_one/department_store.png b/public/images/emoji/emoji_one/department_store.png new file mode 100644 index 0000000000..6433ed1549 Binary files /dev/null and b/public/images/emoji/emoji_one/department_store.png differ diff --git a/public/images/emoji/emoji_one/diamond_shape_with_a_dot_inside.png b/public/images/emoji/emoji_one/diamond_shape_with_a_dot_inside.png new file mode 100644 index 0000000000..6aec6cb192 Binary files /dev/null and b/public/images/emoji/emoji_one/diamond_shape_with_a_dot_inside.png differ diff --git a/public/images/emoji/emoji_one/diamonds.png b/public/images/emoji/emoji_one/diamonds.png new file mode 100644 index 0000000000..892d8bd24b Binary files /dev/null and b/public/images/emoji/emoji_one/diamonds.png differ diff --git a/public/images/emoji/emoji_one/disappointed.png b/public/images/emoji/emoji_one/disappointed.png new file mode 100644 index 0000000000..807e4698eb Binary files /dev/null and b/public/images/emoji/emoji_one/disappointed.png differ diff --git a/public/images/emoji/emoji_one/disappointed_relieved.png b/public/images/emoji/emoji_one/disappointed_relieved.png new file mode 100644 index 0000000000..aac3db94ce Binary files /dev/null and b/public/images/emoji/emoji_one/disappointed_relieved.png differ diff --git a/public/images/emoji/emoji_one/dizzy.png b/public/images/emoji/emoji_one/dizzy.png new file mode 100644 index 0000000000..b6972196c2 Binary files /dev/null and b/public/images/emoji/emoji_one/dizzy.png differ diff --git a/public/images/emoji/emoji_one/dizzy_face.png b/public/images/emoji/emoji_one/dizzy_face.png new file mode 100644 index 0000000000..6f7caabcb6 Binary files /dev/null and b/public/images/emoji/emoji_one/dizzy_face.png differ diff --git a/public/images/emoji/emoji_one/do_not_litter.png b/public/images/emoji/emoji_one/do_not_litter.png new file mode 100644 index 0000000000..66e692d41e Binary files /dev/null and b/public/images/emoji/emoji_one/do_not_litter.png differ diff --git a/public/images/emoji/emoji_one/dog.png b/public/images/emoji/emoji_one/dog.png new file mode 100644 index 0000000000..3c107deb40 Binary files /dev/null and b/public/images/emoji/emoji_one/dog.png differ diff --git a/public/images/emoji/emoji_one/dog2.png b/public/images/emoji/emoji_one/dog2.png new file mode 100644 index 0000000000..7057ffe660 Binary files /dev/null and b/public/images/emoji/emoji_one/dog2.png differ diff --git a/public/images/emoji/emoji_one/dollar.png b/public/images/emoji/emoji_one/dollar.png new file mode 100644 index 0000000000..04ebccf74d Binary files /dev/null and b/public/images/emoji/emoji_one/dollar.png differ diff --git a/public/images/emoji/emoji_one/dolls.png b/public/images/emoji/emoji_one/dolls.png new file mode 100644 index 0000000000..dcd7b93852 Binary files /dev/null and b/public/images/emoji/emoji_one/dolls.png differ diff --git a/public/images/emoji/emoji_one/dolphin.png b/public/images/emoji/emoji_one/dolphin.png new file mode 100644 index 0000000000..4dde4cc134 Binary files /dev/null and b/public/images/emoji/emoji_one/dolphin.png differ diff --git a/plugins/emoji/public/images/unicode/1f6aa.png b/public/images/emoji/emoji_one/door.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f6aa.png rename to public/images/emoji/emoji_one/door.png diff --git a/public/images/emoji/emoji_one/doughnut.png b/public/images/emoji/emoji_one/doughnut.png new file mode 100644 index 0000000000..b415c96bcf Binary files /dev/null and b/public/images/emoji/emoji_one/doughnut.png differ diff --git a/public/images/emoji/emoji_one/dragon.png b/public/images/emoji/emoji_one/dragon.png new file mode 100644 index 0000000000..b5b6357705 Binary files /dev/null and b/public/images/emoji/emoji_one/dragon.png differ diff --git a/public/images/emoji/emoji_one/dragon_face.png b/public/images/emoji/emoji_one/dragon_face.png new file mode 100644 index 0000000000..aba3bd4715 Binary files /dev/null and b/public/images/emoji/emoji_one/dragon_face.png differ diff --git a/public/images/emoji/emoji_one/dress.png b/public/images/emoji/emoji_one/dress.png new file mode 100644 index 0000000000..512fe8bfeb Binary files /dev/null and b/public/images/emoji/emoji_one/dress.png differ diff --git a/public/images/emoji/emoji_one/dromedary_camel.png b/public/images/emoji/emoji_one/dromedary_camel.png new file mode 100644 index 0000000000..e881a8b5f4 Binary files /dev/null and b/public/images/emoji/emoji_one/dromedary_camel.png differ diff --git a/public/images/emoji/emoji_one/droplet.png b/public/images/emoji/emoji_one/droplet.png new file mode 100644 index 0000000000..12ea46c196 Binary files /dev/null and b/public/images/emoji/emoji_one/droplet.png differ diff --git a/public/images/emoji/emoji_one/dvd.png b/public/images/emoji/emoji_one/dvd.png new file mode 100644 index 0000000000..294b914086 Binary files /dev/null and b/public/images/emoji/emoji_one/dvd.png differ diff --git a/public/images/emoji/emoji_one/e-mail.png b/public/images/emoji/emoji_one/e-mail.png new file mode 100644 index 0000000000..52a0022f88 Binary files /dev/null and b/public/images/emoji/emoji_one/e-mail.png differ diff --git a/public/images/emoji/emoji_one/ear.png b/public/images/emoji/emoji_one/ear.png new file mode 100644 index 0000000000..84053622b1 Binary files /dev/null and b/public/images/emoji/emoji_one/ear.png differ diff --git a/public/images/emoji/emoji_one/ear_of_rice.png b/public/images/emoji/emoji_one/ear_of_rice.png new file mode 100644 index 0000000000..9d3f454f86 Binary files /dev/null and b/public/images/emoji/emoji_one/ear_of_rice.png differ diff --git a/public/images/emoji/emoji_one/earth_africa.png b/public/images/emoji/emoji_one/earth_africa.png new file mode 100644 index 0000000000..a4bbace549 Binary files /dev/null and b/public/images/emoji/emoji_one/earth_africa.png differ diff --git a/public/images/emoji/emoji_one/earth_americas.png b/public/images/emoji/emoji_one/earth_americas.png new file mode 100644 index 0000000000..13a307f3c3 Binary files /dev/null and b/public/images/emoji/emoji_one/earth_americas.png differ diff --git a/public/images/emoji/emoji_one/earth_asia.png b/public/images/emoji/emoji_one/earth_asia.png new file mode 100644 index 0000000000..e8fcb563f7 Binary files /dev/null and b/public/images/emoji/emoji_one/earth_asia.png differ diff --git a/public/images/emoji/emoji_one/egg.png b/public/images/emoji/emoji_one/egg.png new file mode 100644 index 0000000000..b5c090a17f Binary files /dev/null and b/public/images/emoji/emoji_one/egg.png differ diff --git a/public/images/emoji/emoji_one/eggplant.png b/public/images/emoji/emoji_one/eggplant.png new file mode 100644 index 0000000000..75c902569e Binary files /dev/null and b/public/images/emoji/emoji_one/eggplant.png differ diff --git a/public/images/emoji/emoji_one/eight.png b/public/images/emoji/emoji_one/eight.png new file mode 100644 index 0000000000..0507811606 Binary files /dev/null and b/public/images/emoji/emoji_one/eight.png differ diff --git a/public/images/emoji/emoji_one/eight_pointed_black_star.png b/public/images/emoji/emoji_one/eight_pointed_black_star.png new file mode 100644 index 0000000000..7dcb6b338c Binary files /dev/null and b/public/images/emoji/emoji_one/eight_pointed_black_star.png differ diff --git a/public/images/emoji/emoji_one/eight_spoked_asterisk.png b/public/images/emoji/emoji_one/eight_spoked_asterisk.png new file mode 100644 index 0000000000..1d3717b994 Binary files /dev/null and b/public/images/emoji/emoji_one/eight_spoked_asterisk.png differ diff --git a/plugins/emoji/public/images/unicode/1f50c.png b/public/images/emoji/emoji_one/electric_plug.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f50c.png rename to public/images/emoji/emoji_one/electric_plug.png diff --git a/public/images/emoji/emoji_one/elephant.png b/public/images/emoji/emoji_one/elephant.png new file mode 100644 index 0000000000..af079ec4db Binary files /dev/null and b/public/images/emoji/emoji_one/elephant.png differ diff --git a/plugins/emoji/public/images/unicode/2709.png b/public/images/emoji/emoji_one/email.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/2709.png rename to public/images/emoji/emoji_one/email.png diff --git a/public/images/emoji/emoji_one/end.png b/public/images/emoji/emoji_one/end.png new file mode 100644 index 0000000000..a0cfda28b9 Binary files /dev/null and b/public/images/emoji/emoji_one/end.png differ diff --git a/public/images/emoji/emoji_one/envelope.png b/public/images/emoji/emoji_one/envelope.png new file mode 100644 index 0000000000..b512f6d6a7 Binary files /dev/null and b/public/images/emoji/emoji_one/envelope.png differ diff --git a/public/images/emoji/emoji_one/envelope_with_arrow.png b/public/images/emoji/emoji_one/envelope_with_arrow.png new file mode 100644 index 0000000000..373cd8f0f5 Binary files /dev/null and b/public/images/emoji/emoji_one/envelope_with_arrow.png differ diff --git a/public/images/emoji/emoji_one/es.png b/public/images/emoji/emoji_one/es.png new file mode 100644 index 0000000000..b47b1de834 Binary files /dev/null and b/public/images/emoji/emoji_one/es.png differ diff --git a/public/images/emoji/emoji_one/euro.png b/public/images/emoji/emoji_one/euro.png new file mode 100644 index 0000000000..2c71bbde8f Binary files /dev/null and b/public/images/emoji/emoji_one/euro.png differ diff --git a/public/images/emoji/emoji_one/european_castle.png b/public/images/emoji/emoji_one/european_castle.png new file mode 100644 index 0000000000..1af59a5b67 Binary files /dev/null and b/public/images/emoji/emoji_one/european_castle.png differ diff --git a/public/images/emoji/emoji_one/european_post_office.png b/public/images/emoji/emoji_one/european_post_office.png new file mode 100644 index 0000000000..efc6617c57 Binary files /dev/null and b/public/images/emoji/emoji_one/european_post_office.png differ diff --git a/public/images/emoji/emoji_one/evergreen_tree.png b/public/images/emoji/emoji_one/evergreen_tree.png new file mode 100644 index 0000000000..605e9a167c Binary files /dev/null and b/public/images/emoji/emoji_one/evergreen_tree.png differ diff --git a/public/images/emoji/emoji_one/exclamation.png b/public/images/emoji/emoji_one/exclamation.png new file mode 100644 index 0000000000..6f9147ac0d Binary files /dev/null and b/public/images/emoji/emoji_one/exclamation.png differ diff --git a/public/images/emoji/emoji_one/expressionless.png b/public/images/emoji/emoji_one/expressionless.png new file mode 100644 index 0000000000..5b7eb95a6a Binary files /dev/null and b/public/images/emoji/emoji_one/expressionless.png differ diff --git a/public/images/emoji/emoji_one/eyeglasses.png b/public/images/emoji/emoji_one/eyeglasses.png new file mode 100644 index 0000000000..f1f4612ba2 Binary files /dev/null and b/public/images/emoji/emoji_one/eyeglasses.png differ diff --git a/public/images/emoji/emoji_one/eyes.png b/public/images/emoji/emoji_one/eyes.png new file mode 100644 index 0000000000..d9a6163f43 Binary files /dev/null and b/public/images/emoji/emoji_one/eyes.png differ diff --git a/plugins/emoji/public/images/unicode/1f44a.png b/public/images/emoji/emoji_one/facepunch.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f44a.png rename to public/images/emoji/emoji_one/facepunch.png diff --git a/public/images/emoji/emoji_one/factory.png b/public/images/emoji/emoji_one/factory.png new file mode 100644 index 0000000000..ba04537b7c Binary files /dev/null and b/public/images/emoji/emoji_one/factory.png differ diff --git a/public/images/emoji/emoji_one/fallen_leaf.png b/public/images/emoji/emoji_one/fallen_leaf.png new file mode 100644 index 0000000000..430ac02970 Binary files /dev/null and b/public/images/emoji/emoji_one/fallen_leaf.png differ diff --git a/public/images/emoji/emoji_one/family.png b/public/images/emoji/emoji_one/family.png new file mode 100644 index 0000000000..93b4bcccbd Binary files /dev/null and b/public/images/emoji/emoji_one/family.png differ diff --git a/public/images/emoji/emoji_one/fast_forward.png b/public/images/emoji/emoji_one/fast_forward.png new file mode 100644 index 0000000000..c810d4039c Binary files /dev/null and b/public/images/emoji/emoji_one/fast_forward.png differ diff --git a/public/images/emoji/emoji_one/fax.png b/public/images/emoji/emoji_one/fax.png new file mode 100644 index 0000000000..471d88a2ac Binary files /dev/null and b/public/images/emoji/emoji_one/fax.png differ diff --git a/public/images/emoji/emoji_one/fearful.png b/public/images/emoji/emoji_one/fearful.png new file mode 100644 index 0000000000..194824206c Binary files /dev/null and b/public/images/emoji/emoji_one/fearful.png differ diff --git a/public/images/emoji/emoji_one/feet.png b/public/images/emoji/emoji_one/feet.png new file mode 100644 index 0000000000..970366ce19 Binary files /dev/null and b/public/images/emoji/emoji_one/feet.png differ diff --git a/public/images/emoji/emoji_one/ferris_wheel.png b/public/images/emoji/emoji_one/ferris_wheel.png new file mode 100644 index 0000000000..5f1f8c1589 Binary files /dev/null and b/public/images/emoji/emoji_one/ferris_wheel.png differ diff --git a/public/images/emoji/emoji_one/file_folder.png b/public/images/emoji/emoji_one/file_folder.png new file mode 100644 index 0000000000..244d70720b Binary files /dev/null and b/public/images/emoji/emoji_one/file_folder.png differ diff --git a/public/images/emoji/emoji_one/fire.png b/public/images/emoji/emoji_one/fire.png new file mode 100644 index 0000000000..3149ed6758 Binary files /dev/null and b/public/images/emoji/emoji_one/fire.png differ diff --git a/public/images/emoji/emoji_one/fire_engine.png b/public/images/emoji/emoji_one/fire_engine.png new file mode 100644 index 0000000000..0336bfdf5c Binary files /dev/null and b/public/images/emoji/emoji_one/fire_engine.png differ diff --git a/public/images/emoji/emoji_one/fireworks.png b/public/images/emoji/emoji_one/fireworks.png new file mode 100644 index 0000000000..6c67a0e15f Binary files /dev/null and b/public/images/emoji/emoji_one/fireworks.png differ diff --git a/public/images/emoji/emoji_one/first_quarter_moon.png b/public/images/emoji/emoji_one/first_quarter_moon.png new file mode 100644 index 0000000000..14734a42c4 Binary files /dev/null and b/public/images/emoji/emoji_one/first_quarter_moon.png differ diff --git a/plugins/emoji/public/images/unicode/1f31b.png b/public/images/emoji/emoji_one/first_quarter_moon_with_face.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f31b.png rename to public/images/emoji/emoji_one/first_quarter_moon_with_face.png diff --git a/public/images/emoji/emoji_one/fish.png b/public/images/emoji/emoji_one/fish.png new file mode 100644 index 0000000000..cb7b896d6d Binary files /dev/null and b/public/images/emoji/emoji_one/fish.png differ diff --git a/public/images/emoji/emoji_one/fish_cake.png b/public/images/emoji/emoji_one/fish_cake.png new file mode 100644 index 0000000000..6ff94bc36a Binary files /dev/null and b/public/images/emoji/emoji_one/fish_cake.png differ diff --git a/public/images/emoji/emoji_one/fishing_pole_and_fish.png b/public/images/emoji/emoji_one/fishing_pole_and_fish.png new file mode 100644 index 0000000000..270a011dd6 Binary files /dev/null and b/public/images/emoji/emoji_one/fishing_pole_and_fish.png differ diff --git a/plugins/emoji/public/images/unicode/270a.png b/public/images/emoji/emoji_one/fist.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/270a.png rename to public/images/emoji/emoji_one/fist.png diff --git a/public/images/emoji/emoji_one/five.png b/public/images/emoji/emoji_one/five.png new file mode 100644 index 0000000000..8d8d661465 Binary files /dev/null and b/public/images/emoji/emoji_one/five.png differ diff --git a/public/images/emoji/emoji_one/flags.png b/public/images/emoji/emoji_one/flags.png new file mode 100644 index 0000000000..a6bbd0ea3c Binary files /dev/null and b/public/images/emoji/emoji_one/flags.png differ diff --git a/public/images/emoji/emoji_one/flashlight.png b/public/images/emoji/emoji_one/flashlight.png new file mode 100644 index 0000000000..8c7781e9a9 Binary files /dev/null and b/public/images/emoji/emoji_one/flashlight.png differ diff --git a/public/images/emoji/emoji_one/flipper.png b/public/images/emoji/emoji_one/flipper.png new file mode 100644 index 0000000000..4dde4cc134 Binary files /dev/null and b/public/images/emoji/emoji_one/flipper.png differ diff --git a/plugins/emoji/public/images/unicode/1f4be.png b/public/images/emoji/emoji_one/floppy_disk.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f4be.png rename to public/images/emoji/emoji_one/floppy_disk.png diff --git a/public/images/emoji/emoji_one/flower_playing_cards.png b/public/images/emoji/emoji_one/flower_playing_cards.png new file mode 100644 index 0000000000..62a910aa5e Binary files /dev/null and b/public/images/emoji/emoji_one/flower_playing_cards.png differ diff --git a/public/images/emoji/emoji_one/flushed.png b/public/images/emoji/emoji_one/flushed.png new file mode 100644 index 0000000000..77487a9f59 Binary files /dev/null and b/public/images/emoji/emoji_one/flushed.png differ diff --git a/public/images/emoji/emoji_one/foggy.png b/public/images/emoji/emoji_one/foggy.png new file mode 100644 index 0000000000..5686be4c1a Binary files /dev/null and b/public/images/emoji/emoji_one/foggy.png differ diff --git a/public/images/emoji/emoji_one/football.png b/public/images/emoji/emoji_one/football.png new file mode 100644 index 0000000000..77614589c7 Binary files /dev/null and b/public/images/emoji/emoji_one/football.png differ diff --git a/public/images/emoji/emoji_one/footprints.png b/public/images/emoji/emoji_one/footprints.png new file mode 100644 index 0000000000..95c26fc9da Binary files /dev/null and b/public/images/emoji/emoji_one/footprints.png differ diff --git a/public/images/emoji/emoji_one/fork_and_knife.png b/public/images/emoji/emoji_one/fork_and_knife.png new file mode 100644 index 0000000000..35c2962881 Binary files /dev/null and b/public/images/emoji/emoji_one/fork_and_knife.png differ diff --git a/public/images/emoji/emoji_one/fountain.png b/public/images/emoji/emoji_one/fountain.png new file mode 100644 index 0000000000..6c01c0c907 Binary files /dev/null and b/public/images/emoji/emoji_one/fountain.png differ diff --git a/plugins/emoji/public/images/unicode/0034-20E3.png b/public/images/emoji/emoji_one/four.png similarity index 100% rename from plugins/emoji/public/images/unicode/0034-20E3.png rename to public/images/emoji/emoji_one/four.png diff --git a/public/images/emoji/emoji_one/four_leaf_clover.png b/public/images/emoji/emoji_one/four_leaf_clover.png new file mode 100644 index 0000000000..4e034da307 Binary files /dev/null and b/public/images/emoji/emoji_one/four_leaf_clover.png differ diff --git a/public/images/emoji/emoji_one/fr.png b/public/images/emoji/emoji_one/fr.png new file mode 100644 index 0000000000..14271f059d Binary files /dev/null and b/public/images/emoji/emoji_one/fr.png differ diff --git a/plugins/emoji/public/images/unicode/1f193.png b/public/images/emoji/emoji_one/free.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f193.png rename to public/images/emoji/emoji_one/free.png diff --git a/public/images/emoji/emoji_one/fried_shrimp.png b/public/images/emoji/emoji_one/fried_shrimp.png new file mode 100644 index 0000000000..30c165438a Binary files /dev/null and b/public/images/emoji/emoji_one/fried_shrimp.png differ diff --git a/plugins/emoji/public/images/unicode/1f35f.png b/public/images/emoji/emoji_one/fries.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f35f.png rename to public/images/emoji/emoji_one/fries.png diff --git a/public/images/emoji/emoji_one/frog.png b/public/images/emoji/emoji_one/frog.png new file mode 100644 index 0000000000..726d0280b2 Binary files /dev/null and b/public/images/emoji/emoji_one/frog.png differ diff --git a/public/images/emoji/emoji_one/frowning.png b/public/images/emoji/emoji_one/frowning.png new file mode 100644 index 0000000000..15f0913848 Binary files /dev/null and b/public/images/emoji/emoji_one/frowning.png differ diff --git a/public/images/emoji/emoji_one/fuelpump.png b/public/images/emoji/emoji_one/fuelpump.png new file mode 100644 index 0000000000..13be715301 Binary files /dev/null and b/public/images/emoji/emoji_one/fuelpump.png differ diff --git a/plugins/emoji/public/images/unicode/1f315.png b/public/images/emoji/emoji_one/full_moon.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f315.png rename to public/images/emoji/emoji_one/full_moon.png diff --git a/public/images/emoji/emoji_one/full_moon_with_face.png b/public/images/emoji/emoji_one/full_moon_with_face.png new file mode 100644 index 0000000000..adb3479d9c Binary files /dev/null and b/public/images/emoji/emoji_one/full_moon_with_face.png differ diff --git a/public/images/emoji/emoji_one/game_die.png b/public/images/emoji/emoji_one/game_die.png new file mode 100644 index 0000000000..7723d290b7 Binary files /dev/null and b/public/images/emoji/emoji_one/game_die.png differ diff --git a/public/images/emoji/emoji_one/gb.png b/public/images/emoji/emoji_one/gb.png new file mode 100644 index 0000000000..61c03f759f Binary files /dev/null and b/public/images/emoji/emoji_one/gb.png differ diff --git a/public/images/emoji/emoji_one/gem.png b/public/images/emoji/emoji_one/gem.png new file mode 100644 index 0000000000..a24a082a78 Binary files /dev/null and b/public/images/emoji/emoji_one/gem.png differ diff --git a/public/images/emoji/emoji_one/gemini.png b/public/images/emoji/emoji_one/gemini.png new file mode 100644 index 0000000000..9c53145cae Binary files /dev/null and b/public/images/emoji/emoji_one/gemini.png differ diff --git a/public/images/emoji/emoji_one/ghost.png b/public/images/emoji/emoji_one/ghost.png new file mode 100644 index 0000000000..6983969927 Binary files /dev/null and b/public/images/emoji/emoji_one/ghost.png differ diff --git a/public/images/emoji/emoji_one/gift.png b/public/images/emoji/emoji_one/gift.png new file mode 100644 index 0000000000..bde801b559 Binary files /dev/null and b/public/images/emoji/emoji_one/gift.png differ diff --git a/public/images/emoji/emoji_one/gift_heart.png b/public/images/emoji/emoji_one/gift_heart.png new file mode 100644 index 0000000000..eb0292da0c Binary files /dev/null and b/public/images/emoji/emoji_one/gift_heart.png differ diff --git a/public/images/emoji/emoji_one/girl.png b/public/images/emoji/emoji_one/girl.png new file mode 100644 index 0000000000..987e5e057a Binary files /dev/null and b/public/images/emoji/emoji_one/girl.png differ diff --git a/public/images/emoji/emoji_one/globe_with_meridians.png b/public/images/emoji/emoji_one/globe_with_meridians.png new file mode 100644 index 0000000000..a7af2cb1c9 Binary files /dev/null and b/public/images/emoji/emoji_one/globe_with_meridians.png differ diff --git a/public/images/emoji/emoji_one/goat.png b/public/images/emoji/emoji_one/goat.png new file mode 100644 index 0000000000..41210e5fae Binary files /dev/null and b/public/images/emoji/emoji_one/goat.png differ diff --git a/public/images/emoji/emoji_one/golf.png b/public/images/emoji/emoji_one/golf.png new file mode 100644 index 0000000000..c03f809174 Binary files /dev/null and b/public/images/emoji/emoji_one/golf.png differ diff --git a/public/images/emoji/emoji_one/grapes.png b/public/images/emoji/emoji_one/grapes.png new file mode 100644 index 0000000000..20b0971319 Binary files /dev/null and b/public/images/emoji/emoji_one/grapes.png differ diff --git a/public/images/emoji/emoji_one/green_apple.png b/public/images/emoji/emoji_one/green_apple.png new file mode 100644 index 0000000000..e375147731 Binary files /dev/null and b/public/images/emoji/emoji_one/green_apple.png differ diff --git a/public/images/emoji/emoji_one/green_book.png b/public/images/emoji/emoji_one/green_book.png new file mode 100644 index 0000000000..e5e0a6b31e Binary files /dev/null and b/public/images/emoji/emoji_one/green_book.png differ diff --git a/plugins/emoji/public/images/unicode/1f49a.png b/public/images/emoji/emoji_one/green_heart.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f49a.png rename to public/images/emoji/emoji_one/green_heart.png diff --git a/public/images/emoji/emoji_one/grey_exclamation.png b/public/images/emoji/emoji_one/grey_exclamation.png new file mode 100644 index 0000000000..bed5d887d7 Binary files /dev/null and b/public/images/emoji/emoji_one/grey_exclamation.png differ diff --git a/public/images/emoji/emoji_one/grey_question.png b/public/images/emoji/emoji_one/grey_question.png new file mode 100644 index 0000000000..f4884404f0 Binary files /dev/null and b/public/images/emoji/emoji_one/grey_question.png differ diff --git a/public/images/emoji/emoji_one/grimacing.png b/public/images/emoji/emoji_one/grimacing.png new file mode 100644 index 0000000000..7b2944ec2a Binary files /dev/null and b/public/images/emoji/emoji_one/grimacing.png differ diff --git a/public/images/emoji/emoji_one/grin.png b/public/images/emoji/emoji_one/grin.png new file mode 100644 index 0000000000..d7e7bd7206 Binary files /dev/null and b/public/images/emoji/emoji_one/grin.png differ diff --git a/public/images/emoji/emoji_one/grinning.png b/public/images/emoji/emoji_one/grinning.png new file mode 100644 index 0000000000..70aba64425 Binary files /dev/null and b/public/images/emoji/emoji_one/grinning.png differ diff --git a/public/images/emoji/emoji_one/guardsman.png b/public/images/emoji/emoji_one/guardsman.png new file mode 100644 index 0000000000..766ee30fa5 Binary files /dev/null and b/public/images/emoji/emoji_one/guardsman.png differ diff --git a/public/images/emoji/emoji_one/guitar.png b/public/images/emoji/emoji_one/guitar.png new file mode 100644 index 0000000000..ab62a7fbb7 Binary files /dev/null and b/public/images/emoji/emoji_one/guitar.png differ diff --git a/public/images/emoji/emoji_one/gun.png b/public/images/emoji/emoji_one/gun.png new file mode 100644 index 0000000000..f0e6b8fab7 Binary files /dev/null and b/public/images/emoji/emoji_one/gun.png differ diff --git a/public/images/emoji/emoji_one/haircut.png b/public/images/emoji/emoji_one/haircut.png new file mode 100644 index 0000000000..191c5036b1 Binary files /dev/null and b/public/images/emoji/emoji_one/haircut.png differ diff --git a/public/images/emoji/emoji_one/hamburger.png b/public/images/emoji/emoji_one/hamburger.png new file mode 100644 index 0000000000..b18121930c Binary files /dev/null and b/public/images/emoji/emoji_one/hamburger.png differ diff --git a/public/images/emoji/emoji_one/hammer.png b/public/images/emoji/emoji_one/hammer.png new file mode 100644 index 0000000000..6011c3a7cf Binary files /dev/null and b/public/images/emoji/emoji_one/hammer.png differ diff --git a/public/images/emoji/emoji_one/hamster.png b/public/images/emoji/emoji_one/hamster.png new file mode 100644 index 0000000000..b19be37384 Binary files /dev/null and b/public/images/emoji/emoji_one/hamster.png differ diff --git a/public/images/emoji/emoji_one/hand.png b/public/images/emoji/emoji_one/hand.png new file mode 100644 index 0000000000..4f1fce256e Binary files /dev/null and b/public/images/emoji/emoji_one/hand.png differ diff --git a/public/images/emoji/emoji_one/handbag.png b/public/images/emoji/emoji_one/handbag.png new file mode 100644 index 0000000000..5566887879 Binary files /dev/null and b/public/images/emoji/emoji_one/handbag.png differ diff --git a/public/images/emoji/emoji_one/hankey.png b/public/images/emoji/emoji_one/hankey.png new file mode 100644 index 0000000000..0c237a7881 Binary files /dev/null and b/public/images/emoji/emoji_one/hankey.png differ diff --git a/public/images/emoji/emoji_one/hash.png b/public/images/emoji/emoji_one/hash.png new file mode 100644 index 0000000000..46d5f7d87a Binary files /dev/null and b/public/images/emoji/emoji_one/hash.png differ diff --git a/public/images/emoji/emoji_one/hatched_chick.png b/public/images/emoji/emoji_one/hatched_chick.png new file mode 100644 index 0000000000..de82199dcf Binary files /dev/null and b/public/images/emoji/emoji_one/hatched_chick.png differ diff --git a/public/images/emoji/emoji_one/hatching_chick.png b/public/images/emoji/emoji_one/hatching_chick.png new file mode 100644 index 0000000000..1e688c2284 Binary files /dev/null and b/public/images/emoji/emoji_one/hatching_chick.png differ diff --git a/public/images/emoji/emoji_one/headphones.png b/public/images/emoji/emoji_one/headphones.png new file mode 100644 index 0000000000..3335d40600 Binary files /dev/null and b/public/images/emoji/emoji_one/headphones.png differ diff --git a/public/images/emoji/emoji_one/hear_no_evil.png b/public/images/emoji/emoji_one/hear_no_evil.png new file mode 100644 index 0000000000..e909ebf246 Binary files /dev/null and b/public/images/emoji/emoji_one/hear_no_evil.png differ diff --git a/plugins/emoji/public/images/unicode/2764.png b/public/images/emoji/emoji_one/heart.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/2764.png rename to public/images/emoji/emoji_one/heart.png diff --git a/plugins/emoji/public/images/unicode/1f49f.png b/public/images/emoji/emoji_one/heart_decoration.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f49f.png rename to public/images/emoji/emoji_one/heart_decoration.png diff --git a/public/images/emoji/emoji_one/heart_eyes.png b/public/images/emoji/emoji_one/heart_eyes.png new file mode 100644 index 0000000000..56aa60e8df Binary files /dev/null and b/public/images/emoji/emoji_one/heart_eyes.png differ diff --git a/public/images/emoji/emoji_one/heart_eyes_cat.png b/public/images/emoji/emoji_one/heart_eyes_cat.png new file mode 100644 index 0000000000..7bdd596417 Binary files /dev/null and b/public/images/emoji/emoji_one/heart_eyes_cat.png differ diff --git a/public/images/emoji/emoji_one/heartbeat.png b/public/images/emoji/emoji_one/heartbeat.png new file mode 100644 index 0000000000..43a365e95f Binary files /dev/null and b/public/images/emoji/emoji_one/heartbeat.png differ diff --git a/public/images/emoji/emoji_one/heartpulse.png b/public/images/emoji/emoji_one/heartpulse.png new file mode 100644 index 0000000000..b3d5da2e39 Binary files /dev/null and b/public/images/emoji/emoji_one/heartpulse.png differ diff --git a/public/images/emoji/emoji_one/hearts.png b/public/images/emoji/emoji_one/hearts.png new file mode 100644 index 0000000000..f861548353 Binary files /dev/null and b/public/images/emoji/emoji_one/hearts.png differ diff --git a/plugins/emoji/public/images/unicode/2714.png b/public/images/emoji/emoji_one/heavy_check_mark.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/2714.png rename to public/images/emoji/emoji_one/heavy_check_mark.png diff --git a/public/images/emoji/emoji_one/heavy_division_sign.png b/public/images/emoji/emoji_one/heavy_division_sign.png new file mode 100644 index 0000000000..56b457e61b Binary files /dev/null and b/public/images/emoji/emoji_one/heavy_division_sign.png differ diff --git a/public/images/emoji/emoji_one/heavy_dollar_sign.png b/public/images/emoji/emoji_one/heavy_dollar_sign.png new file mode 100644 index 0000000000..4bad879bd9 Binary files /dev/null and b/public/images/emoji/emoji_one/heavy_dollar_sign.png differ diff --git a/public/images/emoji/emoji_one/heavy_exclamation_mark.png b/public/images/emoji/emoji_one/heavy_exclamation_mark.png new file mode 100644 index 0000000000..6f9147ac0d Binary files /dev/null and b/public/images/emoji/emoji_one/heavy_exclamation_mark.png differ diff --git a/public/images/emoji/emoji_one/heavy_minus_sign.png b/public/images/emoji/emoji_one/heavy_minus_sign.png new file mode 100644 index 0000000000..6a930366a7 Binary files /dev/null and b/public/images/emoji/emoji_one/heavy_minus_sign.png differ diff --git a/public/images/emoji/emoji_one/heavy_multiplication_x.png b/public/images/emoji/emoji_one/heavy_multiplication_x.png new file mode 100644 index 0000000000..8979247250 Binary files /dev/null and b/public/images/emoji/emoji_one/heavy_multiplication_x.png differ diff --git a/plugins/emoji/public/images/unicode/2795.png b/public/images/emoji/emoji_one/heavy_plus_sign.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/2795.png rename to public/images/emoji/emoji_one/heavy_plus_sign.png diff --git a/public/images/emoji/emoji_one/helicopter.png b/public/images/emoji/emoji_one/helicopter.png new file mode 100644 index 0000000000..838acddc74 Binary files /dev/null and b/public/images/emoji/emoji_one/helicopter.png differ diff --git a/public/images/emoji/emoji_one/herb.png b/public/images/emoji/emoji_one/herb.png new file mode 100644 index 0000000000..cb5f8f7353 Binary files /dev/null and b/public/images/emoji/emoji_one/herb.png differ diff --git a/public/images/emoji/emoji_one/hibiscus.png b/public/images/emoji/emoji_one/hibiscus.png new file mode 100644 index 0000000000..e0fd12c95d Binary files /dev/null and b/public/images/emoji/emoji_one/hibiscus.png differ diff --git a/public/images/emoji/emoji_one/high_brightness.png b/public/images/emoji/emoji_one/high_brightness.png new file mode 100644 index 0000000000..7fd0d11c6a Binary files /dev/null and b/public/images/emoji/emoji_one/high_brightness.png differ diff --git a/public/images/emoji/emoji_one/high_heel.png b/public/images/emoji/emoji_one/high_heel.png new file mode 100644 index 0000000000..36bdb74841 Binary files /dev/null and b/public/images/emoji/emoji_one/high_heel.png differ diff --git a/public/images/emoji/emoji_one/hocho.png b/public/images/emoji/emoji_one/hocho.png new file mode 100644 index 0000000000..43249ba942 Binary files /dev/null and b/public/images/emoji/emoji_one/hocho.png differ diff --git a/public/images/emoji/emoji_one/honey_pot.png b/public/images/emoji/emoji_one/honey_pot.png new file mode 100644 index 0000000000..a563f59c7c Binary files /dev/null and b/public/images/emoji/emoji_one/honey_pot.png differ diff --git a/public/images/emoji/emoji_one/honeybee.png b/public/images/emoji/emoji_one/honeybee.png new file mode 100644 index 0000000000..e67b7703bf Binary files /dev/null and b/public/images/emoji/emoji_one/honeybee.png differ diff --git a/public/images/emoji/emoji_one/horse.png b/public/images/emoji/emoji_one/horse.png new file mode 100644 index 0000000000..90b9555064 Binary files /dev/null and b/public/images/emoji/emoji_one/horse.png differ diff --git a/public/images/emoji/emoji_one/horse_racing.png b/public/images/emoji/emoji_one/horse_racing.png new file mode 100644 index 0000000000..57133e8b7a Binary files /dev/null and b/public/images/emoji/emoji_one/horse_racing.png differ diff --git a/public/images/emoji/emoji_one/hospital.png b/public/images/emoji/emoji_one/hospital.png new file mode 100644 index 0000000000..80f87bfbe0 Binary files /dev/null and b/public/images/emoji/emoji_one/hospital.png differ diff --git a/public/images/emoji/emoji_one/hotel.png b/public/images/emoji/emoji_one/hotel.png new file mode 100644 index 0000000000..121b93b0fb Binary files /dev/null and b/public/images/emoji/emoji_one/hotel.png differ diff --git a/public/images/emoji/emoji_one/hotsprings.png b/public/images/emoji/emoji_one/hotsprings.png new file mode 100644 index 0000000000..dbe158292b Binary files /dev/null and b/public/images/emoji/emoji_one/hotsprings.png differ diff --git a/public/images/emoji/emoji_one/hourglass.png b/public/images/emoji/emoji_one/hourglass.png new file mode 100644 index 0000000000..06799f8c51 Binary files /dev/null and b/public/images/emoji/emoji_one/hourglass.png differ diff --git a/public/images/emoji/emoji_one/hourglass_flowing_sand.png b/public/images/emoji/emoji_one/hourglass_flowing_sand.png new file mode 100644 index 0000000000..bb0bbd8f70 Binary files /dev/null and b/public/images/emoji/emoji_one/hourglass_flowing_sand.png differ diff --git a/public/images/emoji/emoji_one/house.png b/public/images/emoji/emoji_one/house.png new file mode 100644 index 0000000000..90551d8235 Binary files /dev/null and b/public/images/emoji/emoji_one/house.png differ diff --git a/public/images/emoji/emoji_one/house_with_garden.png b/public/images/emoji/emoji_one/house_with_garden.png new file mode 100644 index 0000000000..b00186c463 Binary files /dev/null and b/public/images/emoji/emoji_one/house_with_garden.png differ diff --git a/public/images/emoji/emoji_one/hushed.png b/public/images/emoji/emoji_one/hushed.png new file mode 100644 index 0000000000..e1a2b43ec9 Binary files /dev/null and b/public/images/emoji/emoji_one/hushed.png differ diff --git a/public/images/emoji/emoji_one/ice_cream.png b/public/images/emoji/emoji_one/ice_cream.png new file mode 100644 index 0000000000..ffcad18b4e Binary files /dev/null and b/public/images/emoji/emoji_one/ice_cream.png differ diff --git a/public/images/emoji/emoji_one/icecream.png b/public/images/emoji/emoji_one/icecream.png new file mode 100644 index 0000000000..39c2e6a8f9 Binary files /dev/null and b/public/images/emoji/emoji_one/icecream.png differ diff --git a/plugins/emoji/public/images/unicode/1f194.png b/public/images/emoji/emoji_one/id.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f194.png rename to public/images/emoji/emoji_one/id.png diff --git a/public/images/emoji/emoji_one/ideograph_advantage.png b/public/images/emoji/emoji_one/ideograph_advantage.png new file mode 100644 index 0000000000..8df529c4ae Binary files /dev/null and b/public/images/emoji/emoji_one/ideograph_advantage.png differ diff --git a/public/images/emoji/emoji_one/imp.png b/public/images/emoji/emoji_one/imp.png new file mode 100644 index 0000000000..f5e6059dab Binary files /dev/null and b/public/images/emoji/emoji_one/imp.png differ diff --git a/public/images/emoji/emoji_one/inbox_tray.png b/public/images/emoji/emoji_one/inbox_tray.png new file mode 100644 index 0000000000..89073464d5 Binary files /dev/null and b/public/images/emoji/emoji_one/inbox_tray.png differ diff --git a/plugins/emoji/public/images/unicode/1f4e8.png b/public/images/emoji/emoji_one/incoming_envelope.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f4e8.png rename to public/images/emoji/emoji_one/incoming_envelope.png diff --git a/public/images/emoji/emoji_one/information_desk_person.png b/public/images/emoji/emoji_one/information_desk_person.png new file mode 100644 index 0000000000..13d1ed0053 Binary files /dev/null and b/public/images/emoji/emoji_one/information_desk_person.png differ diff --git a/plugins/emoji/public/images/unicode/2139.png b/public/images/emoji/emoji_one/information_source.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/2139.png rename to public/images/emoji/emoji_one/information_source.png diff --git a/public/images/emoji/emoji_one/innocent.png b/public/images/emoji/emoji_one/innocent.png new file mode 100644 index 0000000000..1633f48d36 Binary files /dev/null and b/public/images/emoji/emoji_one/innocent.png differ diff --git a/plugins/emoji/public/images/unicode/2049.png b/public/images/emoji/emoji_one/interrobang.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/2049.png rename to public/images/emoji/emoji_one/interrobang.png diff --git a/plugins/emoji/public/images/unicode/1f4f1.png b/public/images/emoji/emoji_one/iphone.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f4f1.png rename to public/images/emoji/emoji_one/iphone.png diff --git a/public/images/emoji/emoji_one/it.png b/public/images/emoji/emoji_one/it.png new file mode 100644 index 0000000000..bce0623505 Binary files /dev/null and b/public/images/emoji/emoji_one/it.png differ diff --git a/public/images/emoji/emoji_one/izakaya_lantern.png b/public/images/emoji/emoji_one/izakaya_lantern.png new file mode 100644 index 0000000000..acdca08909 Binary files /dev/null and b/public/images/emoji/emoji_one/izakaya_lantern.png differ diff --git a/public/images/emoji/emoji_one/jack_o_lantern.png b/public/images/emoji/emoji_one/jack_o_lantern.png new file mode 100644 index 0000000000..6471e1d824 Binary files /dev/null and b/public/images/emoji/emoji_one/jack_o_lantern.png differ diff --git a/public/images/emoji/emoji_one/japan.png b/public/images/emoji/emoji_one/japan.png new file mode 100644 index 0000000000..3e21b14db3 Binary files /dev/null and b/public/images/emoji/emoji_one/japan.png differ diff --git a/plugins/emoji/public/images/unicode/1f3ef.png b/public/images/emoji/emoji_one/japanese_castle.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f3ef.png rename to public/images/emoji/emoji_one/japanese_castle.png diff --git a/public/images/emoji/emoji_one/japanese_goblin.png b/public/images/emoji/emoji_one/japanese_goblin.png new file mode 100644 index 0000000000..20e1b4eb54 Binary files /dev/null and b/public/images/emoji/emoji_one/japanese_goblin.png differ diff --git a/public/images/emoji/emoji_one/japanese_ogre.png b/public/images/emoji/emoji_one/japanese_ogre.png new file mode 100644 index 0000000000..fd1cab65f8 Binary files /dev/null and b/public/images/emoji/emoji_one/japanese_ogre.png differ diff --git a/public/images/emoji/emoji_one/jeans.png b/public/images/emoji/emoji_one/jeans.png new file mode 100644 index 0000000000..713ccbcecf Binary files /dev/null and b/public/images/emoji/emoji_one/jeans.png differ diff --git a/public/images/emoji/emoji_one/joy.png b/public/images/emoji/emoji_one/joy.png new file mode 100644 index 0000000000..eef42396a8 Binary files /dev/null and b/public/images/emoji/emoji_one/joy.png differ diff --git a/public/images/emoji/emoji_one/joy_cat.png b/public/images/emoji/emoji_one/joy_cat.png new file mode 100644 index 0000000000..a9ec186250 Binary files /dev/null and b/public/images/emoji/emoji_one/joy_cat.png differ diff --git a/public/images/emoji/emoji_one/jp.png b/public/images/emoji/emoji_one/jp.png new file mode 100644 index 0000000000..5f779898b2 Binary files /dev/null and b/public/images/emoji/emoji_one/jp.png differ diff --git a/public/images/emoji/emoji_one/key.png b/public/images/emoji/emoji_one/key.png new file mode 100644 index 0000000000..d5182ccff1 Binary files /dev/null and b/public/images/emoji/emoji_one/key.png differ diff --git a/public/images/emoji/emoji_one/keycap_ten.png b/public/images/emoji/emoji_one/keycap_ten.png new file mode 100644 index 0000000000..7caf26665a Binary files /dev/null and b/public/images/emoji/emoji_one/keycap_ten.png differ diff --git a/public/images/emoji/emoji_one/kimono.png b/public/images/emoji/emoji_one/kimono.png new file mode 100644 index 0000000000..1cbdea0b17 Binary files /dev/null and b/public/images/emoji/emoji_one/kimono.png differ diff --git a/public/images/emoji/emoji_one/kiss.png b/public/images/emoji/emoji_one/kiss.png new file mode 100644 index 0000000000..bc4c8f73a3 Binary files /dev/null and b/public/images/emoji/emoji_one/kiss.png differ diff --git a/public/images/emoji/emoji_one/kissing.png b/public/images/emoji/emoji_one/kissing.png new file mode 100644 index 0000000000..beae104a69 Binary files /dev/null and b/public/images/emoji/emoji_one/kissing.png differ diff --git a/public/images/emoji/emoji_one/kissing_cat.png b/public/images/emoji/emoji_one/kissing_cat.png new file mode 100644 index 0000000000..3a58d5cfa7 Binary files /dev/null and b/public/images/emoji/emoji_one/kissing_cat.png differ diff --git a/public/images/emoji/emoji_one/kissing_closed_eyes.png b/public/images/emoji/emoji_one/kissing_closed_eyes.png new file mode 100644 index 0000000000..d27a678e7a Binary files /dev/null and b/public/images/emoji/emoji_one/kissing_closed_eyes.png differ diff --git a/public/images/emoji/emoji_one/kissing_heart.png b/public/images/emoji/emoji_one/kissing_heart.png new file mode 100644 index 0000000000..e4ca8e05d0 Binary files /dev/null and b/public/images/emoji/emoji_one/kissing_heart.png differ diff --git a/public/images/emoji/emoji_one/kissing_smiling_eyes.png b/public/images/emoji/emoji_one/kissing_smiling_eyes.png new file mode 100644 index 0000000000..732671be63 Binary files /dev/null and b/public/images/emoji/emoji_one/kissing_smiling_eyes.png differ diff --git a/public/images/emoji/emoji_one/knife.png b/public/images/emoji/emoji_one/knife.png new file mode 100644 index 0000000000..43249ba942 Binary files /dev/null and b/public/images/emoji/emoji_one/knife.png differ diff --git a/public/images/emoji/emoji_one/koala.png b/public/images/emoji/emoji_one/koala.png new file mode 100644 index 0000000000..9281439055 Binary files /dev/null and b/public/images/emoji/emoji_one/koala.png differ diff --git a/public/images/emoji/emoji_one/koko.png b/public/images/emoji/emoji_one/koko.png new file mode 100644 index 0000000000..c95a69a620 Binary files /dev/null and b/public/images/emoji/emoji_one/koko.png differ diff --git a/public/images/emoji/emoji_one/kr.png b/public/images/emoji/emoji_one/kr.png new file mode 100644 index 0000000000..f4f14acc0f Binary files /dev/null and b/public/images/emoji/emoji_one/kr.png differ diff --git a/public/images/emoji/emoji_one/lantern.png b/public/images/emoji/emoji_one/lantern.png new file mode 100644 index 0000000000..acdca08909 Binary files /dev/null and b/public/images/emoji/emoji_one/lantern.png differ diff --git a/public/images/emoji/emoji_one/large_blue_circle.png b/public/images/emoji/emoji_one/large_blue_circle.png new file mode 100644 index 0000000000..ea80b06cb2 Binary files /dev/null and b/public/images/emoji/emoji_one/large_blue_circle.png differ diff --git a/public/images/emoji/emoji_one/large_blue_diamond.png b/public/images/emoji/emoji_one/large_blue_diamond.png new file mode 100644 index 0000000000..266529e075 Binary files /dev/null and b/public/images/emoji/emoji_one/large_blue_diamond.png differ diff --git a/public/images/emoji/emoji_one/large_orange_diamond.png b/public/images/emoji/emoji_one/large_orange_diamond.png new file mode 100644 index 0000000000..b7d46226f6 Binary files /dev/null and b/public/images/emoji/emoji_one/large_orange_diamond.png differ diff --git a/public/images/emoji/emoji_one/last_quarter_moon.png b/public/images/emoji/emoji_one/last_quarter_moon.png new file mode 100644 index 0000000000..85e0df65a8 Binary files /dev/null and b/public/images/emoji/emoji_one/last_quarter_moon.png differ diff --git a/public/images/emoji/emoji_one/last_quarter_moon_with_face.png b/public/images/emoji/emoji_one/last_quarter_moon_with_face.png new file mode 100644 index 0000000000..37ecabbc7d Binary files /dev/null and b/public/images/emoji/emoji_one/last_quarter_moon_with_face.png differ diff --git a/public/images/emoji/emoji_one/laughing.png b/public/images/emoji/emoji_one/laughing.png new file mode 100644 index 0000000000..06adb559cd Binary files /dev/null and b/public/images/emoji/emoji_one/laughing.png differ diff --git a/public/images/emoji/emoji_one/leaves.png b/public/images/emoji/emoji_one/leaves.png new file mode 100644 index 0000000000..569a66fac2 Binary files /dev/null and b/public/images/emoji/emoji_one/leaves.png differ diff --git a/public/images/emoji/emoji_one/ledger.png b/public/images/emoji/emoji_one/ledger.png new file mode 100644 index 0000000000..04e7930af2 Binary files /dev/null and b/public/images/emoji/emoji_one/ledger.png differ diff --git a/public/images/emoji/emoji_one/left_luggage.png b/public/images/emoji/emoji_one/left_luggage.png new file mode 100644 index 0000000000..e8645bc2ea Binary files /dev/null and b/public/images/emoji/emoji_one/left_luggage.png differ diff --git a/public/images/emoji/emoji_one/left_right_arrow.png b/public/images/emoji/emoji_one/left_right_arrow.png new file mode 100644 index 0000000000..77a9e88d3c Binary files /dev/null and b/public/images/emoji/emoji_one/left_right_arrow.png differ diff --git a/public/images/emoji/emoji_one/leftwards_arrow_with_hook.png b/public/images/emoji/emoji_one/leftwards_arrow_with_hook.png new file mode 100644 index 0000000000..f5fb9718a5 Binary files /dev/null and b/public/images/emoji/emoji_one/leftwards_arrow_with_hook.png differ diff --git a/public/images/emoji/emoji_one/lemon.png b/public/images/emoji/emoji_one/lemon.png new file mode 100644 index 0000000000..fa5e87b030 Binary files /dev/null and b/public/images/emoji/emoji_one/lemon.png differ diff --git a/public/images/emoji/emoji_one/leo.png b/public/images/emoji/emoji_one/leo.png new file mode 100644 index 0000000000..8891081ff1 Binary files /dev/null and b/public/images/emoji/emoji_one/leo.png differ diff --git a/public/images/emoji/emoji_one/leopard.png b/public/images/emoji/emoji_one/leopard.png new file mode 100644 index 0000000000..eb16aa9c62 Binary files /dev/null and b/public/images/emoji/emoji_one/leopard.png differ diff --git a/public/images/emoji/emoji_one/libra.png b/public/images/emoji/emoji_one/libra.png new file mode 100644 index 0000000000..d601047945 Binary files /dev/null and b/public/images/emoji/emoji_one/libra.png differ diff --git a/public/images/emoji/emoji_one/light_rail.png b/public/images/emoji/emoji_one/light_rail.png new file mode 100644 index 0000000000..51bf05e278 Binary files /dev/null and b/public/images/emoji/emoji_one/light_rail.png differ diff --git a/public/images/emoji/emoji_one/link.png b/public/images/emoji/emoji_one/link.png new file mode 100644 index 0000000000..0954f494a9 Binary files /dev/null and b/public/images/emoji/emoji_one/link.png differ diff --git a/plugins/emoji/public/images/unicode/1f444.png b/public/images/emoji/emoji_one/lips.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f444.png rename to public/images/emoji/emoji_one/lips.png diff --git a/public/images/emoji/emoji_one/lipstick.png b/public/images/emoji/emoji_one/lipstick.png new file mode 100644 index 0000000000..ddf0baad76 Binary files /dev/null and b/public/images/emoji/emoji_one/lipstick.png differ diff --git a/public/images/emoji/emoji_one/lock.png b/public/images/emoji/emoji_one/lock.png new file mode 100644 index 0000000000..417050d0a8 Binary files /dev/null and b/public/images/emoji/emoji_one/lock.png differ diff --git a/public/images/emoji/emoji_one/lock_with_ink_pen.png b/public/images/emoji/emoji_one/lock_with_ink_pen.png new file mode 100644 index 0000000000..621fe24c2b Binary files /dev/null and b/public/images/emoji/emoji_one/lock_with_ink_pen.png differ diff --git a/public/images/emoji/emoji_one/lollipop.png b/public/images/emoji/emoji_one/lollipop.png new file mode 100644 index 0000000000..2c6a0ed4c3 Binary files /dev/null and b/public/images/emoji/emoji_one/lollipop.png differ diff --git a/public/images/emoji/emoji_one/loop.png b/public/images/emoji/emoji_one/loop.png new file mode 100644 index 0000000000..3b6f4933df Binary files /dev/null and b/public/images/emoji/emoji_one/loop.png differ diff --git a/public/images/emoji/emoji_one/loud_sound.png b/public/images/emoji/emoji_one/loud_sound.png new file mode 100644 index 0000000000..e84702e56d Binary files /dev/null and b/public/images/emoji/emoji_one/loud_sound.png differ diff --git a/public/images/emoji/emoji_one/loudspeaker.png b/public/images/emoji/emoji_one/loudspeaker.png new file mode 100644 index 0000000000..aaf588cd7b Binary files /dev/null and b/public/images/emoji/emoji_one/loudspeaker.png differ diff --git a/public/images/emoji/emoji_one/love_hotel.png b/public/images/emoji/emoji_one/love_hotel.png new file mode 100644 index 0000000000..d057d46ca2 Binary files /dev/null and b/public/images/emoji/emoji_one/love_hotel.png differ diff --git a/public/images/emoji/emoji_one/love_letter.png b/public/images/emoji/emoji_one/love_letter.png new file mode 100644 index 0000000000..e75e217da2 Binary files /dev/null and b/public/images/emoji/emoji_one/love_letter.png differ diff --git a/public/images/emoji/emoji_one/low_brightness.png b/public/images/emoji/emoji_one/low_brightness.png new file mode 100644 index 0000000000..8566d0381b Binary files /dev/null and b/public/images/emoji/emoji_one/low_brightness.png differ diff --git a/public/images/emoji/emoji_one/m.png b/public/images/emoji/emoji_one/m.png new file mode 100644 index 0000000000..2c72d4696d Binary files /dev/null and b/public/images/emoji/emoji_one/m.png differ diff --git a/public/images/emoji/emoji_one/mag.png b/public/images/emoji/emoji_one/mag.png new file mode 100644 index 0000000000..99982fbecf Binary files /dev/null and b/public/images/emoji/emoji_one/mag.png differ diff --git a/public/images/emoji/emoji_one/mag_right.png b/public/images/emoji/emoji_one/mag_right.png new file mode 100644 index 0000000000..95d12da0ee Binary files /dev/null and b/public/images/emoji/emoji_one/mag_right.png differ diff --git a/plugins/emoji/public/images/unicode/1f004.png b/public/images/emoji/emoji_one/mahjong.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f004.png rename to public/images/emoji/emoji_one/mahjong.png diff --git a/public/images/emoji/emoji_one/mailbox.png b/public/images/emoji/emoji_one/mailbox.png new file mode 100644 index 0000000000..2f805d4d7c Binary files /dev/null and b/public/images/emoji/emoji_one/mailbox.png differ diff --git a/plugins/emoji/public/images/unicode/1f4ea.png b/public/images/emoji/emoji_one/mailbox_closed.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f4ea.png rename to public/images/emoji/emoji_one/mailbox_closed.png diff --git a/public/images/emoji/emoji_one/mailbox_with_mail.png b/public/images/emoji/emoji_one/mailbox_with_mail.png new file mode 100644 index 0000000000..8803e087b6 Binary files /dev/null and b/public/images/emoji/emoji_one/mailbox_with_mail.png differ diff --git a/plugins/emoji/public/images/unicode/1f4ed.png b/public/images/emoji/emoji_one/mailbox_with_no_mail.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f4ed.png rename to public/images/emoji/emoji_one/mailbox_with_no_mail.png diff --git a/public/images/emoji/emoji_one/man.png b/public/images/emoji/emoji_one/man.png new file mode 100644 index 0000000000..cc67805eba Binary files /dev/null and b/public/images/emoji/emoji_one/man.png differ diff --git a/public/images/emoji/emoji_one/man_with_gua_pi_mao.png b/public/images/emoji/emoji_one/man_with_gua_pi_mao.png new file mode 100644 index 0000000000..feeca141c8 Binary files /dev/null and b/public/images/emoji/emoji_one/man_with_gua_pi_mao.png differ diff --git a/public/images/emoji/emoji_one/man_with_turban.png b/public/images/emoji/emoji_one/man_with_turban.png new file mode 100644 index 0000000000..1ea734de7b Binary files /dev/null and b/public/images/emoji/emoji_one/man_with_turban.png differ diff --git a/public/images/emoji/emoji_one/mans_shoe.png b/public/images/emoji/emoji_one/mans_shoe.png new file mode 100644 index 0000000000..8d8a9feeb2 Binary files /dev/null and b/public/images/emoji/emoji_one/mans_shoe.png differ diff --git a/public/images/emoji/emoji_one/maple_leaf.png b/public/images/emoji/emoji_one/maple_leaf.png new file mode 100644 index 0000000000..2756537f1a Binary files /dev/null and b/public/images/emoji/emoji_one/maple_leaf.png differ diff --git a/public/images/emoji/emoji_one/mask.png b/public/images/emoji/emoji_one/mask.png new file mode 100644 index 0000000000..bfcfad6cf0 Binary files /dev/null and b/public/images/emoji/emoji_one/mask.png differ diff --git a/public/images/emoji/emoji_one/massage.png b/public/images/emoji/emoji_one/massage.png new file mode 100644 index 0000000000..11ca3b5b66 Binary files /dev/null and b/public/images/emoji/emoji_one/massage.png differ diff --git a/public/images/emoji/emoji_one/meat_on_bone.png b/public/images/emoji/emoji_one/meat_on_bone.png new file mode 100644 index 0000000000..e9697fef17 Binary files /dev/null and b/public/images/emoji/emoji_one/meat_on_bone.png differ diff --git a/public/images/emoji/emoji_one/mega.png b/public/images/emoji/emoji_one/mega.png new file mode 100644 index 0000000000..66f741a831 Binary files /dev/null and b/public/images/emoji/emoji_one/mega.png differ diff --git a/public/images/emoji/emoji_one/melon.png b/public/images/emoji/emoji_one/melon.png new file mode 100644 index 0000000000..b27f00d254 Binary files /dev/null and b/public/images/emoji/emoji_one/melon.png differ diff --git a/public/images/emoji/emoji_one/memo.png b/public/images/emoji/emoji_one/memo.png new file mode 100644 index 0000000000..fd8f286091 Binary files /dev/null and b/public/images/emoji/emoji_one/memo.png differ diff --git a/public/images/emoji/emoji_one/mens.png b/public/images/emoji/emoji_one/mens.png new file mode 100644 index 0000000000..086526c208 Binary files /dev/null and b/public/images/emoji/emoji_one/mens.png differ diff --git a/public/images/emoji/emoji_one/metro.png b/public/images/emoji/emoji_one/metro.png new file mode 100644 index 0000000000..8900ba5740 Binary files /dev/null and b/public/images/emoji/emoji_one/metro.png differ diff --git a/public/images/emoji/emoji_one/microphone.png b/public/images/emoji/emoji_one/microphone.png new file mode 100644 index 0000000000..346b8967c9 Binary files /dev/null and b/public/images/emoji/emoji_one/microphone.png differ diff --git a/public/images/emoji/emoji_one/microscope.png b/public/images/emoji/emoji_one/microscope.png new file mode 100644 index 0000000000..944293d1ef Binary files /dev/null and b/public/images/emoji/emoji_one/microscope.png differ diff --git a/public/images/emoji/emoji_one/milky_way.png b/public/images/emoji/emoji_one/milky_way.png new file mode 100644 index 0000000000..2d95b96902 Binary files /dev/null and b/public/images/emoji/emoji_one/milky_way.png differ diff --git a/public/images/emoji/emoji_one/minibus.png b/public/images/emoji/emoji_one/minibus.png new file mode 100644 index 0000000000..462d0a3487 Binary files /dev/null and b/public/images/emoji/emoji_one/minibus.png differ diff --git a/plugins/emoji/public/images/unicode/1f4bd.png b/public/images/emoji/emoji_one/minidisc.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f4bd.png rename to public/images/emoji/emoji_one/minidisc.png diff --git a/public/images/emoji/emoji_one/mobile_phone_off.png b/public/images/emoji/emoji_one/mobile_phone_off.png new file mode 100644 index 0000000000..c62ea827fe Binary files /dev/null and b/public/images/emoji/emoji_one/mobile_phone_off.png differ diff --git a/public/images/emoji/emoji_one/money_with_wings.png b/public/images/emoji/emoji_one/money_with_wings.png new file mode 100644 index 0000000000..46591e443d Binary files /dev/null and b/public/images/emoji/emoji_one/money_with_wings.png differ diff --git a/public/images/emoji/emoji_one/moneybag.png b/public/images/emoji/emoji_one/moneybag.png new file mode 100644 index 0000000000..a44896ab0f Binary files /dev/null and b/public/images/emoji/emoji_one/moneybag.png differ diff --git a/public/images/emoji/emoji_one/monkey.png b/public/images/emoji/emoji_one/monkey.png new file mode 100644 index 0000000000..33e542ba67 Binary files /dev/null and b/public/images/emoji/emoji_one/monkey.png differ diff --git a/public/images/emoji/emoji_one/monkey_face.png b/public/images/emoji/emoji_one/monkey_face.png new file mode 100644 index 0000000000..a753bae1c9 Binary files /dev/null and b/public/images/emoji/emoji_one/monkey_face.png differ diff --git a/public/images/emoji/emoji_one/monorail.png b/public/images/emoji/emoji_one/monorail.png new file mode 100644 index 0000000000..3adeb964c4 Binary files /dev/null and b/public/images/emoji/emoji_one/monorail.png differ diff --git a/public/images/emoji/emoji_one/moon.png b/public/images/emoji/emoji_one/moon.png new file mode 100644 index 0000000000..b53fd42886 Binary files /dev/null and b/public/images/emoji/emoji_one/moon.png differ diff --git a/public/images/emoji/emoji_one/mortar_board.png b/public/images/emoji/emoji_one/mortar_board.png new file mode 100644 index 0000000000..4d5744f60d Binary files /dev/null and b/public/images/emoji/emoji_one/mortar_board.png differ diff --git a/plugins/emoji/public/images/unicode/1f5fb.png b/public/images/emoji/emoji_one/mount_fuji.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f5fb.png rename to public/images/emoji/emoji_one/mount_fuji.png diff --git a/public/images/emoji/emoji_one/mountain_bicyclist.png b/public/images/emoji/emoji_one/mountain_bicyclist.png new file mode 100644 index 0000000000..3645a6d83e Binary files /dev/null and b/public/images/emoji/emoji_one/mountain_bicyclist.png differ diff --git a/public/images/emoji/emoji_one/mountain_cableway.png b/public/images/emoji/emoji_one/mountain_cableway.png new file mode 100644 index 0000000000..6dbd7307e6 Binary files /dev/null and b/public/images/emoji/emoji_one/mountain_cableway.png differ diff --git a/public/images/emoji/emoji_one/mountain_railway.png b/public/images/emoji/emoji_one/mountain_railway.png new file mode 100644 index 0000000000..c69df1f8f5 Binary files /dev/null and b/public/images/emoji/emoji_one/mountain_railway.png differ diff --git a/public/images/emoji/emoji_one/mouse.png b/public/images/emoji/emoji_one/mouse.png new file mode 100644 index 0000000000..a8e5eb73fd Binary files /dev/null and b/public/images/emoji/emoji_one/mouse.png differ diff --git a/public/images/emoji/emoji_one/mouse2.png b/public/images/emoji/emoji_one/mouse2.png new file mode 100644 index 0000000000..5b1b1137fe Binary files /dev/null and b/public/images/emoji/emoji_one/mouse2.png differ diff --git a/public/images/emoji/emoji_one/movie_camera.png b/public/images/emoji/emoji_one/movie_camera.png new file mode 100644 index 0000000000..55f9b27538 Binary files /dev/null and b/public/images/emoji/emoji_one/movie_camera.png differ diff --git a/public/images/emoji/emoji_one/moyai.png b/public/images/emoji/emoji_one/moyai.png new file mode 100644 index 0000000000..247d34e258 Binary files /dev/null and b/public/images/emoji/emoji_one/moyai.png differ diff --git a/plugins/emoji/public/images/unicode/1f4aa.png b/public/images/emoji/emoji_one/muscle.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f4aa.png rename to public/images/emoji/emoji_one/muscle.png diff --git a/public/images/emoji/emoji_one/mushroom.png b/public/images/emoji/emoji_one/mushroom.png new file mode 100644 index 0000000000..b1673df26c Binary files /dev/null and b/public/images/emoji/emoji_one/mushroom.png differ diff --git a/plugins/emoji/public/images/unicode/1f3b9.png b/public/images/emoji/emoji_one/musical_keyboard.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f3b9.png rename to public/images/emoji/emoji_one/musical_keyboard.png diff --git a/public/images/emoji/emoji_one/musical_note.png b/public/images/emoji/emoji_one/musical_note.png new file mode 100644 index 0000000000..9a4606df01 Binary files /dev/null and b/public/images/emoji/emoji_one/musical_note.png differ diff --git a/public/images/emoji/emoji_one/musical_score.png b/public/images/emoji/emoji_one/musical_score.png new file mode 100644 index 0000000000..700bc6223d Binary files /dev/null and b/public/images/emoji/emoji_one/musical_score.png differ diff --git a/public/images/emoji/emoji_one/mute.png b/public/images/emoji/emoji_one/mute.png new file mode 100644 index 0000000000..ae7a757725 Binary files /dev/null and b/public/images/emoji/emoji_one/mute.png differ diff --git a/public/images/emoji/emoji_one/nail_care.png b/public/images/emoji/emoji_one/nail_care.png new file mode 100644 index 0000000000..279881c269 Binary files /dev/null and b/public/images/emoji/emoji_one/nail_care.png differ diff --git a/public/images/emoji/emoji_one/name_badge.png b/public/images/emoji/emoji_one/name_badge.png new file mode 100644 index 0000000000..7e023acb2c Binary files /dev/null and b/public/images/emoji/emoji_one/name_badge.png differ diff --git a/public/images/emoji/emoji_one/necktie.png b/public/images/emoji/emoji_one/necktie.png new file mode 100644 index 0000000000..3ed0339bfd Binary files /dev/null and b/public/images/emoji/emoji_one/necktie.png differ diff --git a/public/images/emoji/emoji_one/negative_squared_cross_mark.png b/public/images/emoji/emoji_one/negative_squared_cross_mark.png new file mode 100644 index 0000000000..2aba98954c Binary files /dev/null and b/public/images/emoji/emoji_one/negative_squared_cross_mark.png differ diff --git a/public/images/emoji/emoji_one/neutral_face.png b/public/images/emoji/emoji_one/neutral_face.png new file mode 100644 index 0000000000..929ef11a0b Binary files /dev/null and b/public/images/emoji/emoji_one/neutral_face.png differ diff --git a/public/images/emoji/emoji_one/new.png b/public/images/emoji/emoji_one/new.png new file mode 100644 index 0000000000..22dab436b2 Binary files /dev/null and b/public/images/emoji/emoji_one/new.png differ diff --git a/plugins/emoji/public/images/unicode/1f311.png b/public/images/emoji/emoji_one/new_moon.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f311.png rename to public/images/emoji/emoji_one/new_moon.png diff --git a/public/images/emoji/emoji_one/new_moon_with_face.png b/public/images/emoji/emoji_one/new_moon_with_face.png new file mode 100644 index 0000000000..c7b2e8011a Binary files /dev/null and b/public/images/emoji/emoji_one/new_moon_with_face.png differ diff --git a/public/images/emoji/emoji_one/newspaper.png b/public/images/emoji/emoji_one/newspaper.png new file mode 100644 index 0000000000..ae5e314945 Binary files /dev/null and b/public/images/emoji/emoji_one/newspaper.png differ diff --git a/public/images/emoji/emoji_one/ng.png b/public/images/emoji/emoji_one/ng.png new file mode 100644 index 0000000000..2925a559a6 Binary files /dev/null and b/public/images/emoji/emoji_one/ng.png differ diff --git a/public/images/emoji/emoji_one/night_with_stars.png b/public/images/emoji/emoji_one/night_with_stars.png new file mode 100644 index 0000000000..bf9a44ab6c Binary files /dev/null and b/public/images/emoji/emoji_one/night_with_stars.png differ diff --git a/public/images/emoji/emoji_one/nine.png b/public/images/emoji/emoji_one/nine.png new file mode 100644 index 0000000000..5ae453155b Binary files /dev/null and b/public/images/emoji/emoji_one/nine.png differ diff --git a/public/images/emoji/emoji_one/no_bell.png b/public/images/emoji/emoji_one/no_bell.png new file mode 100644 index 0000000000..30e8858aaf Binary files /dev/null and b/public/images/emoji/emoji_one/no_bell.png differ diff --git a/public/images/emoji/emoji_one/no_bicycles.png b/public/images/emoji/emoji_one/no_bicycles.png new file mode 100644 index 0000000000..222ab031d5 Binary files /dev/null and b/public/images/emoji/emoji_one/no_bicycles.png differ diff --git a/public/images/emoji/emoji_one/no_entry.png b/public/images/emoji/emoji_one/no_entry.png new file mode 100644 index 0000000000..2c49af1c56 Binary files /dev/null and b/public/images/emoji/emoji_one/no_entry.png differ diff --git a/public/images/emoji/emoji_one/no_entry_sign.png b/public/images/emoji/emoji_one/no_entry_sign.png new file mode 100644 index 0000000000..6577a1058f Binary files /dev/null and b/public/images/emoji/emoji_one/no_entry_sign.png differ diff --git a/public/images/emoji/emoji_one/no_good.png b/public/images/emoji/emoji_one/no_good.png new file mode 100644 index 0000000000..e6463ef847 Binary files /dev/null and b/public/images/emoji/emoji_one/no_good.png differ diff --git a/plugins/emoji/public/images/unicode/1f4f5.png b/public/images/emoji/emoji_one/no_mobile_phones.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f4f5.png rename to public/images/emoji/emoji_one/no_mobile_phones.png diff --git a/public/images/emoji/emoji_one/no_mouth.png b/public/images/emoji/emoji_one/no_mouth.png new file mode 100644 index 0000000000..2ac7ab19b7 Binary files /dev/null and b/public/images/emoji/emoji_one/no_mouth.png differ diff --git a/public/images/emoji/emoji_one/no_pedestrians.png b/public/images/emoji/emoji_one/no_pedestrians.png new file mode 100644 index 0000000000..9337d8f31a Binary files /dev/null and b/public/images/emoji/emoji_one/no_pedestrians.png differ diff --git a/public/images/emoji/emoji_one/no_smoking.png b/public/images/emoji/emoji_one/no_smoking.png new file mode 100644 index 0000000000..1f70c8fa66 Binary files /dev/null and b/public/images/emoji/emoji_one/no_smoking.png differ diff --git a/public/images/emoji/emoji_one/non-potable_water.png b/public/images/emoji/emoji_one/non-potable_water.png new file mode 100644 index 0000000000..45c12a545d Binary files /dev/null and b/public/images/emoji/emoji_one/non-potable_water.png differ diff --git a/public/images/emoji/emoji_one/nose.png b/public/images/emoji/emoji_one/nose.png new file mode 100644 index 0000000000..729535807c Binary files /dev/null and b/public/images/emoji/emoji_one/nose.png differ diff --git a/public/images/emoji/emoji_one/notebook.png b/public/images/emoji/emoji_one/notebook.png new file mode 100644 index 0000000000..6ab5257a8c Binary files /dev/null and b/public/images/emoji/emoji_one/notebook.png differ diff --git a/public/images/emoji/emoji_one/notebook_with_decorative_cover.png b/public/images/emoji/emoji_one/notebook_with_decorative_cover.png new file mode 100644 index 0000000000..8cc7fe68ff Binary files /dev/null and b/public/images/emoji/emoji_one/notebook_with_decorative_cover.png differ diff --git a/public/images/emoji/emoji_one/notes.png b/public/images/emoji/emoji_one/notes.png new file mode 100644 index 0000000000..68f5fb7a98 Binary files /dev/null and b/public/images/emoji/emoji_one/notes.png differ diff --git a/public/images/emoji/emoji_one/nut_and_bolt.png b/public/images/emoji/emoji_one/nut_and_bolt.png new file mode 100644 index 0000000000..7b0024e06f Binary files /dev/null and b/public/images/emoji/emoji_one/nut_and_bolt.png differ diff --git a/public/images/emoji/emoji_one/o.png b/public/images/emoji/emoji_one/o.png new file mode 100644 index 0000000000..f141dc4529 Binary files /dev/null and b/public/images/emoji/emoji_one/o.png differ diff --git a/public/images/emoji/emoji_one/o2.png b/public/images/emoji/emoji_one/o2.png new file mode 100644 index 0000000000..0537a9ad0f Binary files /dev/null and b/public/images/emoji/emoji_one/o2.png differ diff --git a/public/images/emoji/emoji_one/ocean.png b/public/images/emoji/emoji_one/ocean.png new file mode 100644 index 0000000000..172dfffdfb Binary files /dev/null and b/public/images/emoji/emoji_one/ocean.png differ diff --git a/public/images/emoji/emoji_one/octopus.png b/public/images/emoji/emoji_one/octopus.png new file mode 100644 index 0000000000..8add7143eb Binary files /dev/null and b/public/images/emoji/emoji_one/octopus.png differ diff --git a/public/images/emoji/emoji_one/oden.png b/public/images/emoji/emoji_one/oden.png new file mode 100644 index 0000000000..8ac803517b Binary files /dev/null and b/public/images/emoji/emoji_one/oden.png differ diff --git a/public/images/emoji/emoji_one/office.png b/public/images/emoji/emoji_one/office.png new file mode 100644 index 0000000000..9b06c86fac Binary files /dev/null and b/public/images/emoji/emoji_one/office.png differ diff --git a/public/images/emoji/emoji_one/ok.png b/public/images/emoji/emoji_one/ok.png new file mode 100644 index 0000000000..8acef8cce9 Binary files /dev/null and b/public/images/emoji/emoji_one/ok.png differ diff --git a/plugins/emoji/public/images/unicode/1f44c.png b/public/images/emoji/emoji_one/ok_hand.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f44c.png rename to public/images/emoji/emoji_one/ok_hand.png diff --git a/public/images/emoji/emoji_one/ok_woman.png b/public/images/emoji/emoji_one/ok_woman.png new file mode 100644 index 0000000000..4bb5176c54 Binary files /dev/null and b/public/images/emoji/emoji_one/ok_woman.png differ diff --git a/public/images/emoji/emoji_one/older_man.png b/public/images/emoji/emoji_one/older_man.png new file mode 100644 index 0000000000..66fd194007 Binary files /dev/null and b/public/images/emoji/emoji_one/older_man.png differ diff --git a/public/images/emoji/emoji_one/older_woman.png b/public/images/emoji/emoji_one/older_woman.png new file mode 100644 index 0000000000..e2c5bc72f4 Binary files /dev/null and b/public/images/emoji/emoji_one/older_woman.png differ diff --git a/public/images/emoji/emoji_one/on.png b/public/images/emoji/emoji_one/on.png new file mode 100644 index 0000000000..bdb75220de Binary files /dev/null and b/public/images/emoji/emoji_one/on.png differ diff --git a/public/images/emoji/emoji_one/oncoming_automobile.png b/public/images/emoji/emoji_one/oncoming_automobile.png new file mode 100644 index 0000000000..b9b0dafbf5 Binary files /dev/null and b/public/images/emoji/emoji_one/oncoming_automobile.png differ diff --git a/public/images/emoji/emoji_one/oncoming_bus.png b/public/images/emoji/emoji_one/oncoming_bus.png new file mode 100644 index 0000000000..89381abf96 Binary files /dev/null and b/public/images/emoji/emoji_one/oncoming_bus.png differ diff --git a/public/images/emoji/emoji_one/oncoming_police_car.png b/public/images/emoji/emoji_one/oncoming_police_car.png new file mode 100644 index 0000000000..2ab9c3302e Binary files /dev/null and b/public/images/emoji/emoji_one/oncoming_police_car.png differ diff --git a/public/images/emoji/emoji_one/oncoming_taxi.png b/public/images/emoji/emoji_one/oncoming_taxi.png new file mode 100644 index 0000000000..806e3958f8 Binary files /dev/null and b/public/images/emoji/emoji_one/oncoming_taxi.png differ diff --git a/plugins/emoji/public/images/unicode/0031-20E3.png b/public/images/emoji/emoji_one/one.png similarity index 100% rename from plugins/emoji/public/images/unicode/0031-20E3.png rename to public/images/emoji/emoji_one/one.png diff --git a/public/images/emoji/emoji_one/open_book.png b/public/images/emoji/emoji_one/open_book.png new file mode 100644 index 0000000000..158ba4965d Binary files /dev/null and b/public/images/emoji/emoji_one/open_book.png differ diff --git a/public/images/emoji/emoji_one/open_file_folder.png b/public/images/emoji/emoji_one/open_file_folder.png new file mode 100644 index 0000000000..073afe5fe6 Binary files /dev/null and b/public/images/emoji/emoji_one/open_file_folder.png differ diff --git a/public/images/emoji/emoji_one/open_hands.png b/public/images/emoji/emoji_one/open_hands.png new file mode 100644 index 0000000000..c8eb0bab52 Binary files /dev/null and b/public/images/emoji/emoji_one/open_hands.png differ diff --git a/public/images/emoji/emoji_one/open_mouth.png b/public/images/emoji/emoji_one/open_mouth.png new file mode 100644 index 0000000000..f1759e1146 Binary files /dev/null and b/public/images/emoji/emoji_one/open_mouth.png differ diff --git a/public/images/emoji/emoji_one/ophiuchus.png b/public/images/emoji/emoji_one/ophiuchus.png new file mode 100644 index 0000000000..5c34933519 Binary files /dev/null and b/public/images/emoji/emoji_one/ophiuchus.png differ diff --git a/public/images/emoji/emoji_one/orange_book.png b/public/images/emoji/emoji_one/orange_book.png new file mode 100644 index 0000000000..3b5fc46e59 Binary files /dev/null and b/public/images/emoji/emoji_one/orange_book.png differ diff --git a/public/images/emoji/emoji_one/outbox_tray.png b/public/images/emoji/emoji_one/outbox_tray.png new file mode 100644 index 0000000000..c5e55e33ef Binary files /dev/null and b/public/images/emoji/emoji_one/outbox_tray.png differ diff --git a/public/images/emoji/emoji_one/ox.png b/public/images/emoji/emoji_one/ox.png new file mode 100644 index 0000000000..e1688630c4 Binary files /dev/null and b/public/images/emoji/emoji_one/ox.png differ diff --git a/public/images/emoji/emoji_one/package.png b/public/images/emoji/emoji_one/package.png new file mode 100644 index 0000000000..424c4fd193 Binary files /dev/null and b/public/images/emoji/emoji_one/package.png differ diff --git a/public/images/emoji/emoji_one/page_facing_up.png b/public/images/emoji/emoji_one/page_facing_up.png new file mode 100644 index 0000000000..674077a224 Binary files /dev/null and b/public/images/emoji/emoji_one/page_facing_up.png differ diff --git a/plugins/emoji/public/images/unicode/1f4c3.png b/public/images/emoji/emoji_one/page_with_curl.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f4c3.png rename to public/images/emoji/emoji_one/page_with_curl.png diff --git a/public/images/emoji/emoji_one/pager.png b/public/images/emoji/emoji_one/pager.png new file mode 100644 index 0000000000..eba5f9f167 Binary files /dev/null and b/public/images/emoji/emoji_one/pager.png differ diff --git a/public/images/emoji/emoji_one/palm_tree.png b/public/images/emoji/emoji_one/palm_tree.png new file mode 100644 index 0000000000..b18cffc943 Binary files /dev/null and b/public/images/emoji/emoji_one/palm_tree.png differ diff --git a/public/images/emoji/emoji_one/panda_face.png b/public/images/emoji/emoji_one/panda_face.png new file mode 100644 index 0000000000..c10d77c23f Binary files /dev/null and b/public/images/emoji/emoji_one/panda_face.png differ diff --git a/public/images/emoji/emoji_one/paperclip.png b/public/images/emoji/emoji_one/paperclip.png new file mode 100644 index 0000000000..9b5af1de6b Binary files /dev/null and b/public/images/emoji/emoji_one/paperclip.png differ diff --git a/plugins/emoji/public/images/unicode/1f17f.png b/public/images/emoji/emoji_one/parking.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f17f.png rename to public/images/emoji/emoji_one/parking.png diff --git a/public/images/emoji/emoji_one/part_alternation_mark.png b/public/images/emoji/emoji_one/part_alternation_mark.png new file mode 100644 index 0000000000..e348a58d1e Binary files /dev/null and b/public/images/emoji/emoji_one/part_alternation_mark.png differ diff --git a/public/images/emoji/emoji_one/partly_sunny.png b/public/images/emoji/emoji_one/partly_sunny.png new file mode 100644 index 0000000000..a5c8d31b84 Binary files /dev/null and b/public/images/emoji/emoji_one/partly_sunny.png differ diff --git a/public/images/emoji/emoji_one/passport_control.png b/public/images/emoji/emoji_one/passport_control.png new file mode 100644 index 0000000000..8a72a80ad0 Binary files /dev/null and b/public/images/emoji/emoji_one/passport_control.png differ diff --git a/public/images/emoji/emoji_one/paw_prints.png b/public/images/emoji/emoji_one/paw_prints.png new file mode 100644 index 0000000000..970366ce19 Binary files /dev/null and b/public/images/emoji/emoji_one/paw_prints.png differ diff --git a/public/images/emoji/emoji_one/peach.png b/public/images/emoji/emoji_one/peach.png new file mode 100644 index 0000000000..b7250fed91 Binary files /dev/null and b/public/images/emoji/emoji_one/peach.png differ diff --git a/public/images/emoji/emoji_one/pear.png b/public/images/emoji/emoji_one/pear.png new file mode 100644 index 0000000000..7f6d05ee61 Binary files /dev/null and b/public/images/emoji/emoji_one/pear.png differ diff --git a/public/images/emoji/emoji_one/pencil.png b/public/images/emoji/emoji_one/pencil.png new file mode 100644 index 0000000000..fd8f286091 Binary files /dev/null and b/public/images/emoji/emoji_one/pencil.png differ diff --git a/public/images/emoji/emoji_one/pencil2.png b/public/images/emoji/emoji_one/pencil2.png new file mode 100644 index 0000000000..693ee8429f Binary files /dev/null and b/public/images/emoji/emoji_one/pencil2.png differ diff --git a/public/images/emoji/emoji_one/penguin.png b/public/images/emoji/emoji_one/penguin.png new file mode 100644 index 0000000000..1789bf2222 Binary files /dev/null and b/public/images/emoji/emoji_one/penguin.png differ diff --git a/public/images/emoji/emoji_one/pensive.png b/public/images/emoji/emoji_one/pensive.png new file mode 100644 index 0000000000..60add1c1d0 Binary files /dev/null and b/public/images/emoji/emoji_one/pensive.png differ diff --git a/public/images/emoji/emoji_one/performing_arts.png b/public/images/emoji/emoji_one/performing_arts.png new file mode 100644 index 0000000000..97a5bb7fea Binary files /dev/null and b/public/images/emoji/emoji_one/performing_arts.png differ diff --git a/public/images/emoji/emoji_one/persevere.png b/public/images/emoji/emoji_one/persevere.png new file mode 100644 index 0000000000..83b08ba35b Binary files /dev/null and b/public/images/emoji/emoji_one/persevere.png differ diff --git a/public/images/emoji/emoji_one/person_frowning.png b/public/images/emoji/emoji_one/person_frowning.png new file mode 100644 index 0000000000..19377e9eb6 Binary files /dev/null and b/public/images/emoji/emoji_one/person_frowning.png differ diff --git a/public/images/emoji/emoji_one/person_with_blond_hair.png b/public/images/emoji/emoji_one/person_with_blond_hair.png new file mode 100644 index 0000000000..eff188afa4 Binary files /dev/null and b/public/images/emoji/emoji_one/person_with_blond_hair.png differ diff --git a/public/images/emoji/emoji_one/person_with_pouting_face.png b/public/images/emoji/emoji_one/person_with_pouting_face.png new file mode 100644 index 0000000000..92ea6a97a8 Binary files /dev/null and b/public/images/emoji/emoji_one/person_with_pouting_face.png differ diff --git a/public/images/emoji/emoji_one/phone.png b/public/images/emoji/emoji_one/phone.png new file mode 100644 index 0000000000..6a14f1bfe9 Binary files /dev/null and b/public/images/emoji/emoji_one/phone.png differ diff --git a/public/images/emoji/emoji_one/pig.png b/public/images/emoji/emoji_one/pig.png new file mode 100644 index 0000000000..e2ef71cc01 Binary files /dev/null and b/public/images/emoji/emoji_one/pig.png differ diff --git a/public/images/emoji/emoji_one/pig2.png b/public/images/emoji/emoji_one/pig2.png new file mode 100644 index 0000000000..de1147954f Binary files /dev/null and b/public/images/emoji/emoji_one/pig2.png differ diff --git a/public/images/emoji/emoji_one/pig_nose.png b/public/images/emoji/emoji_one/pig_nose.png new file mode 100644 index 0000000000..84500633ea Binary files /dev/null and b/public/images/emoji/emoji_one/pig_nose.png differ diff --git a/public/images/emoji/emoji_one/pill.png b/public/images/emoji/emoji_one/pill.png new file mode 100644 index 0000000000..5db2699c47 Binary files /dev/null and b/public/images/emoji/emoji_one/pill.png differ diff --git a/public/images/emoji/emoji_one/pineapple.png b/public/images/emoji/emoji_one/pineapple.png new file mode 100644 index 0000000000..b9b9f8827a Binary files /dev/null and b/public/images/emoji/emoji_one/pineapple.png differ diff --git a/public/images/emoji/emoji_one/pisces.png b/public/images/emoji/emoji_one/pisces.png new file mode 100644 index 0000000000..5762537907 Binary files /dev/null and b/public/images/emoji/emoji_one/pisces.png differ diff --git a/public/images/emoji/emoji_one/pizza.png b/public/images/emoji/emoji_one/pizza.png new file mode 100644 index 0000000000..2e3ff79895 Binary files /dev/null and b/public/images/emoji/emoji_one/pizza.png differ diff --git a/public/images/emoji/emoji_one/point_down.png b/public/images/emoji/emoji_one/point_down.png new file mode 100644 index 0000000000..bbd564336c Binary files /dev/null and b/public/images/emoji/emoji_one/point_down.png differ diff --git a/public/images/emoji/emoji_one/point_left.png b/public/images/emoji/emoji_one/point_left.png new file mode 100644 index 0000000000..9503416b0e Binary files /dev/null and b/public/images/emoji/emoji_one/point_left.png differ diff --git a/public/images/emoji/emoji_one/point_right.png b/public/images/emoji/emoji_one/point_right.png new file mode 100644 index 0000000000..1d7e704a5f Binary files /dev/null and b/public/images/emoji/emoji_one/point_right.png differ diff --git a/plugins/emoji/public/images/unicode/261d.png b/public/images/emoji/emoji_one/point_up.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/261d.png rename to public/images/emoji/emoji_one/point_up.png diff --git a/public/images/emoji/emoji_one/point_up_2.png b/public/images/emoji/emoji_one/point_up_2.png new file mode 100644 index 0000000000..b85e5aab3e Binary files /dev/null and b/public/images/emoji/emoji_one/point_up_2.png differ diff --git a/public/images/emoji/emoji_one/police_car.png b/public/images/emoji/emoji_one/police_car.png new file mode 100644 index 0000000000..4281ed3bb5 Binary files /dev/null and b/public/images/emoji/emoji_one/police_car.png differ diff --git a/public/images/emoji/emoji_one/poodle.png b/public/images/emoji/emoji_one/poodle.png new file mode 100644 index 0000000000..a69f6388d2 Binary files /dev/null and b/public/images/emoji/emoji_one/poodle.png differ diff --git a/public/images/emoji/emoji_one/poop.png b/public/images/emoji/emoji_one/poop.png new file mode 100644 index 0000000000..0c237a7881 Binary files /dev/null and b/public/images/emoji/emoji_one/poop.png differ diff --git a/plugins/emoji/public/images/unicode/1f3e3.png b/public/images/emoji/emoji_one/post_office.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f3e3.png rename to public/images/emoji/emoji_one/post_office.png diff --git a/public/images/emoji/emoji_one/postal_horn.png b/public/images/emoji/emoji_one/postal_horn.png new file mode 100644 index 0000000000..2313eeb452 Binary files /dev/null and b/public/images/emoji/emoji_one/postal_horn.png differ diff --git a/plugins/emoji/public/images/unicode/1f4ee.png b/public/images/emoji/emoji_one/postbox.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f4ee.png rename to public/images/emoji/emoji_one/postbox.png diff --git a/public/images/emoji/emoji_one/potable_water.png b/public/images/emoji/emoji_one/potable_water.png new file mode 100644 index 0000000000..08ee1b5080 Binary files /dev/null and b/public/images/emoji/emoji_one/potable_water.png differ diff --git a/public/images/emoji/emoji_one/pouch.png b/public/images/emoji/emoji_one/pouch.png new file mode 100644 index 0000000000..d8e54dd7c9 Binary files /dev/null and b/public/images/emoji/emoji_one/pouch.png differ diff --git a/public/images/emoji/emoji_one/poultry_leg.png b/public/images/emoji/emoji_one/poultry_leg.png new file mode 100644 index 0000000000..eafba9a3e3 Binary files /dev/null and b/public/images/emoji/emoji_one/poultry_leg.png differ diff --git a/public/images/emoji/emoji_one/pound.png b/public/images/emoji/emoji_one/pound.png new file mode 100644 index 0000000000..3ac812ff40 Binary files /dev/null and b/public/images/emoji/emoji_one/pound.png differ diff --git a/public/images/emoji/emoji_one/pouting_cat.png b/public/images/emoji/emoji_one/pouting_cat.png new file mode 100644 index 0000000000..342a1198bb Binary files /dev/null and b/public/images/emoji/emoji_one/pouting_cat.png differ diff --git a/public/images/emoji/emoji_one/pray.png b/public/images/emoji/emoji_one/pray.png new file mode 100644 index 0000000000..faf1944768 Binary files /dev/null and b/public/images/emoji/emoji_one/pray.png differ diff --git a/public/images/emoji/emoji_one/princess.png b/public/images/emoji/emoji_one/princess.png new file mode 100644 index 0000000000..360d75eb90 Binary files /dev/null and b/public/images/emoji/emoji_one/princess.png differ diff --git a/public/images/emoji/emoji_one/punch.png b/public/images/emoji/emoji_one/punch.png new file mode 100644 index 0000000000..a616ffee19 Binary files /dev/null and b/public/images/emoji/emoji_one/punch.png differ diff --git a/plugins/emoji/public/images/unicode/1f49c.png b/public/images/emoji/emoji_one/purple_heart.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f49c.png rename to public/images/emoji/emoji_one/purple_heart.png diff --git a/public/images/emoji/emoji_one/purse.png b/public/images/emoji/emoji_one/purse.png new file mode 100644 index 0000000000..da09a41a3e Binary files /dev/null and b/public/images/emoji/emoji_one/purse.png differ diff --git a/public/images/emoji/emoji_one/pushpin.png b/public/images/emoji/emoji_one/pushpin.png new file mode 100644 index 0000000000..557ac45f0c Binary files /dev/null and b/public/images/emoji/emoji_one/pushpin.png differ diff --git a/public/images/emoji/emoji_one/put_litter_in_its_place.png b/public/images/emoji/emoji_one/put_litter_in_its_place.png new file mode 100644 index 0000000000..19267a1de2 Binary files /dev/null and b/public/images/emoji/emoji_one/put_litter_in_its_place.png differ diff --git a/plugins/emoji/public/images/unicode/2753.png b/public/images/emoji/emoji_one/question.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/2753.png rename to public/images/emoji/emoji_one/question.png diff --git a/public/images/emoji/emoji_one/rabbit.png b/public/images/emoji/emoji_one/rabbit.png new file mode 100644 index 0000000000..e6203c0523 Binary files /dev/null and b/public/images/emoji/emoji_one/rabbit.png differ diff --git a/public/images/emoji/emoji_one/rabbit2.png b/public/images/emoji/emoji_one/rabbit2.png new file mode 100644 index 0000000000..ab2fae0fc7 Binary files /dev/null and b/public/images/emoji/emoji_one/rabbit2.png differ diff --git a/public/images/emoji/emoji_one/racehorse.png b/public/images/emoji/emoji_one/racehorse.png new file mode 100644 index 0000000000..fadf8f2bf0 Binary files /dev/null and b/public/images/emoji/emoji_one/racehorse.png differ diff --git a/public/images/emoji/emoji_one/radio.png b/public/images/emoji/emoji_one/radio.png new file mode 100644 index 0000000000..dedc50fe9b Binary files /dev/null and b/public/images/emoji/emoji_one/radio.png differ diff --git a/plugins/emoji/public/images/unicode/1f518.png b/public/images/emoji/emoji_one/radio_button.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f518.png rename to public/images/emoji/emoji_one/radio_button.png diff --git a/public/images/emoji/emoji_one/rage.png b/public/images/emoji/emoji_one/rage.png new file mode 100644 index 0000000000..f62d48c494 Binary files /dev/null and b/public/images/emoji/emoji_one/rage.png differ diff --git a/public/images/emoji/emoji_one/railway_car.png b/public/images/emoji/emoji_one/railway_car.png new file mode 100644 index 0000000000..82d8088665 Binary files /dev/null and b/public/images/emoji/emoji_one/railway_car.png differ diff --git a/public/images/emoji/emoji_one/rainbow.png b/public/images/emoji/emoji_one/rainbow.png new file mode 100644 index 0000000000..eb539c5bed Binary files /dev/null and b/public/images/emoji/emoji_one/rainbow.png differ diff --git a/public/images/emoji/emoji_one/raised_hand.png b/public/images/emoji/emoji_one/raised_hand.png new file mode 100644 index 0000000000..4f1fce256e Binary files /dev/null and b/public/images/emoji/emoji_one/raised_hand.png differ diff --git a/public/images/emoji/emoji_one/raised_hands.png b/public/images/emoji/emoji_one/raised_hands.png new file mode 100644 index 0000000000..8dbc25f830 Binary files /dev/null and b/public/images/emoji/emoji_one/raised_hands.png differ diff --git a/public/images/emoji/emoji_one/raising_hand.png b/public/images/emoji/emoji_one/raising_hand.png new file mode 100644 index 0000000000..9d98b8d9c5 Binary files /dev/null and b/public/images/emoji/emoji_one/raising_hand.png differ diff --git a/public/images/emoji/emoji_one/ram.png b/public/images/emoji/emoji_one/ram.png new file mode 100644 index 0000000000..17af1b9a58 Binary files /dev/null and b/public/images/emoji/emoji_one/ram.png differ diff --git a/public/images/emoji/emoji_one/ramen.png b/public/images/emoji/emoji_one/ramen.png new file mode 100644 index 0000000000..7878275665 Binary files /dev/null and b/public/images/emoji/emoji_one/ramen.png differ diff --git a/public/images/emoji/emoji_one/rat.png b/public/images/emoji/emoji_one/rat.png new file mode 100644 index 0000000000..58c2d79d88 Binary files /dev/null and b/public/images/emoji/emoji_one/rat.png differ diff --git a/public/images/emoji/emoji_one/recycle.png b/public/images/emoji/emoji_one/recycle.png new file mode 100644 index 0000000000..181391e861 Binary files /dev/null and b/public/images/emoji/emoji_one/recycle.png differ diff --git a/public/images/emoji/emoji_one/red_car.png b/public/images/emoji/emoji_one/red_car.png new file mode 100644 index 0000000000..e266bd21e5 Binary files /dev/null and b/public/images/emoji/emoji_one/red_car.png differ diff --git a/public/images/emoji/emoji_one/red_circle.png b/public/images/emoji/emoji_one/red_circle.png new file mode 100644 index 0000000000..bf181cc567 Binary files /dev/null and b/public/images/emoji/emoji_one/red_circle.png differ diff --git a/public/images/emoji/emoji_one/registered.png b/public/images/emoji/emoji_one/registered.png new file mode 100644 index 0000000000..9776d66f85 Binary files /dev/null and b/public/images/emoji/emoji_one/registered.png differ diff --git a/public/images/emoji/emoji_one/relaxed.png b/public/images/emoji/emoji_one/relaxed.png new file mode 100644 index 0000000000..ec92098499 Binary files /dev/null and b/public/images/emoji/emoji_one/relaxed.png differ diff --git a/public/images/emoji/emoji_one/relieved.png b/public/images/emoji/emoji_one/relieved.png new file mode 100644 index 0000000000..c011b974dd Binary files /dev/null and b/public/images/emoji/emoji_one/relieved.png differ diff --git a/public/images/emoji/emoji_one/repeat.png b/public/images/emoji/emoji_one/repeat.png new file mode 100644 index 0000000000..edd87a8b20 Binary files /dev/null and b/public/images/emoji/emoji_one/repeat.png differ diff --git a/public/images/emoji/emoji_one/repeat_one.png b/public/images/emoji/emoji_one/repeat_one.png new file mode 100644 index 0000000000..5019980878 Binary files /dev/null and b/public/images/emoji/emoji_one/repeat_one.png differ diff --git a/public/images/emoji/emoji_one/restroom.png b/public/images/emoji/emoji_one/restroom.png new file mode 100644 index 0000000000..c8b338688a Binary files /dev/null and b/public/images/emoji/emoji_one/restroom.png differ diff --git a/public/images/emoji/emoji_one/revolving_hearts.png b/public/images/emoji/emoji_one/revolving_hearts.png new file mode 100644 index 0000000000..0602d03c11 Binary files /dev/null and b/public/images/emoji/emoji_one/revolving_hearts.png differ diff --git a/public/images/emoji/emoji_one/rewind.png b/public/images/emoji/emoji_one/rewind.png new file mode 100644 index 0000000000..18aae0affc Binary files /dev/null and b/public/images/emoji/emoji_one/rewind.png differ diff --git a/public/images/emoji/emoji_one/ribbon.png b/public/images/emoji/emoji_one/ribbon.png new file mode 100644 index 0000000000..0cb50b38ca Binary files /dev/null and b/public/images/emoji/emoji_one/ribbon.png differ diff --git a/public/images/emoji/emoji_one/rice.png b/public/images/emoji/emoji_one/rice.png new file mode 100644 index 0000000000..15358ae021 Binary files /dev/null and b/public/images/emoji/emoji_one/rice.png differ diff --git a/public/images/emoji/emoji_one/rice_ball.png b/public/images/emoji/emoji_one/rice_ball.png new file mode 100644 index 0000000000..4a24a91e28 Binary files /dev/null and b/public/images/emoji/emoji_one/rice_ball.png differ diff --git a/public/images/emoji/emoji_one/rice_cracker.png b/public/images/emoji/emoji_one/rice_cracker.png new file mode 100644 index 0000000000..974d205ed8 Binary files /dev/null and b/public/images/emoji/emoji_one/rice_cracker.png differ diff --git a/public/images/emoji/emoji_one/rice_scene.png b/public/images/emoji/emoji_one/rice_scene.png new file mode 100644 index 0000000000..d68d294f0b Binary files /dev/null and b/public/images/emoji/emoji_one/rice_scene.png differ diff --git a/public/images/emoji/emoji_one/ring.png b/public/images/emoji/emoji_one/ring.png new file mode 100644 index 0000000000..f033d7941c Binary files /dev/null and b/public/images/emoji/emoji_one/ring.png differ diff --git a/public/images/emoji/emoji_one/rocket.png b/public/images/emoji/emoji_one/rocket.png new file mode 100644 index 0000000000..0ca6d5fed2 Binary files /dev/null and b/public/images/emoji/emoji_one/rocket.png differ diff --git a/public/images/emoji/emoji_one/roller_coaster.png b/public/images/emoji/emoji_one/roller_coaster.png new file mode 100644 index 0000000000..8492298637 Binary files /dev/null and b/public/images/emoji/emoji_one/roller_coaster.png differ diff --git a/public/images/emoji/emoji_one/rooster.png b/public/images/emoji/emoji_one/rooster.png new file mode 100644 index 0000000000..798542a0ea Binary files /dev/null and b/public/images/emoji/emoji_one/rooster.png differ diff --git a/public/images/emoji/emoji_one/rose.png b/public/images/emoji/emoji_one/rose.png new file mode 100644 index 0000000000..2a9d77598c Binary files /dev/null and b/public/images/emoji/emoji_one/rose.png differ diff --git a/public/images/emoji/emoji_one/rotating_light.png b/public/images/emoji/emoji_one/rotating_light.png new file mode 100644 index 0000000000..e286febd4b Binary files /dev/null and b/public/images/emoji/emoji_one/rotating_light.png differ diff --git a/public/images/emoji/emoji_one/round_pushpin.png b/public/images/emoji/emoji_one/round_pushpin.png new file mode 100644 index 0000000000..b16e5c41a9 Binary files /dev/null and b/public/images/emoji/emoji_one/round_pushpin.png differ diff --git a/public/images/emoji/emoji_one/rowboat.png b/public/images/emoji/emoji_one/rowboat.png new file mode 100644 index 0000000000..c554e28f20 Binary files /dev/null and b/public/images/emoji/emoji_one/rowboat.png differ diff --git a/public/images/emoji/emoji_one/ru.png b/public/images/emoji/emoji_one/ru.png new file mode 100644 index 0000000000..2b3c9d915f Binary files /dev/null and b/public/images/emoji/emoji_one/ru.png differ diff --git a/public/images/emoji/emoji_one/rugby_football.png b/public/images/emoji/emoji_one/rugby_football.png new file mode 100644 index 0000000000..e32b362221 Binary files /dev/null and b/public/images/emoji/emoji_one/rugby_football.png differ diff --git a/public/images/emoji/emoji_one/runner.png b/public/images/emoji/emoji_one/runner.png new file mode 100644 index 0000000000..0ca1f27cd5 Binary files /dev/null and b/public/images/emoji/emoji_one/runner.png differ diff --git a/public/images/emoji/emoji_one/running.png b/public/images/emoji/emoji_one/running.png new file mode 100644 index 0000000000..0ca1f27cd5 Binary files /dev/null and b/public/images/emoji/emoji_one/running.png differ diff --git a/public/images/emoji/emoji_one/running_shirt_with_sash.png b/public/images/emoji/emoji_one/running_shirt_with_sash.png new file mode 100644 index 0000000000..e49632dadf Binary files /dev/null and b/public/images/emoji/emoji_one/running_shirt_with_sash.png differ diff --git a/public/images/emoji/emoji_one/sa.png b/public/images/emoji/emoji_one/sa.png new file mode 100644 index 0000000000..033348515e Binary files /dev/null and b/public/images/emoji/emoji_one/sa.png differ diff --git a/public/images/emoji/emoji_one/sagittarius.png b/public/images/emoji/emoji_one/sagittarius.png new file mode 100644 index 0000000000..533c7772ad Binary files /dev/null and b/public/images/emoji/emoji_one/sagittarius.png differ diff --git a/public/images/emoji/emoji_one/sailboat.png b/public/images/emoji/emoji_one/sailboat.png new file mode 100644 index 0000000000..9e862f831f Binary files /dev/null and b/public/images/emoji/emoji_one/sailboat.png differ diff --git a/public/images/emoji/emoji_one/sake.png b/public/images/emoji/emoji_one/sake.png new file mode 100644 index 0000000000..40a158bfe3 Binary files /dev/null and b/public/images/emoji/emoji_one/sake.png differ diff --git a/public/images/emoji/emoji_one/sandal.png b/public/images/emoji/emoji_one/sandal.png new file mode 100644 index 0000000000..1db7005bb7 Binary files /dev/null and b/public/images/emoji/emoji_one/sandal.png differ diff --git a/public/images/emoji/emoji_one/santa.png b/public/images/emoji/emoji_one/santa.png new file mode 100644 index 0000000000..29d63fd912 Binary files /dev/null and b/public/images/emoji/emoji_one/santa.png differ diff --git a/public/images/emoji/emoji_one/satellite.png b/public/images/emoji/emoji_one/satellite.png new file mode 100644 index 0000000000..7411b9235a Binary files /dev/null and b/public/images/emoji/emoji_one/satellite.png differ diff --git a/public/images/emoji/emoji_one/satisfied.png b/public/images/emoji/emoji_one/satisfied.png new file mode 100644 index 0000000000..06adb559cd Binary files /dev/null and b/public/images/emoji/emoji_one/satisfied.png differ diff --git a/public/images/emoji/emoji_one/saxophone.png b/public/images/emoji/emoji_one/saxophone.png new file mode 100644 index 0000000000..63446652f0 Binary files /dev/null and b/public/images/emoji/emoji_one/saxophone.png differ diff --git a/public/images/emoji/emoji_one/school.png b/public/images/emoji/emoji_one/school.png new file mode 100644 index 0000000000..7bc84c877b Binary files /dev/null and b/public/images/emoji/emoji_one/school.png differ diff --git a/public/images/emoji/emoji_one/school_satchel.png b/public/images/emoji/emoji_one/school_satchel.png new file mode 100644 index 0000000000..79d4ff844a Binary files /dev/null and b/public/images/emoji/emoji_one/school_satchel.png differ diff --git a/public/images/emoji/emoji_one/scissors.png b/public/images/emoji/emoji_one/scissors.png new file mode 100644 index 0000000000..0a2d1b07f9 Binary files /dev/null and b/public/images/emoji/emoji_one/scissors.png differ diff --git a/public/images/emoji/emoji_one/scorpius.png b/public/images/emoji/emoji_one/scorpius.png new file mode 100644 index 0000000000..458c843d2b Binary files /dev/null and b/public/images/emoji/emoji_one/scorpius.png differ diff --git a/public/images/emoji/emoji_one/scream.png b/public/images/emoji/emoji_one/scream.png new file mode 100644 index 0000000000..3929adaf02 Binary files /dev/null and b/public/images/emoji/emoji_one/scream.png differ diff --git a/public/images/emoji/emoji_one/scream_cat.png b/public/images/emoji/emoji_one/scream_cat.png new file mode 100644 index 0000000000..b82c3b1b3e Binary files /dev/null and b/public/images/emoji/emoji_one/scream_cat.png differ diff --git a/public/images/emoji/emoji_one/scroll.png b/public/images/emoji/emoji_one/scroll.png new file mode 100644 index 0000000000..2505dbf59f Binary files /dev/null and b/public/images/emoji/emoji_one/scroll.png differ diff --git a/public/images/emoji/emoji_one/seat.png b/public/images/emoji/emoji_one/seat.png new file mode 100644 index 0000000000..1ec8166b6e Binary files /dev/null and b/public/images/emoji/emoji_one/seat.png differ diff --git a/public/images/emoji/emoji_one/secret.png b/public/images/emoji/emoji_one/secret.png new file mode 100644 index 0000000000..c5f8644393 Binary files /dev/null and b/public/images/emoji/emoji_one/secret.png differ diff --git a/public/images/emoji/emoji_one/see_no_evil.png b/public/images/emoji/emoji_one/see_no_evil.png new file mode 100644 index 0000000000..df1fb6607f Binary files /dev/null and b/public/images/emoji/emoji_one/see_no_evil.png differ diff --git a/public/images/emoji/emoji_one/seedling.png b/public/images/emoji/emoji_one/seedling.png new file mode 100644 index 0000000000..eae79d2746 Binary files /dev/null and b/public/images/emoji/emoji_one/seedling.png differ diff --git a/public/images/emoji/emoji_one/seven.png b/public/images/emoji/emoji_one/seven.png new file mode 100644 index 0000000000..3f5e9fa9e5 Binary files /dev/null and b/public/images/emoji/emoji_one/seven.png differ diff --git a/public/images/emoji/emoji_one/shaved_ice.png b/public/images/emoji/emoji_one/shaved_ice.png new file mode 100644 index 0000000000..a14f0f4ec2 Binary files /dev/null and b/public/images/emoji/emoji_one/shaved_ice.png differ diff --git a/public/images/emoji/emoji_one/sheep.png b/public/images/emoji/emoji_one/sheep.png new file mode 100644 index 0000000000..e47d6eda89 Binary files /dev/null and b/public/images/emoji/emoji_one/sheep.png differ diff --git a/public/images/emoji/emoji_one/shell.png b/public/images/emoji/emoji_one/shell.png new file mode 100644 index 0000000000..aa3599cf9e Binary files /dev/null and b/public/images/emoji/emoji_one/shell.png differ diff --git a/public/images/emoji/emoji_one/ship.png b/public/images/emoji/emoji_one/ship.png new file mode 100644 index 0000000000..9c24f0d3ac Binary files /dev/null and b/public/images/emoji/emoji_one/ship.png differ diff --git a/public/images/emoji/emoji_one/shirt.png b/public/images/emoji/emoji_one/shirt.png new file mode 100644 index 0000000000..556efde26e Binary files /dev/null and b/public/images/emoji/emoji_one/shirt.png differ diff --git a/public/images/emoji/emoji_one/shit.png b/public/images/emoji/emoji_one/shit.png new file mode 100644 index 0000000000..0c237a7881 Binary files /dev/null and b/public/images/emoji/emoji_one/shit.png differ diff --git a/public/images/emoji/emoji_one/shoe.png b/public/images/emoji/emoji_one/shoe.png new file mode 100644 index 0000000000..8d8a9feeb2 Binary files /dev/null and b/public/images/emoji/emoji_one/shoe.png differ diff --git a/public/images/emoji/emoji_one/shower.png b/public/images/emoji/emoji_one/shower.png new file mode 100644 index 0000000000..c794f7765c Binary files /dev/null and b/public/images/emoji/emoji_one/shower.png differ diff --git a/public/images/emoji/emoji_one/signal_strength.png b/public/images/emoji/emoji_one/signal_strength.png new file mode 100644 index 0000000000..b905562cd8 Binary files /dev/null and b/public/images/emoji/emoji_one/signal_strength.png differ diff --git a/public/images/emoji/emoji_one/six.png b/public/images/emoji/emoji_one/six.png new file mode 100644 index 0000000000..bf001a11c6 Binary files /dev/null and b/public/images/emoji/emoji_one/six.png differ diff --git a/public/images/emoji/emoji_one/six_pointed_star.png b/public/images/emoji/emoji_one/six_pointed_star.png new file mode 100644 index 0000000000..2078e2fe0d Binary files /dev/null and b/public/images/emoji/emoji_one/six_pointed_star.png differ diff --git a/public/images/emoji/emoji_one/ski.png b/public/images/emoji/emoji_one/ski.png new file mode 100644 index 0000000000..ac81516191 Binary files /dev/null and b/public/images/emoji/emoji_one/ski.png differ diff --git a/public/images/emoji/emoji_one/skull.png b/public/images/emoji/emoji_one/skull.png new file mode 100644 index 0000000000..ee66d71472 Binary files /dev/null and b/public/images/emoji/emoji_one/skull.png differ diff --git a/public/images/emoji/emoji_one/sleeping.png b/public/images/emoji/emoji_one/sleeping.png new file mode 100644 index 0000000000..68a67595e4 Binary files /dev/null and b/public/images/emoji/emoji_one/sleeping.png differ diff --git a/public/images/emoji/emoji_one/sleepy.png b/public/images/emoji/emoji_one/sleepy.png new file mode 100644 index 0000000000..4e3f47c8f5 Binary files /dev/null and b/public/images/emoji/emoji_one/sleepy.png differ diff --git a/public/images/emoji/emoji_one/slot_machine.png b/public/images/emoji/emoji_one/slot_machine.png new file mode 100644 index 0000000000..81db7b1c0e Binary files /dev/null and b/public/images/emoji/emoji_one/slot_machine.png differ diff --git a/public/images/emoji/emoji_one/small_blue_diamond.png b/public/images/emoji/emoji_one/small_blue_diamond.png new file mode 100644 index 0000000000..006243c103 Binary files /dev/null and b/public/images/emoji/emoji_one/small_blue_diamond.png differ diff --git a/public/images/emoji/emoji_one/small_orange_diamond.png b/public/images/emoji/emoji_one/small_orange_diamond.png new file mode 100644 index 0000000000..cf5fd94597 Binary files /dev/null and b/public/images/emoji/emoji_one/small_orange_diamond.png differ diff --git a/public/images/emoji/emoji_one/small_red_triangle.png b/public/images/emoji/emoji_one/small_red_triangle.png new file mode 100644 index 0000000000..f9864d1238 Binary files /dev/null and b/public/images/emoji/emoji_one/small_red_triangle.png differ diff --git a/public/images/emoji/emoji_one/small_red_triangle_down.png b/public/images/emoji/emoji_one/small_red_triangle_down.png new file mode 100644 index 0000000000..7a8ebd19bf Binary files /dev/null and b/public/images/emoji/emoji_one/small_red_triangle_down.png differ diff --git a/public/images/emoji/emoji_one/smile.png b/public/images/emoji/emoji_one/smile.png new file mode 100644 index 0000000000..05873ba728 Binary files /dev/null and b/public/images/emoji/emoji_one/smile.png differ diff --git a/public/images/emoji/emoji_one/smile_cat.png b/public/images/emoji/emoji_one/smile_cat.png new file mode 100644 index 0000000000..8db58061ad Binary files /dev/null and b/public/images/emoji/emoji_one/smile_cat.png differ diff --git a/public/images/emoji/emoji_one/smiley.png b/public/images/emoji/emoji_one/smiley.png new file mode 100644 index 0000000000..a33fbf9f92 Binary files /dev/null and b/public/images/emoji/emoji_one/smiley.png differ diff --git a/public/images/emoji/emoji_one/smiley_cat.png b/public/images/emoji/emoji_one/smiley_cat.png new file mode 100644 index 0000000000..1610dcfd31 Binary files /dev/null and b/public/images/emoji/emoji_one/smiley_cat.png differ diff --git a/public/images/emoji/emoji_one/smiling_imp.png b/public/images/emoji/emoji_one/smiling_imp.png new file mode 100644 index 0000000000..594d8f25db Binary files /dev/null and b/public/images/emoji/emoji_one/smiling_imp.png differ diff --git a/public/images/emoji/emoji_one/smirk.png b/public/images/emoji/emoji_one/smirk.png new file mode 100644 index 0000000000..69c37acd7b Binary files /dev/null and b/public/images/emoji/emoji_one/smirk.png differ diff --git a/public/images/emoji/emoji_one/smirk_cat.png b/public/images/emoji/emoji_one/smirk_cat.png new file mode 100644 index 0000000000..9fe3cd72fd Binary files /dev/null and b/public/images/emoji/emoji_one/smirk_cat.png differ diff --git a/public/images/emoji/emoji_one/smoking.png b/public/images/emoji/emoji_one/smoking.png new file mode 100644 index 0000000000..c5ff800023 Binary files /dev/null and b/public/images/emoji/emoji_one/smoking.png differ diff --git a/public/images/emoji/emoji_one/snail.png b/public/images/emoji/emoji_one/snail.png new file mode 100644 index 0000000000..97f67fb2d3 Binary files /dev/null and b/public/images/emoji/emoji_one/snail.png differ diff --git a/public/images/emoji/emoji_one/snake.png b/public/images/emoji/emoji_one/snake.png new file mode 100644 index 0000000000..dae731e220 Binary files /dev/null and b/public/images/emoji/emoji_one/snake.png differ diff --git a/public/images/emoji/emoji_one/snowboarder.png b/public/images/emoji/emoji_one/snowboarder.png new file mode 100644 index 0000000000..076c48889c Binary files /dev/null and b/public/images/emoji/emoji_one/snowboarder.png differ diff --git a/public/images/emoji/emoji_one/snowflake.png b/public/images/emoji/emoji_one/snowflake.png new file mode 100644 index 0000000000..e62a5b29e3 Binary files /dev/null and b/public/images/emoji/emoji_one/snowflake.png differ diff --git a/public/images/emoji/emoji_one/snowman.png b/public/images/emoji/emoji_one/snowman.png new file mode 100644 index 0000000000..5cfd23ec02 Binary files /dev/null and b/public/images/emoji/emoji_one/snowman.png differ diff --git a/public/images/emoji/emoji_one/sob.png b/public/images/emoji/emoji_one/sob.png new file mode 100644 index 0000000000..39b85e7ff9 Binary files /dev/null and b/public/images/emoji/emoji_one/sob.png differ diff --git a/public/images/emoji/emoji_one/soccer.png b/public/images/emoji/emoji_one/soccer.png new file mode 100644 index 0000000000..3c285fa6cf Binary files /dev/null and b/public/images/emoji/emoji_one/soccer.png differ diff --git a/plugins/emoji/public/images/unicode/1f51c.png b/public/images/emoji/emoji_one/soon.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f51c.png rename to public/images/emoji/emoji_one/soon.png diff --git a/public/images/emoji/emoji_one/sos.png b/public/images/emoji/emoji_one/sos.png new file mode 100644 index 0000000000..83b011ebc3 Binary files /dev/null and b/public/images/emoji/emoji_one/sos.png differ diff --git a/public/images/emoji/emoji_one/sound.png b/public/images/emoji/emoji_one/sound.png new file mode 100644 index 0000000000..0d7011bcb2 Binary files /dev/null and b/public/images/emoji/emoji_one/sound.png differ diff --git a/public/images/emoji/emoji_one/space_invader.png b/public/images/emoji/emoji_one/space_invader.png new file mode 100644 index 0000000000..88554d9656 Binary files /dev/null and b/public/images/emoji/emoji_one/space_invader.png differ diff --git a/public/images/emoji/emoji_one/spades.png b/public/images/emoji/emoji_one/spades.png new file mode 100644 index 0000000000..99cc688573 Binary files /dev/null and b/public/images/emoji/emoji_one/spades.png differ diff --git a/public/images/emoji/emoji_one/spaghetti.png b/public/images/emoji/emoji_one/spaghetti.png new file mode 100644 index 0000000000..ff15e877a9 Binary files /dev/null and b/public/images/emoji/emoji_one/spaghetti.png differ diff --git a/public/images/emoji/emoji_one/sparkle.png b/public/images/emoji/emoji_one/sparkle.png new file mode 100644 index 0000000000..73ae3ee69d Binary files /dev/null and b/public/images/emoji/emoji_one/sparkle.png differ diff --git a/public/images/emoji/emoji_one/sparkler.png b/public/images/emoji/emoji_one/sparkler.png new file mode 100644 index 0000000000..265e328338 Binary files /dev/null and b/public/images/emoji/emoji_one/sparkler.png differ diff --git a/public/images/emoji/emoji_one/sparkles.png b/public/images/emoji/emoji_one/sparkles.png new file mode 100644 index 0000000000..aa574e0255 Binary files /dev/null and b/public/images/emoji/emoji_one/sparkles.png differ diff --git a/public/images/emoji/emoji_one/sparkling_heart.png b/public/images/emoji/emoji_one/sparkling_heart.png new file mode 100644 index 0000000000..0bfffc4ca7 Binary files /dev/null and b/public/images/emoji/emoji_one/sparkling_heart.png differ diff --git a/public/images/emoji/emoji_one/speak_no_evil.png b/public/images/emoji/emoji_one/speak_no_evil.png new file mode 100644 index 0000000000..c6f76937f2 Binary files /dev/null and b/public/images/emoji/emoji_one/speak_no_evil.png differ diff --git a/public/images/emoji/emoji_one/speaker.png b/public/images/emoji/emoji_one/speaker.png new file mode 100644 index 0000000000..38d5ee391f Binary files /dev/null and b/public/images/emoji/emoji_one/speaker.png differ diff --git a/public/images/emoji/emoji_one/speech_balloon.png b/public/images/emoji/emoji_one/speech_balloon.png new file mode 100644 index 0000000000..902004e92b Binary files /dev/null and b/public/images/emoji/emoji_one/speech_balloon.png differ diff --git a/public/images/emoji/emoji_one/speedboat.png b/public/images/emoji/emoji_one/speedboat.png new file mode 100644 index 0000000000..474b052c0d Binary files /dev/null and b/public/images/emoji/emoji_one/speedboat.png differ diff --git a/plugins/emoji/public/images/unicode/2b50.png b/public/images/emoji/emoji_one/star.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/2b50.png rename to public/images/emoji/emoji_one/star.png diff --git a/public/images/emoji/emoji_one/star2.png b/public/images/emoji/emoji_one/star2.png new file mode 100644 index 0000000000..6399b9335b Binary files /dev/null and b/public/images/emoji/emoji_one/star2.png differ diff --git a/public/images/emoji/emoji_one/stars.png b/public/images/emoji/emoji_one/stars.png new file mode 100644 index 0000000000..5d2b19ebd9 Binary files /dev/null and b/public/images/emoji/emoji_one/stars.png differ diff --git a/public/images/emoji/emoji_one/station.png b/public/images/emoji/emoji_one/station.png new file mode 100644 index 0000000000..970d2799d9 Binary files /dev/null and b/public/images/emoji/emoji_one/station.png differ diff --git a/public/images/emoji/emoji_one/statue_of_liberty.png b/public/images/emoji/emoji_one/statue_of_liberty.png new file mode 100644 index 0000000000..3ab4414c68 Binary files /dev/null and b/public/images/emoji/emoji_one/statue_of_liberty.png differ diff --git a/public/images/emoji/emoji_one/steam_locomotive.png b/public/images/emoji/emoji_one/steam_locomotive.png new file mode 100644 index 0000000000..f7c52db713 Binary files /dev/null and b/public/images/emoji/emoji_one/steam_locomotive.png differ diff --git a/public/images/emoji/emoji_one/stew.png b/public/images/emoji/emoji_one/stew.png new file mode 100644 index 0000000000..6f83053386 Binary files /dev/null and b/public/images/emoji/emoji_one/stew.png differ diff --git a/public/images/emoji/emoji_one/straight_ruler.png b/public/images/emoji/emoji_one/straight_ruler.png new file mode 100644 index 0000000000..42e7ac39cf Binary files /dev/null and b/public/images/emoji/emoji_one/straight_ruler.png differ diff --git a/public/images/emoji/emoji_one/strawberry.png b/public/images/emoji/emoji_one/strawberry.png new file mode 100644 index 0000000000..b28837732e Binary files /dev/null and b/public/images/emoji/emoji_one/strawberry.png differ diff --git a/public/images/emoji/emoji_one/stuck_out_tongue.png b/public/images/emoji/emoji_one/stuck_out_tongue.png new file mode 100644 index 0000000000..3b25a869a3 Binary files /dev/null and b/public/images/emoji/emoji_one/stuck_out_tongue.png differ diff --git a/public/images/emoji/emoji_one/stuck_out_tongue_closed_eyes.png b/public/images/emoji/emoji_one/stuck_out_tongue_closed_eyes.png new file mode 100644 index 0000000000..e473376590 Binary files /dev/null and b/public/images/emoji/emoji_one/stuck_out_tongue_closed_eyes.png differ diff --git a/public/images/emoji/emoji_one/stuck_out_tongue_winking_eye.png b/public/images/emoji/emoji_one/stuck_out_tongue_winking_eye.png new file mode 100644 index 0000000000..fdae18df7d Binary files /dev/null and b/public/images/emoji/emoji_one/stuck_out_tongue_winking_eye.png differ diff --git a/public/images/emoji/emoji_one/sun_with_face.png b/public/images/emoji/emoji_one/sun_with_face.png new file mode 100644 index 0000000000..7ee45e4dd1 Binary files /dev/null and b/public/images/emoji/emoji_one/sun_with_face.png differ diff --git a/public/images/emoji/emoji_one/sunflower.png b/public/images/emoji/emoji_one/sunflower.png new file mode 100644 index 0000000000..b7cfbf82b4 Binary files /dev/null and b/public/images/emoji/emoji_one/sunflower.png differ diff --git a/public/images/emoji/emoji_one/sunglasses.png b/public/images/emoji/emoji_one/sunglasses.png new file mode 100644 index 0000000000..4d62237660 Binary files /dev/null and b/public/images/emoji/emoji_one/sunglasses.png differ diff --git a/plugins/emoji/public/images/unicode/2600.png b/public/images/emoji/emoji_one/sunny.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/2600.png rename to public/images/emoji/emoji_one/sunny.png diff --git a/public/images/emoji/emoji_one/sunrise.png b/public/images/emoji/emoji_one/sunrise.png new file mode 100644 index 0000000000..3d17e794a5 Binary files /dev/null and b/public/images/emoji/emoji_one/sunrise.png differ diff --git a/public/images/emoji/emoji_one/sunrise_over_mountains.png b/public/images/emoji/emoji_one/sunrise_over_mountains.png new file mode 100644 index 0000000000..c1c30134d9 Binary files /dev/null and b/public/images/emoji/emoji_one/sunrise_over_mountains.png differ diff --git a/public/images/emoji/emoji_one/surfer.png b/public/images/emoji/emoji_one/surfer.png new file mode 100644 index 0000000000..85cf23a6e7 Binary files /dev/null and b/public/images/emoji/emoji_one/surfer.png differ diff --git a/public/images/emoji/emoji_one/sushi.png b/public/images/emoji/emoji_one/sushi.png new file mode 100644 index 0000000000..8c2f6826e0 Binary files /dev/null and b/public/images/emoji/emoji_one/sushi.png differ diff --git a/public/images/emoji/emoji_one/suspension_railway.png b/public/images/emoji/emoji_one/suspension_railway.png new file mode 100644 index 0000000000..2c5ff64a16 Binary files /dev/null and b/public/images/emoji/emoji_one/suspension_railway.png differ diff --git a/public/images/emoji/emoji_one/sweat.png b/public/images/emoji/emoji_one/sweat.png new file mode 100644 index 0000000000..1aee2558cf Binary files /dev/null and b/public/images/emoji/emoji_one/sweat.png differ diff --git a/public/images/emoji/emoji_one/sweat_drops.png b/public/images/emoji/emoji_one/sweat_drops.png new file mode 100644 index 0000000000..3a48a0a7fc Binary files /dev/null and b/public/images/emoji/emoji_one/sweat_drops.png differ diff --git a/public/images/emoji/emoji_one/sweat_smile.png b/public/images/emoji/emoji_one/sweat_smile.png new file mode 100644 index 0000000000..09e8bf7fc6 Binary files /dev/null and b/public/images/emoji/emoji_one/sweat_smile.png differ diff --git a/public/images/emoji/emoji_one/sweet_potato.png b/public/images/emoji/emoji_one/sweet_potato.png new file mode 100644 index 0000000000..7c60e76cbe Binary files /dev/null and b/public/images/emoji/emoji_one/sweet_potato.png differ diff --git a/public/images/emoji/emoji_one/swimmer.png b/public/images/emoji/emoji_one/swimmer.png new file mode 100644 index 0000000000..ed2b641ab2 Binary files /dev/null and b/public/images/emoji/emoji_one/swimmer.png differ diff --git a/public/images/emoji/emoji_one/symbols.png b/public/images/emoji/emoji_one/symbols.png new file mode 100644 index 0000000000..4958f104e6 Binary files /dev/null and b/public/images/emoji/emoji_one/symbols.png differ diff --git a/public/images/emoji/emoji_one/syringe.png b/public/images/emoji/emoji_one/syringe.png new file mode 100644 index 0000000000..73eed8cde9 Binary files /dev/null and b/public/images/emoji/emoji_one/syringe.png differ diff --git a/public/images/emoji/emoji_one/tada.png b/public/images/emoji/emoji_one/tada.png new file mode 100644 index 0000000000..003a669964 Binary files /dev/null and b/public/images/emoji/emoji_one/tada.png differ diff --git a/public/images/emoji/emoji_one/tanabata_tree.png b/public/images/emoji/emoji_one/tanabata_tree.png new file mode 100644 index 0000000000..ec7d8cc229 Binary files /dev/null and b/public/images/emoji/emoji_one/tanabata_tree.png differ diff --git a/public/images/emoji/emoji_one/tangerine.png b/public/images/emoji/emoji_one/tangerine.png new file mode 100644 index 0000000000..7cb15fcf79 Binary files /dev/null and b/public/images/emoji/emoji_one/tangerine.png differ diff --git a/public/images/emoji/emoji_one/taurus.png b/public/images/emoji/emoji_one/taurus.png new file mode 100644 index 0000000000..10426ef746 Binary files /dev/null and b/public/images/emoji/emoji_one/taurus.png differ diff --git a/public/images/emoji/emoji_one/taxi.png b/public/images/emoji/emoji_one/taxi.png new file mode 100644 index 0000000000..8652b1f294 Binary files /dev/null and b/public/images/emoji/emoji_one/taxi.png differ diff --git a/public/images/emoji/emoji_one/tea.png b/public/images/emoji/emoji_one/tea.png new file mode 100644 index 0000000000..898815bb5f Binary files /dev/null and b/public/images/emoji/emoji_one/tea.png differ diff --git a/public/images/emoji/emoji_one/telephone.png b/public/images/emoji/emoji_one/telephone.png new file mode 100644 index 0000000000..6a14f1bfe9 Binary files /dev/null and b/public/images/emoji/emoji_one/telephone.png differ diff --git a/public/images/emoji/emoji_one/telephone_receiver.png b/public/images/emoji/emoji_one/telephone_receiver.png new file mode 100644 index 0000000000..6a8259cb7c Binary files /dev/null and b/public/images/emoji/emoji_one/telephone_receiver.png differ diff --git a/public/images/emoji/emoji_one/telescope.png b/public/images/emoji/emoji_one/telescope.png new file mode 100644 index 0000000000..8fc07d50fe Binary files /dev/null and b/public/images/emoji/emoji_one/telescope.png differ diff --git a/public/images/emoji/emoji_one/tennis.png b/public/images/emoji/emoji_one/tennis.png new file mode 100644 index 0000000000..26288eb238 Binary files /dev/null and b/public/images/emoji/emoji_one/tennis.png differ diff --git a/public/images/emoji/emoji_one/tent.png b/public/images/emoji/emoji_one/tent.png new file mode 100644 index 0000000000..d1c4fe63dc Binary files /dev/null and b/public/images/emoji/emoji_one/tent.png differ diff --git a/plugins/emoji/public/images/unicode/1f4ad.png b/public/images/emoji/emoji_one/thought_balloon.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f4ad.png rename to public/images/emoji/emoji_one/thought_balloon.png diff --git a/plugins/emoji/public/images/unicode/0033-20E3.png b/public/images/emoji/emoji_one/three.png similarity index 100% rename from plugins/emoji/public/images/unicode/0033-20E3.png rename to public/images/emoji/emoji_one/three.png diff --git a/public/images/emoji/emoji_one/thumbsdown.png b/public/images/emoji/emoji_one/thumbsdown.png new file mode 100644 index 0000000000..9b6d251b54 Binary files /dev/null and b/public/images/emoji/emoji_one/thumbsdown.png differ diff --git a/public/images/emoji/emoji_one/thumbsup.png b/public/images/emoji/emoji_one/thumbsup.png new file mode 100644 index 0000000000..0eca450d59 Binary files /dev/null and b/public/images/emoji/emoji_one/thumbsup.png differ diff --git a/plugins/emoji/public/images/unicode/1f3ab.png b/public/images/emoji/emoji_one/ticket.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f3ab.png rename to public/images/emoji/emoji_one/ticket.png diff --git a/public/images/emoji/emoji_one/tiger.png b/public/images/emoji/emoji_one/tiger.png new file mode 100644 index 0000000000..64a19cd61e Binary files /dev/null and b/public/images/emoji/emoji_one/tiger.png differ diff --git a/public/images/emoji/emoji_one/tiger2.png b/public/images/emoji/emoji_one/tiger2.png new file mode 100644 index 0000000000..c7910315e5 Binary files /dev/null and b/public/images/emoji/emoji_one/tiger2.png differ diff --git a/public/images/emoji/emoji_one/tired_face.png b/public/images/emoji/emoji_one/tired_face.png new file mode 100644 index 0000000000..641d8d8338 Binary files /dev/null and b/public/images/emoji/emoji_one/tired_face.png differ diff --git a/public/images/emoji/emoji_one/tm.png b/public/images/emoji/emoji_one/tm.png new file mode 100644 index 0000000000..8b37929683 Binary files /dev/null and b/public/images/emoji/emoji_one/tm.png differ diff --git a/public/images/emoji/emoji_one/toilet.png b/public/images/emoji/emoji_one/toilet.png new file mode 100644 index 0000000000..ab811075fd Binary files /dev/null and b/public/images/emoji/emoji_one/toilet.png differ diff --git a/public/images/emoji/emoji_one/tokyo_tower.png b/public/images/emoji/emoji_one/tokyo_tower.png new file mode 100644 index 0000000000..22abea95fb Binary files /dev/null and b/public/images/emoji/emoji_one/tokyo_tower.png differ diff --git a/public/images/emoji/emoji_one/tomato.png b/public/images/emoji/emoji_one/tomato.png new file mode 100644 index 0000000000..4114c35ffb Binary files /dev/null and b/public/images/emoji/emoji_one/tomato.png differ diff --git a/plugins/emoji/public/images/unicode/1f445.png b/public/images/emoji/emoji_one/tongue.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f445.png rename to public/images/emoji/emoji_one/tongue.png diff --git a/public/images/emoji/emoji_one/top.png b/public/images/emoji/emoji_one/top.png new file mode 100644 index 0000000000..e744f94e73 Binary files /dev/null and b/public/images/emoji/emoji_one/top.png differ diff --git a/plugins/emoji/public/images/unicode/1f3a9.png b/public/images/emoji/emoji_one/tophat.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f3a9.png rename to public/images/emoji/emoji_one/tophat.png diff --git a/public/images/emoji/emoji_one/tractor.png b/public/images/emoji/emoji_one/tractor.png new file mode 100644 index 0000000000..fcd4000c56 Binary files /dev/null and b/public/images/emoji/emoji_one/tractor.png differ diff --git a/public/images/emoji/emoji_one/traffic_light.png b/public/images/emoji/emoji_one/traffic_light.png new file mode 100644 index 0000000000..9add0f5bb6 Binary files /dev/null and b/public/images/emoji/emoji_one/traffic_light.png differ diff --git a/public/images/emoji/emoji_one/train.png b/public/images/emoji/emoji_one/train.png new file mode 100644 index 0000000000..fc015d4c3b Binary files /dev/null and b/public/images/emoji/emoji_one/train.png differ diff --git a/public/images/emoji/emoji_one/train2.png b/public/images/emoji/emoji_one/train2.png new file mode 100644 index 0000000000..22d8433022 Binary files /dev/null and b/public/images/emoji/emoji_one/train2.png differ diff --git a/public/images/emoji/emoji_one/tram.png b/public/images/emoji/emoji_one/tram.png new file mode 100644 index 0000000000..851cf18274 Binary files /dev/null and b/public/images/emoji/emoji_one/tram.png differ diff --git a/public/images/emoji/emoji_one/triangular_flag_on_post.png b/public/images/emoji/emoji_one/triangular_flag_on_post.png new file mode 100644 index 0000000000..180d6cf278 Binary files /dev/null and b/public/images/emoji/emoji_one/triangular_flag_on_post.png differ diff --git a/plugins/emoji/public/images/unicode/1f4d0.png b/public/images/emoji/emoji_one/triangular_ruler.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f4d0.png rename to public/images/emoji/emoji_one/triangular_ruler.png diff --git a/public/images/emoji/emoji_one/trident.png b/public/images/emoji/emoji_one/trident.png new file mode 100644 index 0000000000..ae1e0bc883 Binary files /dev/null and b/public/images/emoji/emoji_one/trident.png differ diff --git a/public/images/emoji/emoji_one/triumph.png b/public/images/emoji/emoji_one/triumph.png new file mode 100644 index 0000000000..ba927f8c1e Binary files /dev/null and b/public/images/emoji/emoji_one/triumph.png differ diff --git a/public/images/emoji/emoji_one/trolleybus.png b/public/images/emoji/emoji_one/trolleybus.png new file mode 100644 index 0000000000..5207d82da8 Binary files /dev/null and b/public/images/emoji/emoji_one/trolleybus.png differ diff --git a/public/images/emoji/emoji_one/trophy.png b/public/images/emoji/emoji_one/trophy.png new file mode 100644 index 0000000000..38429fe197 Binary files /dev/null and b/public/images/emoji/emoji_one/trophy.png differ diff --git a/public/images/emoji/emoji_one/tropical_drink.png b/public/images/emoji/emoji_one/tropical_drink.png new file mode 100644 index 0000000000..aa0f2ee7ea Binary files /dev/null and b/public/images/emoji/emoji_one/tropical_drink.png differ diff --git a/public/images/emoji/emoji_one/tropical_fish.png b/public/images/emoji/emoji_one/tropical_fish.png new file mode 100644 index 0000000000..1904f61be2 Binary files /dev/null and b/public/images/emoji/emoji_one/tropical_fish.png differ diff --git a/public/images/emoji/emoji_one/truck.png b/public/images/emoji/emoji_one/truck.png new file mode 100644 index 0000000000..ef99f12fb2 Binary files /dev/null and b/public/images/emoji/emoji_one/truck.png differ diff --git a/public/images/emoji/emoji_one/trumpet.png b/public/images/emoji/emoji_one/trumpet.png new file mode 100644 index 0000000000..c2270ccbfc Binary files /dev/null and b/public/images/emoji/emoji_one/trumpet.png differ diff --git a/public/images/emoji/emoji_one/tshirt.png b/public/images/emoji/emoji_one/tshirt.png new file mode 100644 index 0000000000..556efde26e Binary files /dev/null and b/public/images/emoji/emoji_one/tshirt.png differ diff --git a/public/images/emoji/emoji_one/tulip.png b/public/images/emoji/emoji_one/tulip.png new file mode 100644 index 0000000000..a05a51edfa Binary files /dev/null and b/public/images/emoji/emoji_one/tulip.png differ diff --git a/public/images/emoji/emoji_one/turtle.png b/public/images/emoji/emoji_one/turtle.png new file mode 100644 index 0000000000..c3a628c0a8 Binary files /dev/null and b/public/images/emoji/emoji_one/turtle.png differ diff --git a/public/images/emoji/emoji_one/tv.png b/public/images/emoji/emoji_one/tv.png new file mode 100644 index 0000000000..5318e2dc86 Binary files /dev/null and b/public/images/emoji/emoji_one/tv.png differ diff --git a/public/images/emoji/emoji_one/twisted_rightwards_arrows.png b/public/images/emoji/emoji_one/twisted_rightwards_arrows.png new file mode 100644 index 0000000000..8ddb38cb5a Binary files /dev/null and b/public/images/emoji/emoji_one/twisted_rightwards_arrows.png differ diff --git a/plugins/emoji/public/images/unicode/0032-20E3.png b/public/images/emoji/emoji_one/two.png similarity index 100% rename from plugins/emoji/public/images/unicode/0032-20E3.png rename to public/images/emoji/emoji_one/two.png diff --git a/plugins/emoji/public/images/unicode/1f495.png b/public/images/emoji/emoji_one/two_hearts.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f495.png rename to public/images/emoji/emoji_one/two_hearts.png diff --git a/public/images/emoji/emoji_one/two_men_holding_hands.png b/public/images/emoji/emoji_one/two_men_holding_hands.png new file mode 100644 index 0000000000..a9328449f0 Binary files /dev/null and b/public/images/emoji/emoji_one/two_men_holding_hands.png differ diff --git a/public/images/emoji/emoji_one/two_women_holding_hands.png b/public/images/emoji/emoji_one/two_women_holding_hands.png new file mode 100644 index 0000000000..5c28e28d2e Binary files /dev/null and b/public/images/emoji/emoji_one/two_women_holding_hands.png differ diff --git a/plugins/emoji/public/images/unicode/1f239.png b/public/images/emoji/emoji_one/u5272.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f239.png rename to public/images/emoji/emoji_one/u5272.png diff --git a/public/images/emoji/emoji_one/u5408.png b/public/images/emoji/emoji_one/u5408.png new file mode 100644 index 0000000000..77883fdb2d Binary files /dev/null and b/public/images/emoji/emoji_one/u5408.png differ diff --git a/plugins/emoji/public/images/unicode/1f23a.png b/public/images/emoji/emoji_one/u55b6.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f23a.png rename to public/images/emoji/emoji_one/u55b6.png diff --git a/public/images/emoji/emoji_one/u6307.png b/public/images/emoji/emoji_one/u6307.png new file mode 100644 index 0000000000..85af968abc Binary files /dev/null and b/public/images/emoji/emoji_one/u6307.png differ diff --git a/public/images/emoji/emoji_one/u6708.png b/public/images/emoji/emoji_one/u6708.png new file mode 100644 index 0000000000..8d21aeccfc Binary files /dev/null and b/public/images/emoji/emoji_one/u6708.png differ diff --git a/public/images/emoji/emoji_one/u6709.png b/public/images/emoji/emoji_one/u6709.png new file mode 100644 index 0000000000..002991d631 Binary files /dev/null and b/public/images/emoji/emoji_one/u6709.png differ diff --git a/public/images/emoji/emoji_one/u6e80.png b/public/images/emoji/emoji_one/u6e80.png new file mode 100644 index 0000000000..d2c2a9f860 Binary files /dev/null and b/public/images/emoji/emoji_one/u6e80.png differ diff --git a/public/images/emoji/emoji_one/u7121.png b/public/images/emoji/emoji_one/u7121.png new file mode 100644 index 0000000000..1ae53d1a7c Binary files /dev/null and b/public/images/emoji/emoji_one/u7121.png differ diff --git a/plugins/emoji/public/images/unicode/1f238.png b/public/images/emoji/emoji_one/u7533.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f238.png rename to public/images/emoji/emoji_one/u7533.png diff --git a/public/images/emoji/emoji_one/u7981.png b/public/images/emoji/emoji_one/u7981.png new file mode 100644 index 0000000000..2162850e95 Binary files /dev/null and b/public/images/emoji/emoji_one/u7981.png differ diff --git a/public/images/emoji/emoji_one/u7a7a.png b/public/images/emoji/emoji_one/u7a7a.png new file mode 100644 index 0000000000..8237cfa303 Binary files /dev/null and b/public/images/emoji/emoji_one/u7a7a.png differ diff --git a/public/images/emoji/emoji_one/uk.png b/public/images/emoji/emoji_one/uk.png new file mode 100644 index 0000000000..61c03f759f Binary files /dev/null and b/public/images/emoji/emoji_one/uk.png differ diff --git a/public/images/emoji/emoji_one/umbrella.png b/public/images/emoji/emoji_one/umbrella.png new file mode 100644 index 0000000000..7a172a4e6b Binary files /dev/null and b/public/images/emoji/emoji_one/umbrella.png differ diff --git a/public/images/emoji/emoji_one/unamused.png b/public/images/emoji/emoji_one/unamused.png new file mode 100644 index 0000000000..83a7e92ece Binary files /dev/null and b/public/images/emoji/emoji_one/unamused.png differ diff --git a/public/images/emoji/emoji_one/underage.png b/public/images/emoji/emoji_one/underage.png new file mode 100644 index 0000000000..603ce57eae Binary files /dev/null and b/public/images/emoji/emoji_one/underage.png differ diff --git a/public/images/emoji/emoji_one/unlock.png b/public/images/emoji/emoji_one/unlock.png new file mode 100644 index 0000000000..f2f46d9349 Binary files /dev/null and b/public/images/emoji/emoji_one/unlock.png differ diff --git a/public/images/emoji/emoji_one/up.png b/public/images/emoji/emoji_one/up.png new file mode 100644 index 0000000000..1dc334a6bb Binary files /dev/null and b/public/images/emoji/emoji_one/up.png differ diff --git a/public/images/emoji/emoji_one/us.png b/public/images/emoji/emoji_one/us.png new file mode 100644 index 0000000000..51c1e8477e Binary files /dev/null and b/public/images/emoji/emoji_one/us.png differ diff --git a/plugins/emoji/public/images/unicode/270c.png b/public/images/emoji/emoji_one/v.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/270c.png rename to public/images/emoji/emoji_one/v.png diff --git a/public/images/emoji/emoji_one/vertical_traffic_light.png b/public/images/emoji/emoji_one/vertical_traffic_light.png new file mode 100644 index 0000000000..113c78fa63 Binary files /dev/null and b/public/images/emoji/emoji_one/vertical_traffic_light.png differ diff --git a/public/images/emoji/emoji_one/vhs.png b/public/images/emoji/emoji_one/vhs.png new file mode 100644 index 0000000000..cbc657b756 Binary files /dev/null and b/public/images/emoji/emoji_one/vhs.png differ diff --git a/public/images/emoji/emoji_one/vibration_mode.png b/public/images/emoji/emoji_one/vibration_mode.png new file mode 100644 index 0000000000..1a772ac155 Binary files /dev/null and b/public/images/emoji/emoji_one/vibration_mode.png differ diff --git a/plugins/emoji/public/images/unicode/1f4f9.png b/public/images/emoji/emoji_one/video_camera.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f4f9.png rename to public/images/emoji/emoji_one/video_camera.png diff --git a/public/images/emoji/emoji_one/video_game.png b/public/images/emoji/emoji_one/video_game.png new file mode 100644 index 0000000000..702832e99d Binary files /dev/null and b/public/images/emoji/emoji_one/video_game.png differ diff --git a/public/images/emoji/emoji_one/violin.png b/public/images/emoji/emoji_one/violin.png new file mode 100644 index 0000000000..0bb8e0a10f Binary files /dev/null and b/public/images/emoji/emoji_one/violin.png differ diff --git a/public/images/emoji/emoji_one/virgo.png b/public/images/emoji/emoji_one/virgo.png new file mode 100644 index 0000000000..322502928c Binary files /dev/null and b/public/images/emoji/emoji_one/virgo.png differ diff --git a/public/images/emoji/emoji_one/volcano.png b/public/images/emoji/emoji_one/volcano.png new file mode 100644 index 0000000000..5f2c292f02 Binary files /dev/null and b/public/images/emoji/emoji_one/volcano.png differ diff --git a/public/images/emoji/emoji_one/vs.png b/public/images/emoji/emoji_one/vs.png new file mode 100644 index 0000000000..4c4bd41e84 Binary files /dev/null and b/public/images/emoji/emoji_one/vs.png differ diff --git a/public/images/emoji/emoji_one/walking.png b/public/images/emoji/emoji_one/walking.png new file mode 100644 index 0000000000..780df9f475 Binary files /dev/null and b/public/images/emoji/emoji_one/walking.png differ diff --git a/public/images/emoji/emoji_one/waning_crescent_moon.png b/public/images/emoji/emoji_one/waning_crescent_moon.png new file mode 100644 index 0000000000..f9ab41111e Binary files /dev/null and b/public/images/emoji/emoji_one/waning_crescent_moon.png differ diff --git a/public/images/emoji/emoji_one/waning_gibbous_moon.png b/public/images/emoji/emoji_one/waning_gibbous_moon.png new file mode 100644 index 0000000000..3b9240298e Binary files /dev/null and b/public/images/emoji/emoji_one/waning_gibbous_moon.png differ diff --git a/public/images/emoji/emoji_one/warning.png b/public/images/emoji/emoji_one/warning.png new file mode 100644 index 0000000000..da0bfd5402 Binary files /dev/null and b/public/images/emoji/emoji_one/warning.png differ diff --git a/public/images/emoji/emoji_one/watch.png b/public/images/emoji/emoji_one/watch.png new file mode 100644 index 0000000000..50f16588a1 Binary files /dev/null and b/public/images/emoji/emoji_one/watch.png differ diff --git a/public/images/emoji/emoji_one/water_buffalo.png b/public/images/emoji/emoji_one/water_buffalo.png new file mode 100644 index 0000000000..2c8fa68e8c Binary files /dev/null and b/public/images/emoji/emoji_one/water_buffalo.png differ diff --git a/public/images/emoji/emoji_one/watermelon.png b/public/images/emoji/emoji_one/watermelon.png new file mode 100644 index 0000000000..5125aea3e3 Binary files /dev/null and b/public/images/emoji/emoji_one/watermelon.png differ diff --git a/public/images/emoji/emoji_one/wave.png b/public/images/emoji/emoji_one/wave.png new file mode 100644 index 0000000000..8a7086bd0f Binary files /dev/null and b/public/images/emoji/emoji_one/wave.png differ diff --git a/public/images/emoji/emoji_one/wavy_dash.png b/public/images/emoji/emoji_one/wavy_dash.png new file mode 100644 index 0000000000..de20687bdd Binary files /dev/null and b/public/images/emoji/emoji_one/wavy_dash.png differ diff --git a/public/images/emoji/emoji_one/waxing_crescent_moon.png b/public/images/emoji/emoji_one/waxing_crescent_moon.png new file mode 100644 index 0000000000..7d4b5a056e Binary files /dev/null and b/public/images/emoji/emoji_one/waxing_crescent_moon.png differ diff --git a/public/images/emoji/emoji_one/waxing_gibbous_moon.png b/public/images/emoji/emoji_one/waxing_gibbous_moon.png new file mode 100644 index 0000000000..b53fd42886 Binary files /dev/null and b/public/images/emoji/emoji_one/waxing_gibbous_moon.png differ diff --git a/public/images/emoji/emoji_one/wc.png b/public/images/emoji/emoji_one/wc.png new file mode 100644 index 0000000000..92399227fd Binary files /dev/null and b/public/images/emoji/emoji_one/wc.png differ diff --git a/public/images/emoji/emoji_one/weary.png b/public/images/emoji/emoji_one/weary.png new file mode 100644 index 0000000000..09301ab69b Binary files /dev/null and b/public/images/emoji/emoji_one/weary.png differ diff --git a/public/images/emoji/emoji_one/wedding.png b/public/images/emoji/emoji_one/wedding.png new file mode 100644 index 0000000000..dda4a140f4 Binary files /dev/null and b/public/images/emoji/emoji_one/wedding.png differ diff --git a/public/images/emoji/emoji_one/whale.png b/public/images/emoji/emoji_one/whale.png new file mode 100644 index 0000000000..cf1ecee86f Binary files /dev/null and b/public/images/emoji/emoji_one/whale.png differ diff --git a/public/images/emoji/emoji_one/whale2.png b/public/images/emoji/emoji_one/whale2.png new file mode 100644 index 0000000000..1818605302 Binary files /dev/null and b/public/images/emoji/emoji_one/whale2.png differ diff --git a/public/images/emoji/emoji_one/wheelchair.png b/public/images/emoji/emoji_one/wheelchair.png new file mode 100644 index 0000000000..492324ee06 Binary files /dev/null and b/public/images/emoji/emoji_one/wheelchair.png differ diff --git a/public/images/emoji/emoji_one/white_check_mark.png b/public/images/emoji/emoji_one/white_check_mark.png new file mode 100644 index 0000000000..2f35a6fa80 Binary files /dev/null and b/public/images/emoji/emoji_one/white_check_mark.png differ diff --git a/public/images/emoji/emoji_one/white_circle.png b/public/images/emoji/emoji_one/white_circle.png new file mode 100644 index 0000000000..ba9611efe2 Binary files /dev/null and b/public/images/emoji/emoji_one/white_circle.png differ diff --git a/plugins/emoji/public/images/unicode/1f4ae.png b/public/images/emoji/emoji_one/white_flower.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f4ae.png rename to public/images/emoji/emoji_one/white_flower.png diff --git a/plugins/emoji/public/images/unicode/2b1c.png b/public/images/emoji/emoji_one/white_large_square.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/2b1c.png rename to public/images/emoji/emoji_one/white_large_square.png diff --git a/plugins/emoji/public/images/unicode/25fd.png b/public/images/emoji/emoji_one/white_medium_small_square.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/25fd.png rename to public/images/emoji/emoji_one/white_medium_small_square.png diff --git a/plugins/emoji/public/images/unicode/25fb.png b/public/images/emoji/emoji_one/white_medium_square.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/25fb.png rename to public/images/emoji/emoji_one/white_medium_square.png diff --git a/plugins/emoji/public/images/unicode/25ab.png b/public/images/emoji/emoji_one/white_small_square.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/25ab.png rename to public/images/emoji/emoji_one/white_small_square.png diff --git a/plugins/emoji/public/images/unicode/1f533.png b/public/images/emoji/emoji_one/white_square_button.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f533.png rename to public/images/emoji/emoji_one/white_square_button.png diff --git a/public/images/emoji/emoji_one/wind_chime.png b/public/images/emoji/emoji_one/wind_chime.png new file mode 100644 index 0000000000..8aee0e8bda Binary files /dev/null and b/public/images/emoji/emoji_one/wind_chime.png differ diff --git a/public/images/emoji/emoji_one/wine_glass.png b/public/images/emoji/emoji_one/wine_glass.png new file mode 100644 index 0000000000..5854305f43 Binary files /dev/null and b/public/images/emoji/emoji_one/wine_glass.png differ diff --git a/public/images/emoji/emoji_one/wink.png b/public/images/emoji/emoji_one/wink.png new file mode 100644 index 0000000000..7b060cd393 Binary files /dev/null and b/public/images/emoji/emoji_one/wink.png differ diff --git a/public/images/emoji/emoji_one/wolf.png b/public/images/emoji/emoji_one/wolf.png new file mode 100644 index 0000000000..46f054c625 Binary files /dev/null and b/public/images/emoji/emoji_one/wolf.png differ diff --git a/public/images/emoji/emoji_one/woman.png b/public/images/emoji/emoji_one/woman.png new file mode 100644 index 0000000000..286dce5b5a Binary files /dev/null and b/public/images/emoji/emoji_one/woman.png differ diff --git a/public/images/emoji/emoji_one/womans_clothes.png b/public/images/emoji/emoji_one/womans_clothes.png new file mode 100644 index 0000000000..1a54942671 Binary files /dev/null and b/public/images/emoji/emoji_one/womans_clothes.png differ diff --git a/public/images/emoji/emoji_one/womans_hat.png b/public/images/emoji/emoji_one/womans_hat.png new file mode 100644 index 0000000000..33af561b33 Binary files /dev/null and b/public/images/emoji/emoji_one/womans_hat.png differ diff --git a/public/images/emoji/emoji_one/womens.png b/public/images/emoji/emoji_one/womens.png new file mode 100644 index 0000000000..7e231f009d Binary files /dev/null and b/public/images/emoji/emoji_one/womens.png differ diff --git a/public/images/emoji/emoji_one/worried.png b/public/images/emoji/emoji_one/worried.png new file mode 100644 index 0000000000..8809c790a0 Binary files /dev/null and b/public/images/emoji/emoji_one/worried.png differ diff --git a/public/images/emoji/emoji_one/wrench.png b/public/images/emoji/emoji_one/wrench.png new file mode 100644 index 0000000000..64a0d6a765 Binary files /dev/null and b/public/images/emoji/emoji_one/wrench.png differ diff --git a/plugins/emoji/public/images/unicode/274c.png b/public/images/emoji/emoji_one/x.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/274c.png rename to public/images/emoji/emoji_one/x.png diff --git a/plugins/emoji/public/images/unicode/1f49b.png b/public/images/emoji/emoji_one/yellow_heart.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f49b.png rename to public/images/emoji/emoji_one/yellow_heart.png diff --git a/public/images/emoji/emoji_one/yen.png b/public/images/emoji/emoji_one/yen.png new file mode 100644 index 0000000000..7585eaa75a Binary files /dev/null and b/public/images/emoji/emoji_one/yen.png differ diff --git a/public/images/emoji/emoji_one/yum.png b/public/images/emoji/emoji_one/yum.png new file mode 100644 index 0000000000..3cf0c60b31 Binary files /dev/null and b/public/images/emoji/emoji_one/yum.png differ diff --git a/public/images/emoji/emoji_one/zap.png b/public/images/emoji/emoji_one/zap.png new file mode 100644 index 0000000000..97e8cd8e4a Binary files /dev/null and b/public/images/emoji/emoji_one/zap.png differ diff --git a/public/images/emoji/emoji_one/zero.png b/public/images/emoji/emoji_one/zero.png new file mode 100644 index 0000000000..8de44baeb3 Binary files /dev/null and b/public/images/emoji/emoji_one/zero.png differ diff --git a/plugins/emoji/public/images/unicode/1f4a4.png b/public/images/emoji/emoji_one/zzz.png old mode 100755 new mode 100644 similarity index 100% rename from plugins/emoji/public/images/unicode/1f4a4.png rename to public/images/emoji/emoji_one/zzz.png diff --git a/public/images/emoji/google/+1.png b/public/images/emoji/google/+1.png new file mode 100644 index 0000000000..b997ff3e9f Binary files /dev/null and b/public/images/emoji/google/+1.png differ diff --git a/public/images/emoji/google/-1.png b/public/images/emoji/google/-1.png new file mode 100644 index 0000000000..bd9730bfa4 Binary files /dev/null and b/public/images/emoji/google/-1.png differ diff --git a/public/images/emoji/google/100.png b/public/images/emoji/google/100.png new file mode 100644 index 0000000000..75fd130b23 Binary files /dev/null and b/public/images/emoji/google/100.png differ diff --git a/public/images/emoji/google/1234.png b/public/images/emoji/google/1234.png new file mode 100644 index 0000000000..1f2781eed9 Binary files /dev/null and b/public/images/emoji/google/1234.png differ diff --git a/public/images/emoji/google/8ball.png b/public/images/emoji/google/8ball.png new file mode 100644 index 0000000000..b9c77c88e5 Binary files /dev/null and b/public/images/emoji/google/8ball.png differ diff --git a/public/images/emoji/google/a.png b/public/images/emoji/google/a.png new file mode 100644 index 0000000000..5cd1836610 Binary files /dev/null and b/public/images/emoji/google/a.png differ diff --git a/public/images/emoji/google/ab.png b/public/images/emoji/google/ab.png new file mode 100644 index 0000000000..8b8c9f60f3 Binary files /dev/null and b/public/images/emoji/google/ab.png differ diff --git a/public/images/emoji/google/abc.png b/public/images/emoji/google/abc.png new file mode 100644 index 0000000000..90be89447d Binary files /dev/null and b/public/images/emoji/google/abc.png differ diff --git a/public/images/emoji/google/abcd.png b/public/images/emoji/google/abcd.png new file mode 100644 index 0000000000..0ddc39ad5a Binary files /dev/null and b/public/images/emoji/google/abcd.png differ diff --git a/public/images/emoji/google/accept.png b/public/images/emoji/google/accept.png new file mode 100644 index 0000000000..31b6890f8c Binary files /dev/null and b/public/images/emoji/google/accept.png differ diff --git a/public/images/emoji/google/aerial_tramway.png b/public/images/emoji/google/aerial_tramway.png new file mode 100644 index 0000000000..adf58bbe98 Binary files /dev/null and b/public/images/emoji/google/aerial_tramway.png differ diff --git a/public/images/emoji/google/airplane.png b/public/images/emoji/google/airplane.png new file mode 100644 index 0000000000..7fdbca09e6 Binary files /dev/null and b/public/images/emoji/google/airplane.png differ diff --git a/public/images/emoji/google/alarm_clock.png b/public/images/emoji/google/alarm_clock.png new file mode 100644 index 0000000000..90fd1620ad Binary files /dev/null and b/public/images/emoji/google/alarm_clock.png differ diff --git a/public/images/emoji/google/alien.png b/public/images/emoji/google/alien.png new file mode 100644 index 0000000000..15552a1a14 Binary files /dev/null and b/public/images/emoji/google/alien.png differ diff --git a/public/images/emoji/google/ambulance.png b/public/images/emoji/google/ambulance.png new file mode 100644 index 0000000000..e79610e6fa Binary files /dev/null and b/public/images/emoji/google/ambulance.png differ diff --git a/public/images/emoji/google/anchor.png b/public/images/emoji/google/anchor.png new file mode 100644 index 0000000000..78f95afd76 Binary files /dev/null and b/public/images/emoji/google/anchor.png differ diff --git a/public/images/emoji/google/angel.png b/public/images/emoji/google/angel.png new file mode 100644 index 0000000000..a894d6ccbf Binary files /dev/null and b/public/images/emoji/google/angel.png differ diff --git a/public/images/emoji/google/anger.png b/public/images/emoji/google/anger.png new file mode 100644 index 0000000000..ecd55a67f2 Binary files /dev/null and b/public/images/emoji/google/anger.png differ diff --git a/public/images/emoji/google/angry.png b/public/images/emoji/google/angry.png new file mode 100644 index 0000000000..5bbd6c2395 Binary files /dev/null and b/public/images/emoji/google/angry.png differ diff --git a/public/images/emoji/google/anguished.png b/public/images/emoji/google/anguished.png new file mode 100644 index 0000000000..3d2727e440 Binary files /dev/null and b/public/images/emoji/google/anguished.png differ diff --git a/public/images/emoji/google/ant.png b/public/images/emoji/google/ant.png new file mode 100644 index 0000000000..dc92a8025c Binary files /dev/null and b/public/images/emoji/google/ant.png differ diff --git a/public/images/emoji/google/apple.png b/public/images/emoji/google/apple.png new file mode 100644 index 0000000000..fe2db38113 Binary files /dev/null and b/public/images/emoji/google/apple.png differ diff --git a/public/images/emoji/google/aquarius.png b/public/images/emoji/google/aquarius.png new file mode 100644 index 0000000000..3d76e55381 Binary files /dev/null and b/public/images/emoji/google/aquarius.png differ diff --git a/public/images/emoji/google/aries.png b/public/images/emoji/google/aries.png new file mode 100644 index 0000000000..44762b66a3 Binary files /dev/null and b/public/images/emoji/google/aries.png differ diff --git a/public/images/emoji/google/arrow_backward.png b/public/images/emoji/google/arrow_backward.png new file mode 100644 index 0000000000..76ad862137 Binary files /dev/null and b/public/images/emoji/google/arrow_backward.png differ diff --git a/public/images/emoji/google/arrow_double_down.png b/public/images/emoji/google/arrow_double_down.png new file mode 100644 index 0000000000..241355de28 Binary files /dev/null and b/public/images/emoji/google/arrow_double_down.png differ diff --git a/public/images/emoji/google/arrow_double_up.png b/public/images/emoji/google/arrow_double_up.png new file mode 100644 index 0000000000..516383d82c Binary files /dev/null and b/public/images/emoji/google/arrow_double_up.png differ diff --git a/public/images/emoji/google/arrow_down.png b/public/images/emoji/google/arrow_down.png new file mode 100644 index 0000000000..fa55f985b7 Binary files /dev/null and b/public/images/emoji/google/arrow_down.png differ diff --git a/public/images/emoji/google/arrow_down_small.png b/public/images/emoji/google/arrow_down_small.png new file mode 100644 index 0000000000..1f3fac3d10 Binary files /dev/null and b/public/images/emoji/google/arrow_down_small.png differ diff --git a/public/images/emoji/google/arrow_forward.png b/public/images/emoji/google/arrow_forward.png new file mode 100644 index 0000000000..4766f1e092 Binary files /dev/null and b/public/images/emoji/google/arrow_forward.png differ diff --git a/public/images/emoji/google/arrow_heading_down.png b/public/images/emoji/google/arrow_heading_down.png new file mode 100644 index 0000000000..de7e9c1a4a Binary files /dev/null and b/public/images/emoji/google/arrow_heading_down.png differ diff --git a/public/images/emoji/google/arrow_heading_up.png b/public/images/emoji/google/arrow_heading_up.png new file mode 100644 index 0000000000..d8356bedb2 Binary files /dev/null and b/public/images/emoji/google/arrow_heading_up.png differ diff --git a/public/images/emoji/google/arrow_left.png b/public/images/emoji/google/arrow_left.png new file mode 100644 index 0000000000..0abbb306da Binary files /dev/null and b/public/images/emoji/google/arrow_left.png differ diff --git a/public/images/emoji/google/arrow_lower_left.png b/public/images/emoji/google/arrow_lower_left.png new file mode 100644 index 0000000000..6e2f370712 Binary files /dev/null and b/public/images/emoji/google/arrow_lower_left.png differ diff --git a/public/images/emoji/google/arrow_lower_right.png b/public/images/emoji/google/arrow_lower_right.png new file mode 100644 index 0000000000..7949e5fbba Binary files /dev/null and b/public/images/emoji/google/arrow_lower_right.png differ diff --git a/public/images/emoji/google/arrow_right.png b/public/images/emoji/google/arrow_right.png new file mode 100644 index 0000000000..41bc3f68a6 Binary files /dev/null and b/public/images/emoji/google/arrow_right.png differ diff --git a/public/images/emoji/google/arrow_right_hook.png b/public/images/emoji/google/arrow_right_hook.png new file mode 100644 index 0000000000..448cdb3992 Binary files /dev/null and b/public/images/emoji/google/arrow_right_hook.png differ diff --git a/public/images/emoji/google/arrow_up.png b/public/images/emoji/google/arrow_up.png new file mode 100644 index 0000000000..c39e326cae Binary files /dev/null and b/public/images/emoji/google/arrow_up.png differ diff --git a/public/images/emoji/google/arrow_up_down.png b/public/images/emoji/google/arrow_up_down.png new file mode 100644 index 0000000000..a3f4c1bfe9 Binary files /dev/null and b/public/images/emoji/google/arrow_up_down.png differ diff --git a/public/images/emoji/google/arrow_up_small.png b/public/images/emoji/google/arrow_up_small.png new file mode 100644 index 0000000000..4c880fc967 Binary files /dev/null and b/public/images/emoji/google/arrow_up_small.png differ diff --git a/public/images/emoji/google/arrow_upper_left.png b/public/images/emoji/google/arrow_upper_left.png new file mode 100644 index 0000000000..91f50bdcd4 Binary files /dev/null and b/public/images/emoji/google/arrow_upper_left.png differ diff --git a/public/images/emoji/google/arrow_upper_right.png b/public/images/emoji/google/arrow_upper_right.png new file mode 100644 index 0000000000..9ffce9c00c Binary files /dev/null and b/public/images/emoji/google/arrow_upper_right.png differ diff --git a/public/images/emoji/google/arrows_clockwise.png b/public/images/emoji/google/arrows_clockwise.png new file mode 100644 index 0000000000..b9991d29b3 Binary files /dev/null and b/public/images/emoji/google/arrows_clockwise.png differ diff --git a/public/images/emoji/google/arrows_counterclockwise.png b/public/images/emoji/google/arrows_counterclockwise.png new file mode 100644 index 0000000000..b2011d32d8 Binary files /dev/null and b/public/images/emoji/google/arrows_counterclockwise.png differ diff --git a/public/images/emoji/google/art.png b/public/images/emoji/google/art.png new file mode 100644 index 0000000000..bbb7f9a33e Binary files /dev/null and b/public/images/emoji/google/art.png differ diff --git a/public/images/emoji/google/articulated_lorry.png b/public/images/emoji/google/articulated_lorry.png new file mode 100644 index 0000000000..0cf4f204a7 Binary files /dev/null and b/public/images/emoji/google/articulated_lorry.png differ diff --git a/public/images/emoji/google/astonished.png b/public/images/emoji/google/astonished.png new file mode 100644 index 0000000000..fe2354561f Binary files /dev/null and b/public/images/emoji/google/astonished.png differ diff --git a/public/images/emoji/google/athletic_shoe.png b/public/images/emoji/google/athletic_shoe.png new file mode 100644 index 0000000000..194dd323d4 Binary files /dev/null and b/public/images/emoji/google/athletic_shoe.png differ diff --git a/public/images/emoji/google/atm.png b/public/images/emoji/google/atm.png new file mode 100644 index 0000000000..aa712b55be Binary files /dev/null and b/public/images/emoji/google/atm.png differ diff --git a/public/images/emoji/google/b.png b/public/images/emoji/google/b.png new file mode 100644 index 0000000000..daf24fee43 Binary files /dev/null and b/public/images/emoji/google/b.png differ diff --git a/public/images/emoji/google/baby.png b/public/images/emoji/google/baby.png new file mode 100644 index 0000000000..fb32a0f4d3 Binary files /dev/null and b/public/images/emoji/google/baby.png differ diff --git a/public/images/emoji/google/baby_bottle.png b/public/images/emoji/google/baby_bottle.png new file mode 100644 index 0000000000..fc65ec3ad6 Binary files /dev/null and b/public/images/emoji/google/baby_bottle.png differ diff --git a/public/images/emoji/google/baby_chick.png b/public/images/emoji/google/baby_chick.png new file mode 100644 index 0000000000..55730e4e94 Binary files /dev/null and b/public/images/emoji/google/baby_chick.png differ diff --git a/public/images/emoji/google/baby_symbol.png b/public/images/emoji/google/baby_symbol.png new file mode 100644 index 0000000000..85a20b6a7d Binary files /dev/null and b/public/images/emoji/google/baby_symbol.png differ diff --git a/public/images/emoji/google/back.png b/public/images/emoji/google/back.png new file mode 100644 index 0000000000..49a9449d28 Binary files /dev/null and b/public/images/emoji/google/back.png differ diff --git a/public/images/emoji/google/baggage_claim.png b/public/images/emoji/google/baggage_claim.png new file mode 100644 index 0000000000..1a9441a40d Binary files /dev/null and b/public/images/emoji/google/baggage_claim.png differ diff --git a/public/images/emoji/google/balloon.png b/public/images/emoji/google/balloon.png new file mode 100644 index 0000000000..1cfc5be9f5 Binary files /dev/null and b/public/images/emoji/google/balloon.png differ diff --git a/public/images/emoji/google/ballot_box_with_check.png b/public/images/emoji/google/ballot_box_with_check.png new file mode 100644 index 0000000000..1610e6e103 Binary files /dev/null and b/public/images/emoji/google/ballot_box_with_check.png differ diff --git a/public/images/emoji/google/bamboo.png b/public/images/emoji/google/bamboo.png new file mode 100644 index 0000000000..1b1423823e Binary files /dev/null and b/public/images/emoji/google/bamboo.png differ diff --git a/public/images/emoji/google/banana.png b/public/images/emoji/google/banana.png new file mode 100644 index 0000000000..84cca0dba4 Binary files /dev/null and b/public/images/emoji/google/banana.png differ diff --git a/public/images/emoji/google/bangbang.png b/public/images/emoji/google/bangbang.png new file mode 100644 index 0000000000..8bb3afd93f Binary files /dev/null and b/public/images/emoji/google/bangbang.png differ diff --git a/public/images/emoji/google/bank.png b/public/images/emoji/google/bank.png new file mode 100644 index 0000000000..a2d2387331 Binary files /dev/null and b/public/images/emoji/google/bank.png differ diff --git a/public/images/emoji/google/bar_chart.png b/public/images/emoji/google/bar_chart.png new file mode 100644 index 0000000000..bb2c08eb49 Binary files /dev/null and b/public/images/emoji/google/bar_chart.png differ diff --git a/public/images/emoji/google/barber.png b/public/images/emoji/google/barber.png new file mode 100644 index 0000000000..0034df6031 Binary files /dev/null and b/public/images/emoji/google/barber.png differ diff --git a/public/images/emoji/google/baseball.png b/public/images/emoji/google/baseball.png new file mode 100644 index 0000000000..1eab7d9e9b Binary files /dev/null and b/public/images/emoji/google/baseball.png differ diff --git a/public/images/emoji/google/basketball.png b/public/images/emoji/google/basketball.png new file mode 100644 index 0000000000..cb52921e30 Binary files /dev/null and b/public/images/emoji/google/basketball.png differ diff --git a/public/images/emoji/google/bath.png b/public/images/emoji/google/bath.png new file mode 100644 index 0000000000..a91da05e53 Binary files /dev/null and b/public/images/emoji/google/bath.png differ diff --git a/public/images/emoji/google/bathtub.png b/public/images/emoji/google/bathtub.png new file mode 100644 index 0000000000..ca11175ffb Binary files /dev/null and b/public/images/emoji/google/bathtub.png differ diff --git a/public/images/emoji/google/battery.png b/public/images/emoji/google/battery.png new file mode 100644 index 0000000000..2a010860e4 Binary files /dev/null and b/public/images/emoji/google/battery.png differ diff --git a/public/images/emoji/google/bear.png b/public/images/emoji/google/bear.png new file mode 100644 index 0000000000..1a167eff48 Binary files /dev/null and b/public/images/emoji/google/bear.png differ diff --git a/public/images/emoji/google/bee.png b/public/images/emoji/google/bee.png new file mode 100644 index 0000000000..20a86eb54b Binary files /dev/null and b/public/images/emoji/google/bee.png differ diff --git a/public/images/emoji/google/beer.png b/public/images/emoji/google/beer.png new file mode 100644 index 0000000000..5c4c4002d7 Binary files /dev/null and b/public/images/emoji/google/beer.png differ diff --git a/public/images/emoji/google/beers.png b/public/images/emoji/google/beers.png new file mode 100644 index 0000000000..2767b1a626 Binary files /dev/null and b/public/images/emoji/google/beers.png differ diff --git a/public/images/emoji/google/beetle.png b/public/images/emoji/google/beetle.png new file mode 100644 index 0000000000..d8d410788f Binary files /dev/null and b/public/images/emoji/google/beetle.png differ diff --git a/public/images/emoji/google/beginner.png b/public/images/emoji/google/beginner.png new file mode 100644 index 0000000000..c8fbbadad4 Binary files /dev/null and b/public/images/emoji/google/beginner.png differ diff --git a/public/images/emoji/google/bell.png b/public/images/emoji/google/bell.png new file mode 100644 index 0000000000..4dce6ef261 Binary files /dev/null and b/public/images/emoji/google/bell.png differ diff --git a/public/images/emoji/google/bento.png b/public/images/emoji/google/bento.png new file mode 100644 index 0000000000..7fce1490fa Binary files /dev/null and b/public/images/emoji/google/bento.png differ diff --git a/public/images/emoji/google/bicyclist.png b/public/images/emoji/google/bicyclist.png new file mode 100644 index 0000000000..11faa4e066 Binary files /dev/null and b/public/images/emoji/google/bicyclist.png differ diff --git a/public/images/emoji/google/bike.png b/public/images/emoji/google/bike.png new file mode 100644 index 0000000000..30e59c9099 Binary files /dev/null and b/public/images/emoji/google/bike.png differ diff --git a/public/images/emoji/google/bikini.png b/public/images/emoji/google/bikini.png new file mode 100644 index 0000000000..8e4c392f72 Binary files /dev/null and b/public/images/emoji/google/bikini.png differ diff --git a/public/images/emoji/google/bird.png b/public/images/emoji/google/bird.png new file mode 100644 index 0000000000..5755a25acb Binary files /dev/null and b/public/images/emoji/google/bird.png differ diff --git a/public/images/emoji/google/birthday.png b/public/images/emoji/google/birthday.png new file mode 100644 index 0000000000..9b93153787 Binary files /dev/null and b/public/images/emoji/google/birthday.png differ diff --git a/public/images/emoji/google/black_circle.png b/public/images/emoji/google/black_circle.png new file mode 100644 index 0000000000..6706b3023c Binary files /dev/null and b/public/images/emoji/google/black_circle.png differ diff --git a/public/images/emoji/google/black_joker.png b/public/images/emoji/google/black_joker.png new file mode 100644 index 0000000000..10673f6aa5 Binary files /dev/null and b/public/images/emoji/google/black_joker.png differ diff --git a/public/images/emoji/google/black_large_square.png b/public/images/emoji/google/black_large_square.png new file mode 100644 index 0000000000..42da2c7f2c Binary files /dev/null and b/public/images/emoji/google/black_large_square.png differ diff --git a/public/images/emoji/google/black_medium_small_square.png b/public/images/emoji/google/black_medium_small_square.png new file mode 100644 index 0000000000..c1883976f2 Binary files /dev/null and b/public/images/emoji/google/black_medium_small_square.png differ diff --git a/public/images/emoji/google/black_medium_square.png b/public/images/emoji/google/black_medium_square.png new file mode 100644 index 0000000000..bdb623aa66 Binary files /dev/null and b/public/images/emoji/google/black_medium_square.png differ diff --git a/public/images/emoji/google/black_nib.png b/public/images/emoji/google/black_nib.png new file mode 100644 index 0000000000..4aff9f8783 Binary files /dev/null and b/public/images/emoji/google/black_nib.png differ diff --git a/public/images/emoji/google/black_small_square.png b/public/images/emoji/google/black_small_square.png new file mode 100644 index 0000000000..09aba5f4c8 Binary files /dev/null and b/public/images/emoji/google/black_small_square.png differ diff --git a/public/images/emoji/google/black_square_button.png b/public/images/emoji/google/black_square_button.png new file mode 100644 index 0000000000..420b94b313 Binary files /dev/null and b/public/images/emoji/google/black_square_button.png differ diff --git a/public/images/emoji/google/blossom.png b/public/images/emoji/google/blossom.png new file mode 100644 index 0000000000..1633b97721 Binary files /dev/null and b/public/images/emoji/google/blossom.png differ diff --git a/public/images/emoji/google/blowfish.png b/public/images/emoji/google/blowfish.png new file mode 100644 index 0000000000..ca260c5db0 Binary files /dev/null and b/public/images/emoji/google/blowfish.png differ diff --git a/public/images/emoji/google/blue_book.png b/public/images/emoji/google/blue_book.png new file mode 100644 index 0000000000..f964968ee4 Binary files /dev/null and b/public/images/emoji/google/blue_book.png differ diff --git a/public/images/emoji/google/blue_car.png b/public/images/emoji/google/blue_car.png new file mode 100644 index 0000000000..d7e12ace43 Binary files /dev/null and b/public/images/emoji/google/blue_car.png differ diff --git a/public/images/emoji/google/blue_heart.png b/public/images/emoji/google/blue_heart.png new file mode 100644 index 0000000000..d9b316b307 Binary files /dev/null and b/public/images/emoji/google/blue_heart.png differ diff --git a/public/images/emoji/google/blush.png b/public/images/emoji/google/blush.png new file mode 100644 index 0000000000..9d6c9718ca Binary files /dev/null and b/public/images/emoji/google/blush.png differ diff --git a/public/images/emoji/google/boar.png b/public/images/emoji/google/boar.png new file mode 100644 index 0000000000..cf0feaa969 Binary files /dev/null and b/public/images/emoji/google/boar.png differ diff --git a/public/images/emoji/google/boat.png b/public/images/emoji/google/boat.png new file mode 100644 index 0000000000..2e61e60267 Binary files /dev/null and b/public/images/emoji/google/boat.png differ diff --git a/public/images/emoji/google/bomb.png b/public/images/emoji/google/bomb.png new file mode 100644 index 0000000000..c0be032dfa Binary files /dev/null and b/public/images/emoji/google/bomb.png differ diff --git a/public/images/emoji/google/book.png b/public/images/emoji/google/book.png new file mode 100644 index 0000000000..77ee57a108 Binary files /dev/null and b/public/images/emoji/google/book.png differ diff --git a/public/images/emoji/google/bookmark.png b/public/images/emoji/google/bookmark.png new file mode 100644 index 0000000000..e576cd8fd2 Binary files /dev/null and b/public/images/emoji/google/bookmark.png differ diff --git a/public/images/emoji/google/bookmark_tabs.png b/public/images/emoji/google/bookmark_tabs.png new file mode 100644 index 0000000000..85894a8616 Binary files /dev/null and b/public/images/emoji/google/bookmark_tabs.png differ diff --git a/public/images/emoji/google/books.png b/public/images/emoji/google/books.png new file mode 100644 index 0000000000..689dacf7bb Binary files /dev/null and b/public/images/emoji/google/books.png differ diff --git a/public/images/emoji/google/boom.png b/public/images/emoji/google/boom.png new file mode 100644 index 0000000000..ae9722a56c Binary files /dev/null and b/public/images/emoji/google/boom.png differ diff --git a/public/images/emoji/google/boot.png b/public/images/emoji/google/boot.png new file mode 100644 index 0000000000..c09d55389f Binary files /dev/null and b/public/images/emoji/google/boot.png differ diff --git a/public/images/emoji/google/bouquet.png b/public/images/emoji/google/bouquet.png new file mode 100644 index 0000000000..6d0d6f9e67 Binary files /dev/null and b/public/images/emoji/google/bouquet.png differ diff --git a/public/images/emoji/google/bow.png b/public/images/emoji/google/bow.png new file mode 100644 index 0000000000..ce17dd2815 Binary files /dev/null and b/public/images/emoji/google/bow.png differ diff --git a/public/images/emoji/google/bowling.png b/public/images/emoji/google/bowling.png new file mode 100644 index 0000000000..63856291e7 Binary files /dev/null and b/public/images/emoji/google/bowling.png differ diff --git a/public/images/emoji/google/boy.png b/public/images/emoji/google/boy.png new file mode 100644 index 0000000000..6e66a700ed Binary files /dev/null and b/public/images/emoji/google/boy.png differ diff --git a/public/images/emoji/google/bread.png b/public/images/emoji/google/bread.png new file mode 100644 index 0000000000..9778f2e018 Binary files /dev/null and b/public/images/emoji/google/bread.png differ diff --git a/public/images/emoji/google/bride_with_veil.png b/public/images/emoji/google/bride_with_veil.png new file mode 100644 index 0000000000..204f8ab2ae Binary files /dev/null and b/public/images/emoji/google/bride_with_veil.png differ diff --git a/public/images/emoji/google/bridge_at_night.png b/public/images/emoji/google/bridge_at_night.png new file mode 100644 index 0000000000..b708eafe8b Binary files /dev/null and b/public/images/emoji/google/bridge_at_night.png differ diff --git a/public/images/emoji/google/briefcase.png b/public/images/emoji/google/briefcase.png new file mode 100644 index 0000000000..c2051afc86 Binary files /dev/null and b/public/images/emoji/google/briefcase.png differ diff --git a/public/images/emoji/google/broken_heart.png b/public/images/emoji/google/broken_heart.png new file mode 100644 index 0000000000..3d2f386047 Binary files /dev/null and b/public/images/emoji/google/broken_heart.png differ diff --git a/public/images/emoji/google/bug.png b/public/images/emoji/google/bug.png new file mode 100644 index 0000000000..2785722e53 Binary files /dev/null and b/public/images/emoji/google/bug.png differ diff --git a/public/images/emoji/google/bulb.png b/public/images/emoji/google/bulb.png new file mode 100644 index 0000000000..8be12e3a91 Binary files /dev/null and b/public/images/emoji/google/bulb.png differ diff --git a/public/images/emoji/google/bullettrain_front.png b/public/images/emoji/google/bullettrain_front.png new file mode 100644 index 0000000000..96b42367d1 Binary files /dev/null and b/public/images/emoji/google/bullettrain_front.png differ diff --git a/public/images/emoji/google/bullettrain_side.png b/public/images/emoji/google/bullettrain_side.png new file mode 100644 index 0000000000..4ac1ceadf4 Binary files /dev/null and b/public/images/emoji/google/bullettrain_side.png differ diff --git a/public/images/emoji/google/bus.png b/public/images/emoji/google/bus.png new file mode 100644 index 0000000000..c16cae9a45 Binary files /dev/null and b/public/images/emoji/google/bus.png differ diff --git a/public/images/emoji/google/busstop.png b/public/images/emoji/google/busstop.png new file mode 100644 index 0000000000..c411b70aea Binary files /dev/null and b/public/images/emoji/google/busstop.png differ diff --git a/public/images/emoji/google/bust_in_silhouette.png b/public/images/emoji/google/bust_in_silhouette.png new file mode 100644 index 0000000000..57ef62dd73 Binary files /dev/null and b/public/images/emoji/google/bust_in_silhouette.png differ diff --git a/public/images/emoji/google/busts_in_silhouette.png b/public/images/emoji/google/busts_in_silhouette.png new file mode 100644 index 0000000000..c2f0c35342 Binary files /dev/null and b/public/images/emoji/google/busts_in_silhouette.png differ diff --git a/public/images/emoji/google/cactus.png b/public/images/emoji/google/cactus.png new file mode 100644 index 0000000000..9d654ef035 Binary files /dev/null and b/public/images/emoji/google/cactus.png differ diff --git a/public/images/emoji/google/cake.png b/public/images/emoji/google/cake.png new file mode 100644 index 0000000000..f97c84e044 Binary files /dev/null and b/public/images/emoji/google/cake.png differ diff --git a/public/images/emoji/google/calendar.png b/public/images/emoji/google/calendar.png new file mode 100644 index 0000000000..6d04ca74d8 Binary files /dev/null and b/public/images/emoji/google/calendar.png differ diff --git a/public/images/emoji/google/calling.png b/public/images/emoji/google/calling.png new file mode 100644 index 0000000000..16a9320bfc Binary files /dev/null and b/public/images/emoji/google/calling.png differ diff --git a/public/images/emoji/google/camel.png b/public/images/emoji/google/camel.png new file mode 100644 index 0000000000..6396e59bae Binary files /dev/null and b/public/images/emoji/google/camel.png differ diff --git a/public/images/emoji/google/camera.png b/public/images/emoji/google/camera.png new file mode 100644 index 0000000000..a91232b652 Binary files /dev/null and b/public/images/emoji/google/camera.png differ diff --git a/public/images/emoji/google/cancer.png b/public/images/emoji/google/cancer.png new file mode 100644 index 0000000000..c4c83af8a6 Binary files /dev/null and b/public/images/emoji/google/cancer.png differ diff --git a/public/images/emoji/google/candy.png b/public/images/emoji/google/candy.png new file mode 100644 index 0000000000..d06237f5ff Binary files /dev/null and b/public/images/emoji/google/candy.png differ diff --git a/public/images/emoji/google/capital_abcd.png b/public/images/emoji/google/capital_abcd.png new file mode 100644 index 0000000000..2d9706c46c Binary files /dev/null and b/public/images/emoji/google/capital_abcd.png differ diff --git a/public/images/emoji/google/capricorn.png b/public/images/emoji/google/capricorn.png new file mode 100644 index 0000000000..2c48fa50f7 Binary files /dev/null and b/public/images/emoji/google/capricorn.png differ diff --git a/public/images/emoji/google/car.png b/public/images/emoji/google/car.png new file mode 100644 index 0000000000..f1be3d95ae Binary files /dev/null and b/public/images/emoji/google/car.png differ diff --git a/public/images/emoji/google/card_index.png b/public/images/emoji/google/card_index.png new file mode 100644 index 0000000000..b1c6e85cd9 Binary files /dev/null and b/public/images/emoji/google/card_index.png differ diff --git a/public/images/emoji/google/carousel_horse.png b/public/images/emoji/google/carousel_horse.png new file mode 100644 index 0000000000..4a6aed313e Binary files /dev/null and b/public/images/emoji/google/carousel_horse.png differ diff --git a/public/images/emoji/google/cat.png b/public/images/emoji/google/cat.png new file mode 100644 index 0000000000..10723a9835 Binary files /dev/null and b/public/images/emoji/google/cat.png differ diff --git a/public/images/emoji/google/cat2.png b/public/images/emoji/google/cat2.png new file mode 100644 index 0000000000..c4db7e61fe Binary files /dev/null and b/public/images/emoji/google/cat2.png differ diff --git a/public/images/emoji/google/cd.png b/public/images/emoji/google/cd.png new file mode 100644 index 0000000000..a2965af89d Binary files /dev/null and b/public/images/emoji/google/cd.png differ diff --git a/public/images/emoji/google/chart.png b/public/images/emoji/google/chart.png new file mode 100644 index 0000000000..80c3e7355e Binary files /dev/null and b/public/images/emoji/google/chart.png differ diff --git a/public/images/emoji/google/chart_with_downwards_trend.png b/public/images/emoji/google/chart_with_downwards_trend.png new file mode 100644 index 0000000000..48b84869d1 Binary files /dev/null and b/public/images/emoji/google/chart_with_downwards_trend.png differ diff --git a/public/images/emoji/google/chart_with_upwards_trend.png b/public/images/emoji/google/chart_with_upwards_trend.png new file mode 100644 index 0000000000..1c952e0756 Binary files /dev/null and b/public/images/emoji/google/chart_with_upwards_trend.png differ diff --git a/public/images/emoji/google/checkered_flag.png b/public/images/emoji/google/checkered_flag.png new file mode 100644 index 0000000000..6de617fb76 Binary files /dev/null and b/public/images/emoji/google/checkered_flag.png differ diff --git a/public/images/emoji/google/cherries.png b/public/images/emoji/google/cherries.png new file mode 100644 index 0000000000..857d3f8a9e Binary files /dev/null and b/public/images/emoji/google/cherries.png differ diff --git a/public/images/emoji/google/cherry_blossom.png b/public/images/emoji/google/cherry_blossom.png new file mode 100644 index 0000000000..cde001ba45 Binary files /dev/null and b/public/images/emoji/google/cherry_blossom.png differ diff --git a/public/images/emoji/google/chestnut.png b/public/images/emoji/google/chestnut.png new file mode 100644 index 0000000000..feb11f8e23 Binary files /dev/null and b/public/images/emoji/google/chestnut.png differ diff --git a/public/images/emoji/google/chicken.png b/public/images/emoji/google/chicken.png new file mode 100644 index 0000000000..410a89e560 Binary files /dev/null and b/public/images/emoji/google/chicken.png differ diff --git a/public/images/emoji/google/children_crossing.png b/public/images/emoji/google/children_crossing.png new file mode 100644 index 0000000000..e87942b783 Binary files /dev/null and b/public/images/emoji/google/children_crossing.png differ diff --git a/public/images/emoji/google/chocolate_bar.png b/public/images/emoji/google/chocolate_bar.png new file mode 100644 index 0000000000..ac6f2946da Binary files /dev/null and b/public/images/emoji/google/chocolate_bar.png differ diff --git a/public/images/emoji/google/christmas_tree.png b/public/images/emoji/google/christmas_tree.png new file mode 100644 index 0000000000..26fc2c19cf Binary files /dev/null and b/public/images/emoji/google/christmas_tree.png differ diff --git a/public/images/emoji/google/church.png b/public/images/emoji/google/church.png new file mode 100644 index 0000000000..a1d51b3439 Binary files /dev/null and b/public/images/emoji/google/church.png differ diff --git a/public/images/emoji/google/cinema.png b/public/images/emoji/google/cinema.png new file mode 100644 index 0000000000..b9fcfeb0bb Binary files /dev/null and b/public/images/emoji/google/cinema.png differ diff --git a/public/images/emoji/google/circus_tent.png b/public/images/emoji/google/circus_tent.png new file mode 100644 index 0000000000..8fdb99af61 Binary files /dev/null and b/public/images/emoji/google/circus_tent.png differ diff --git a/public/images/emoji/google/city_sunrise.png b/public/images/emoji/google/city_sunrise.png new file mode 100644 index 0000000000..6198cb6994 Binary files /dev/null and b/public/images/emoji/google/city_sunrise.png differ diff --git a/public/images/emoji/google/city_sunset.png b/public/images/emoji/google/city_sunset.png new file mode 100644 index 0000000000..8c1ab36688 Binary files /dev/null and b/public/images/emoji/google/city_sunset.png differ diff --git a/public/images/emoji/google/cl.png b/public/images/emoji/google/cl.png new file mode 100644 index 0000000000..d26a9ab19c Binary files /dev/null and b/public/images/emoji/google/cl.png differ diff --git a/public/images/emoji/google/clap.png b/public/images/emoji/google/clap.png new file mode 100644 index 0000000000..7451fa022b Binary files /dev/null and b/public/images/emoji/google/clap.png differ diff --git a/public/images/emoji/google/clapper.png b/public/images/emoji/google/clapper.png new file mode 100644 index 0000000000..5f3ca47863 Binary files /dev/null and b/public/images/emoji/google/clapper.png differ diff --git a/public/images/emoji/google/clipboard.png b/public/images/emoji/google/clipboard.png new file mode 100644 index 0000000000..8d7999696e Binary files /dev/null and b/public/images/emoji/google/clipboard.png differ diff --git a/public/images/emoji/google/clock1.png b/public/images/emoji/google/clock1.png new file mode 100644 index 0000000000..99cb83a797 Binary files /dev/null and b/public/images/emoji/google/clock1.png differ diff --git a/public/images/emoji/google/clock10.png b/public/images/emoji/google/clock10.png new file mode 100644 index 0000000000..6608a21774 Binary files /dev/null and b/public/images/emoji/google/clock10.png differ diff --git a/public/images/emoji/google/clock1030.png b/public/images/emoji/google/clock1030.png new file mode 100644 index 0000000000..3b4d7c44e8 Binary files /dev/null and b/public/images/emoji/google/clock1030.png differ diff --git a/public/images/emoji/google/clock11.png b/public/images/emoji/google/clock11.png new file mode 100644 index 0000000000..640bb37ac1 Binary files /dev/null and b/public/images/emoji/google/clock11.png differ diff --git a/public/images/emoji/google/clock1130.png b/public/images/emoji/google/clock1130.png new file mode 100644 index 0000000000..2d52629fe6 Binary files /dev/null and b/public/images/emoji/google/clock1130.png differ diff --git a/public/images/emoji/google/clock12.png b/public/images/emoji/google/clock12.png new file mode 100644 index 0000000000..adac4677fd Binary files /dev/null and b/public/images/emoji/google/clock12.png differ diff --git a/public/images/emoji/google/clock1230.png b/public/images/emoji/google/clock1230.png new file mode 100644 index 0000000000..36a40d1f9e Binary files /dev/null and b/public/images/emoji/google/clock1230.png differ diff --git a/public/images/emoji/google/clock130.png b/public/images/emoji/google/clock130.png new file mode 100644 index 0000000000..59db85445f Binary files /dev/null and b/public/images/emoji/google/clock130.png differ diff --git a/public/images/emoji/google/clock2.png b/public/images/emoji/google/clock2.png new file mode 100644 index 0000000000..2f18e80916 Binary files /dev/null and b/public/images/emoji/google/clock2.png differ diff --git a/public/images/emoji/google/clock230.png b/public/images/emoji/google/clock230.png new file mode 100644 index 0000000000..96fc98d4a1 Binary files /dev/null and b/public/images/emoji/google/clock230.png differ diff --git a/public/images/emoji/google/clock3.png b/public/images/emoji/google/clock3.png new file mode 100644 index 0000000000..203e102f11 Binary files /dev/null and b/public/images/emoji/google/clock3.png differ diff --git a/public/images/emoji/google/clock330.png b/public/images/emoji/google/clock330.png new file mode 100644 index 0000000000..05abb9a661 Binary files /dev/null and b/public/images/emoji/google/clock330.png differ diff --git a/public/images/emoji/google/clock4.png b/public/images/emoji/google/clock4.png new file mode 100644 index 0000000000..d0cd9d61ee Binary files /dev/null and b/public/images/emoji/google/clock4.png differ diff --git a/public/images/emoji/google/clock430.png b/public/images/emoji/google/clock430.png new file mode 100644 index 0000000000..fac985a8f3 Binary files /dev/null and b/public/images/emoji/google/clock430.png differ diff --git a/public/images/emoji/google/clock5.png b/public/images/emoji/google/clock5.png new file mode 100644 index 0000000000..050efd351f Binary files /dev/null and b/public/images/emoji/google/clock5.png differ diff --git a/public/images/emoji/google/clock530.png b/public/images/emoji/google/clock530.png new file mode 100644 index 0000000000..1a46f7fce6 Binary files /dev/null and b/public/images/emoji/google/clock530.png differ diff --git a/public/images/emoji/google/clock6.png b/public/images/emoji/google/clock6.png new file mode 100644 index 0000000000..ee93c676f8 Binary files /dev/null and b/public/images/emoji/google/clock6.png differ diff --git a/public/images/emoji/google/clock630.png b/public/images/emoji/google/clock630.png new file mode 100644 index 0000000000..ea32ddb9e4 Binary files /dev/null and b/public/images/emoji/google/clock630.png differ diff --git a/public/images/emoji/google/clock7.png b/public/images/emoji/google/clock7.png new file mode 100644 index 0000000000..2845ebf484 Binary files /dev/null and b/public/images/emoji/google/clock7.png differ diff --git a/public/images/emoji/google/clock730.png b/public/images/emoji/google/clock730.png new file mode 100644 index 0000000000..ea2b0623fa Binary files /dev/null and b/public/images/emoji/google/clock730.png differ diff --git a/public/images/emoji/google/clock8.png b/public/images/emoji/google/clock8.png new file mode 100644 index 0000000000..0f53349dae Binary files /dev/null and b/public/images/emoji/google/clock8.png differ diff --git a/public/images/emoji/google/clock830.png b/public/images/emoji/google/clock830.png new file mode 100644 index 0000000000..ba5c4cbe7e Binary files /dev/null and b/public/images/emoji/google/clock830.png differ diff --git a/public/images/emoji/google/clock9.png b/public/images/emoji/google/clock9.png new file mode 100644 index 0000000000..86a68a6684 Binary files /dev/null and b/public/images/emoji/google/clock9.png differ diff --git a/public/images/emoji/google/clock930.png b/public/images/emoji/google/clock930.png new file mode 100644 index 0000000000..b90a81e6d0 Binary files /dev/null and b/public/images/emoji/google/clock930.png differ diff --git a/public/images/emoji/google/closed_book.png b/public/images/emoji/google/closed_book.png new file mode 100644 index 0000000000..fefd465eb4 Binary files /dev/null and b/public/images/emoji/google/closed_book.png differ diff --git a/public/images/emoji/google/closed_lock_with_key.png b/public/images/emoji/google/closed_lock_with_key.png new file mode 100644 index 0000000000..a64c97b849 Binary files /dev/null and b/public/images/emoji/google/closed_lock_with_key.png differ diff --git a/public/images/emoji/google/closed_umbrella.png b/public/images/emoji/google/closed_umbrella.png new file mode 100644 index 0000000000..8a5e87da83 Binary files /dev/null and b/public/images/emoji/google/closed_umbrella.png differ diff --git a/public/images/emoji/google/cloud.png b/public/images/emoji/google/cloud.png new file mode 100644 index 0000000000..c2291b5ab9 Binary files /dev/null and b/public/images/emoji/google/cloud.png differ diff --git a/public/images/emoji/google/clubs.png b/public/images/emoji/google/clubs.png new file mode 100644 index 0000000000..19ebed9ccf Binary files /dev/null and b/public/images/emoji/google/clubs.png differ diff --git a/public/images/emoji/google/cn.png b/public/images/emoji/google/cn.png new file mode 100644 index 0000000000..80b5033cac Binary files /dev/null and b/public/images/emoji/google/cn.png differ diff --git a/public/images/emoji/google/cocktail.png b/public/images/emoji/google/cocktail.png new file mode 100644 index 0000000000..3a4e6a04ba Binary files /dev/null and b/public/images/emoji/google/cocktail.png differ diff --git a/public/images/emoji/google/coffee.png b/public/images/emoji/google/coffee.png new file mode 100644 index 0000000000..86a7b6a05b Binary files /dev/null and b/public/images/emoji/google/coffee.png differ diff --git a/public/images/emoji/google/cold_sweat.png b/public/images/emoji/google/cold_sweat.png new file mode 100644 index 0000000000..8240560f25 Binary files /dev/null and b/public/images/emoji/google/cold_sweat.png differ diff --git a/public/images/emoji/google/collision.png b/public/images/emoji/google/collision.png new file mode 100644 index 0000000000..a143d6c405 Binary files /dev/null and b/public/images/emoji/google/collision.png differ diff --git a/public/images/emoji/google/computer.png b/public/images/emoji/google/computer.png new file mode 100644 index 0000000000..fdfb40b1db Binary files /dev/null and b/public/images/emoji/google/computer.png differ diff --git a/public/images/emoji/google/confetti_ball.png b/public/images/emoji/google/confetti_ball.png new file mode 100644 index 0000000000..476a5c2e0a Binary files /dev/null and b/public/images/emoji/google/confetti_ball.png differ diff --git a/public/images/emoji/google/confounded.png b/public/images/emoji/google/confounded.png new file mode 100644 index 0000000000..6820199b54 Binary files /dev/null and b/public/images/emoji/google/confounded.png differ diff --git a/public/images/emoji/google/confused.png b/public/images/emoji/google/confused.png new file mode 100644 index 0000000000..0c830e8e13 Binary files /dev/null and b/public/images/emoji/google/confused.png differ diff --git a/public/images/emoji/google/congratulations.png b/public/images/emoji/google/congratulations.png new file mode 100644 index 0000000000..369d1fbe21 Binary files /dev/null and b/public/images/emoji/google/congratulations.png differ diff --git a/public/images/emoji/google/construction.png b/public/images/emoji/google/construction.png new file mode 100644 index 0000000000..fe526b558b Binary files /dev/null and b/public/images/emoji/google/construction.png differ diff --git a/public/images/emoji/google/construction_worker.png b/public/images/emoji/google/construction_worker.png new file mode 100644 index 0000000000..e9ac71d9fa Binary files /dev/null and b/public/images/emoji/google/construction_worker.png differ diff --git a/public/images/emoji/google/convenience_store.png b/public/images/emoji/google/convenience_store.png new file mode 100644 index 0000000000..1a35206174 Binary files /dev/null and b/public/images/emoji/google/convenience_store.png differ diff --git a/public/images/emoji/google/cookie.png b/public/images/emoji/google/cookie.png new file mode 100644 index 0000000000..ebc0e44ee6 Binary files /dev/null and b/public/images/emoji/google/cookie.png differ diff --git a/public/images/emoji/google/cool.png b/public/images/emoji/google/cool.png new file mode 100644 index 0000000000..ca0ab9ffd3 Binary files /dev/null and b/public/images/emoji/google/cool.png differ diff --git a/public/images/emoji/google/cop.png b/public/images/emoji/google/cop.png new file mode 100644 index 0000000000..58482179d4 Binary files /dev/null and b/public/images/emoji/google/cop.png differ diff --git a/public/images/emoji/google/copyright.png b/public/images/emoji/google/copyright.png new file mode 100644 index 0000000000..84ea103226 Binary files /dev/null and b/public/images/emoji/google/copyright.png differ diff --git a/public/images/emoji/google/corn.png b/public/images/emoji/google/corn.png new file mode 100644 index 0000000000..227207c313 Binary files /dev/null and b/public/images/emoji/google/corn.png differ diff --git a/public/images/emoji/google/couple.png b/public/images/emoji/google/couple.png new file mode 100644 index 0000000000..a54ba3f716 Binary files /dev/null and b/public/images/emoji/google/couple.png differ diff --git a/public/images/emoji/google/couple_with_heart.png b/public/images/emoji/google/couple_with_heart.png new file mode 100644 index 0000000000..2cfe9d3e79 Binary files /dev/null and b/public/images/emoji/google/couple_with_heart.png differ diff --git a/public/images/emoji/google/couplekiss.png b/public/images/emoji/google/couplekiss.png new file mode 100644 index 0000000000..47846113d6 Binary files /dev/null and b/public/images/emoji/google/couplekiss.png differ diff --git a/public/images/emoji/google/cow.png b/public/images/emoji/google/cow.png new file mode 100644 index 0000000000..f477a79cf3 Binary files /dev/null and b/public/images/emoji/google/cow.png differ diff --git a/public/images/emoji/google/cow2.png b/public/images/emoji/google/cow2.png new file mode 100644 index 0000000000..2ad4f10c08 Binary files /dev/null and b/public/images/emoji/google/cow2.png differ diff --git a/public/images/emoji/google/credit_card.png b/public/images/emoji/google/credit_card.png new file mode 100644 index 0000000000..31e899d917 Binary files /dev/null and b/public/images/emoji/google/credit_card.png differ diff --git a/public/images/emoji/google/crescent_moon.png b/public/images/emoji/google/crescent_moon.png new file mode 100644 index 0000000000..a5ad76d3b4 Binary files /dev/null and b/public/images/emoji/google/crescent_moon.png differ diff --git a/public/images/emoji/google/crocodile.png b/public/images/emoji/google/crocodile.png new file mode 100644 index 0000000000..ab7ef7966a Binary files /dev/null and b/public/images/emoji/google/crocodile.png differ diff --git a/public/images/emoji/google/crossed_flags.png b/public/images/emoji/google/crossed_flags.png new file mode 100644 index 0000000000..39cd886dcf Binary files /dev/null and b/public/images/emoji/google/crossed_flags.png differ diff --git a/public/images/emoji/google/crown.png b/public/images/emoji/google/crown.png new file mode 100644 index 0000000000..b2f99e1da3 Binary files /dev/null and b/public/images/emoji/google/crown.png differ diff --git a/public/images/emoji/google/cry.png b/public/images/emoji/google/cry.png new file mode 100644 index 0000000000..5056c8153b Binary files /dev/null and b/public/images/emoji/google/cry.png differ diff --git a/public/images/emoji/google/crying_cat_face.png b/public/images/emoji/google/crying_cat_face.png new file mode 100644 index 0000000000..2b3f6430e2 Binary files /dev/null and b/public/images/emoji/google/crying_cat_face.png differ diff --git a/public/images/emoji/google/crystal_ball.png b/public/images/emoji/google/crystal_ball.png new file mode 100644 index 0000000000..1cbb69a764 Binary files /dev/null and b/public/images/emoji/google/crystal_ball.png differ diff --git a/public/images/emoji/google/cupid.png b/public/images/emoji/google/cupid.png new file mode 100644 index 0000000000..c18d80d32d Binary files /dev/null and b/public/images/emoji/google/cupid.png differ diff --git a/public/images/emoji/google/curly_loop.png b/public/images/emoji/google/curly_loop.png new file mode 100644 index 0000000000..3e90be6b60 Binary files /dev/null and b/public/images/emoji/google/curly_loop.png differ diff --git a/public/images/emoji/google/currency_exchange.png b/public/images/emoji/google/currency_exchange.png new file mode 100644 index 0000000000..8e18b38bf7 Binary files /dev/null and b/public/images/emoji/google/currency_exchange.png differ diff --git a/public/images/emoji/google/curry.png b/public/images/emoji/google/curry.png new file mode 100644 index 0000000000..aaa54d256a Binary files /dev/null and b/public/images/emoji/google/curry.png differ diff --git a/public/images/emoji/google/custard.png b/public/images/emoji/google/custard.png new file mode 100644 index 0000000000..1fe85bde14 Binary files /dev/null and b/public/images/emoji/google/custard.png differ diff --git a/public/images/emoji/google/customs.png b/public/images/emoji/google/customs.png new file mode 100644 index 0000000000..e0f48dc9c7 Binary files /dev/null and b/public/images/emoji/google/customs.png differ diff --git a/public/images/emoji/google/cyclone.png b/public/images/emoji/google/cyclone.png new file mode 100644 index 0000000000..f63cee1e65 Binary files /dev/null and b/public/images/emoji/google/cyclone.png differ diff --git a/public/images/emoji/google/dancer.png b/public/images/emoji/google/dancer.png new file mode 100644 index 0000000000..49e9f30b7b Binary files /dev/null and b/public/images/emoji/google/dancer.png differ diff --git a/public/images/emoji/google/dancers.png b/public/images/emoji/google/dancers.png new file mode 100644 index 0000000000..41c71964e3 Binary files /dev/null and b/public/images/emoji/google/dancers.png differ diff --git a/public/images/emoji/google/dango.png b/public/images/emoji/google/dango.png new file mode 100644 index 0000000000..0bf003e91f Binary files /dev/null and b/public/images/emoji/google/dango.png differ diff --git a/public/images/emoji/google/dart.png b/public/images/emoji/google/dart.png new file mode 100644 index 0000000000..855d690952 Binary files /dev/null and b/public/images/emoji/google/dart.png differ diff --git a/public/images/emoji/google/dash.png b/public/images/emoji/google/dash.png new file mode 100644 index 0000000000..f60692f458 Binary files /dev/null and b/public/images/emoji/google/dash.png differ diff --git a/public/images/emoji/google/date.png b/public/images/emoji/google/date.png new file mode 100644 index 0000000000..d7070ed7c3 Binary files /dev/null and b/public/images/emoji/google/date.png differ diff --git a/public/images/emoji/google/de.png b/public/images/emoji/google/de.png new file mode 100644 index 0000000000..fae9552eb3 Binary files /dev/null and b/public/images/emoji/google/de.png differ diff --git a/public/images/emoji/google/deciduous_tree.png b/public/images/emoji/google/deciduous_tree.png new file mode 100644 index 0000000000..fc846109a7 Binary files /dev/null and b/public/images/emoji/google/deciduous_tree.png differ diff --git a/public/images/emoji/google/department_store.png b/public/images/emoji/google/department_store.png new file mode 100644 index 0000000000..e366e68fbf Binary files /dev/null and b/public/images/emoji/google/department_store.png differ diff --git a/public/images/emoji/google/diamond_shape_with_a_dot_inside.png b/public/images/emoji/google/diamond_shape_with_a_dot_inside.png new file mode 100644 index 0000000000..f5568550ee Binary files /dev/null and b/public/images/emoji/google/diamond_shape_with_a_dot_inside.png differ diff --git a/public/images/emoji/google/diamonds.png b/public/images/emoji/google/diamonds.png new file mode 100644 index 0000000000..30ac5a770b Binary files /dev/null and b/public/images/emoji/google/diamonds.png differ diff --git a/public/images/emoji/google/disappointed.png b/public/images/emoji/google/disappointed.png new file mode 100644 index 0000000000..95c8477721 Binary files /dev/null and b/public/images/emoji/google/disappointed.png differ diff --git a/public/images/emoji/google/disappointed_relieved.png b/public/images/emoji/google/disappointed_relieved.png new file mode 100644 index 0000000000..e1832aba18 Binary files /dev/null and b/public/images/emoji/google/disappointed_relieved.png differ diff --git a/public/images/emoji/google/dizzy.png b/public/images/emoji/google/dizzy.png new file mode 100644 index 0000000000..a231e44f89 Binary files /dev/null and b/public/images/emoji/google/dizzy.png differ diff --git a/public/images/emoji/google/dizzy_face.png b/public/images/emoji/google/dizzy_face.png new file mode 100644 index 0000000000..b4c4af576c Binary files /dev/null and b/public/images/emoji/google/dizzy_face.png differ diff --git a/public/images/emoji/google/do_not_litter.png b/public/images/emoji/google/do_not_litter.png new file mode 100644 index 0000000000..83defc7b26 Binary files /dev/null and b/public/images/emoji/google/do_not_litter.png differ diff --git a/public/images/emoji/google/dog.png b/public/images/emoji/google/dog.png new file mode 100644 index 0000000000..f6fa6a6a32 Binary files /dev/null and b/public/images/emoji/google/dog.png differ diff --git a/public/images/emoji/google/dog2.png b/public/images/emoji/google/dog2.png new file mode 100644 index 0000000000..e0f1279916 Binary files /dev/null and b/public/images/emoji/google/dog2.png differ diff --git a/public/images/emoji/google/dollar.png b/public/images/emoji/google/dollar.png new file mode 100644 index 0000000000..86b0e48160 Binary files /dev/null and b/public/images/emoji/google/dollar.png differ diff --git a/public/images/emoji/google/dolls.png b/public/images/emoji/google/dolls.png new file mode 100644 index 0000000000..dde5eb148c Binary files /dev/null and b/public/images/emoji/google/dolls.png differ diff --git a/public/images/emoji/google/dolphin.png b/public/images/emoji/google/dolphin.png new file mode 100644 index 0000000000..5a96ea2791 Binary files /dev/null and b/public/images/emoji/google/dolphin.png differ diff --git a/public/images/emoji/google/door.png b/public/images/emoji/google/door.png new file mode 100644 index 0000000000..3a336f7925 Binary files /dev/null and b/public/images/emoji/google/door.png differ diff --git a/public/images/emoji/google/doughnut.png b/public/images/emoji/google/doughnut.png new file mode 100644 index 0000000000..30bb26049a Binary files /dev/null and b/public/images/emoji/google/doughnut.png differ diff --git a/public/images/emoji/google/dragon.png b/public/images/emoji/google/dragon.png new file mode 100644 index 0000000000..e8793c8f1e Binary files /dev/null and b/public/images/emoji/google/dragon.png differ diff --git a/public/images/emoji/google/dragon_face.png b/public/images/emoji/google/dragon_face.png new file mode 100644 index 0000000000..afaab1dc8a Binary files /dev/null and b/public/images/emoji/google/dragon_face.png differ diff --git a/public/images/emoji/google/dress.png b/public/images/emoji/google/dress.png new file mode 100644 index 0000000000..cc44eb65e2 Binary files /dev/null and b/public/images/emoji/google/dress.png differ diff --git a/public/images/emoji/google/dromedary_camel.png b/public/images/emoji/google/dromedary_camel.png new file mode 100644 index 0000000000..8e6e4cc700 Binary files /dev/null and b/public/images/emoji/google/dromedary_camel.png differ diff --git a/public/images/emoji/google/droplet.png b/public/images/emoji/google/droplet.png new file mode 100644 index 0000000000..c0ef9ed226 Binary files /dev/null and b/public/images/emoji/google/droplet.png differ diff --git a/public/images/emoji/google/dvd.png b/public/images/emoji/google/dvd.png new file mode 100644 index 0000000000..cf9da9221a Binary files /dev/null and b/public/images/emoji/google/dvd.png differ diff --git a/public/images/emoji/google/e-mail.png b/public/images/emoji/google/e-mail.png new file mode 100644 index 0000000000..b93ed883e8 Binary files /dev/null and b/public/images/emoji/google/e-mail.png differ diff --git a/public/images/emoji/google/ear.png b/public/images/emoji/google/ear.png new file mode 100644 index 0000000000..c8cd99bd01 Binary files /dev/null and b/public/images/emoji/google/ear.png differ diff --git a/public/images/emoji/google/ear_of_rice.png b/public/images/emoji/google/ear_of_rice.png new file mode 100644 index 0000000000..db1264dde9 Binary files /dev/null and b/public/images/emoji/google/ear_of_rice.png differ diff --git a/public/images/emoji/google/earth_africa.png b/public/images/emoji/google/earth_africa.png new file mode 100644 index 0000000000..404f61947b Binary files /dev/null and b/public/images/emoji/google/earth_africa.png differ diff --git a/public/images/emoji/google/earth_americas.png b/public/images/emoji/google/earth_americas.png new file mode 100644 index 0000000000..920faacc83 Binary files /dev/null and b/public/images/emoji/google/earth_americas.png differ diff --git a/public/images/emoji/google/earth_asia.png b/public/images/emoji/google/earth_asia.png new file mode 100644 index 0000000000..18addb4542 Binary files /dev/null and b/public/images/emoji/google/earth_asia.png differ diff --git a/public/images/emoji/google/egg.png b/public/images/emoji/google/egg.png new file mode 100644 index 0000000000..948cf1ef21 Binary files /dev/null and b/public/images/emoji/google/egg.png differ diff --git a/public/images/emoji/google/eggplant.png b/public/images/emoji/google/eggplant.png new file mode 100644 index 0000000000..fe7a9e2590 Binary files /dev/null and b/public/images/emoji/google/eggplant.png differ diff --git a/public/images/emoji/google/eight.png b/public/images/emoji/google/eight.png new file mode 100644 index 0000000000..f0be67d9d5 Binary files /dev/null and b/public/images/emoji/google/eight.png differ diff --git a/public/images/emoji/google/eight_pointed_black_star.png b/public/images/emoji/google/eight_pointed_black_star.png new file mode 100644 index 0000000000..bd3dddd9c3 Binary files /dev/null and b/public/images/emoji/google/eight_pointed_black_star.png differ diff --git a/public/images/emoji/google/eight_spoked_asterisk.png b/public/images/emoji/google/eight_spoked_asterisk.png new file mode 100644 index 0000000000..919703c263 Binary files /dev/null and b/public/images/emoji/google/eight_spoked_asterisk.png differ diff --git a/public/images/emoji/google/electric_plug.png b/public/images/emoji/google/electric_plug.png new file mode 100644 index 0000000000..c7f483fcdc Binary files /dev/null and b/public/images/emoji/google/electric_plug.png differ diff --git a/public/images/emoji/google/elephant.png b/public/images/emoji/google/elephant.png new file mode 100644 index 0000000000..39d833c312 Binary files /dev/null and b/public/images/emoji/google/elephant.png differ diff --git a/public/images/emoji/google/email.png b/public/images/emoji/google/email.png new file mode 100644 index 0000000000..1dd98cc6a6 Binary files /dev/null and b/public/images/emoji/google/email.png differ diff --git a/public/images/emoji/google/end.png b/public/images/emoji/google/end.png new file mode 100644 index 0000000000..97b98c5be2 Binary files /dev/null and b/public/images/emoji/google/end.png differ diff --git a/public/images/emoji/google/envelope.png b/public/images/emoji/google/envelope.png new file mode 100644 index 0000000000..1dd98cc6a6 Binary files /dev/null and b/public/images/emoji/google/envelope.png differ diff --git a/public/images/emoji/google/envelope_with_arrow.png b/public/images/emoji/google/envelope_with_arrow.png new file mode 100644 index 0000000000..477a189e9f Binary files /dev/null and b/public/images/emoji/google/envelope_with_arrow.png differ diff --git a/public/images/emoji/google/es.png b/public/images/emoji/google/es.png new file mode 100644 index 0000000000..71c41be537 Binary files /dev/null and b/public/images/emoji/google/es.png differ diff --git a/public/images/emoji/google/euro.png b/public/images/emoji/google/euro.png new file mode 100644 index 0000000000..35cdc9aa13 Binary files /dev/null and b/public/images/emoji/google/euro.png differ diff --git a/public/images/emoji/google/european_castle.png b/public/images/emoji/google/european_castle.png new file mode 100644 index 0000000000..eec7a3109d Binary files /dev/null and b/public/images/emoji/google/european_castle.png differ diff --git a/public/images/emoji/google/european_post_office.png b/public/images/emoji/google/european_post_office.png new file mode 100644 index 0000000000..154eebc6ca Binary files /dev/null and b/public/images/emoji/google/european_post_office.png differ diff --git a/public/images/emoji/google/evergreen_tree.png b/public/images/emoji/google/evergreen_tree.png new file mode 100644 index 0000000000..594745ef77 Binary files /dev/null and b/public/images/emoji/google/evergreen_tree.png differ diff --git a/public/images/emoji/google/exclamation.png b/public/images/emoji/google/exclamation.png new file mode 100644 index 0000000000..db65e8d05c Binary files /dev/null and b/public/images/emoji/google/exclamation.png differ diff --git a/public/images/emoji/google/expressionless.png b/public/images/emoji/google/expressionless.png new file mode 100644 index 0000000000..7d12ae899d Binary files /dev/null and b/public/images/emoji/google/expressionless.png differ diff --git a/public/images/emoji/google/eyeglasses.png b/public/images/emoji/google/eyeglasses.png new file mode 100644 index 0000000000..48e61fb45f Binary files /dev/null and b/public/images/emoji/google/eyeglasses.png differ diff --git a/public/images/emoji/google/eyes.png b/public/images/emoji/google/eyes.png new file mode 100644 index 0000000000..a341f7a3a3 Binary files /dev/null and b/public/images/emoji/google/eyes.png differ diff --git a/public/images/emoji/google/facepunch.png b/public/images/emoji/google/facepunch.png new file mode 100644 index 0000000000..96672f8da3 Binary files /dev/null and b/public/images/emoji/google/facepunch.png differ diff --git a/public/images/emoji/google/factory.png b/public/images/emoji/google/factory.png new file mode 100644 index 0000000000..9a980e92f8 Binary files /dev/null and b/public/images/emoji/google/factory.png differ diff --git a/public/images/emoji/google/fallen_leaf.png b/public/images/emoji/google/fallen_leaf.png new file mode 100644 index 0000000000..0839ada391 Binary files /dev/null and b/public/images/emoji/google/fallen_leaf.png differ diff --git a/public/images/emoji/google/family.png b/public/images/emoji/google/family.png new file mode 100644 index 0000000000..c02783f6d7 Binary files /dev/null and b/public/images/emoji/google/family.png differ diff --git a/public/images/emoji/google/fast_forward.png b/public/images/emoji/google/fast_forward.png new file mode 100644 index 0000000000..01f6de32db Binary files /dev/null and b/public/images/emoji/google/fast_forward.png differ diff --git a/public/images/emoji/google/fax.png b/public/images/emoji/google/fax.png new file mode 100644 index 0000000000..877c37efe5 Binary files /dev/null and b/public/images/emoji/google/fax.png differ diff --git a/public/images/emoji/google/fearful.png b/public/images/emoji/google/fearful.png new file mode 100644 index 0000000000..a368a71138 Binary files /dev/null and b/public/images/emoji/google/fearful.png differ diff --git a/public/images/emoji/google/feet.png b/public/images/emoji/google/feet.png new file mode 100644 index 0000000000..3f88932e4a Binary files /dev/null and b/public/images/emoji/google/feet.png differ diff --git a/public/images/emoji/google/ferris_wheel.png b/public/images/emoji/google/ferris_wheel.png new file mode 100644 index 0000000000..634643f8ce Binary files /dev/null and b/public/images/emoji/google/ferris_wheel.png differ diff --git a/public/images/emoji/google/file_folder.png b/public/images/emoji/google/file_folder.png new file mode 100644 index 0000000000..8a2f54372c Binary files /dev/null and b/public/images/emoji/google/file_folder.png differ diff --git a/public/images/emoji/google/fire.png b/public/images/emoji/google/fire.png new file mode 100644 index 0000000000..a4bc221c6d Binary files /dev/null and b/public/images/emoji/google/fire.png differ diff --git a/public/images/emoji/google/fire_engine.png b/public/images/emoji/google/fire_engine.png new file mode 100644 index 0000000000..e6ec91a9f4 Binary files /dev/null and b/public/images/emoji/google/fire_engine.png differ diff --git a/public/images/emoji/google/fireworks.png b/public/images/emoji/google/fireworks.png new file mode 100644 index 0000000000..22e886c763 Binary files /dev/null and b/public/images/emoji/google/fireworks.png differ diff --git a/public/images/emoji/google/first_quarter_moon.png b/public/images/emoji/google/first_quarter_moon.png new file mode 100644 index 0000000000..ee32ca7a9a Binary files /dev/null and b/public/images/emoji/google/first_quarter_moon.png differ diff --git a/public/images/emoji/google/first_quarter_moon_with_face.png b/public/images/emoji/google/first_quarter_moon_with_face.png new file mode 100644 index 0000000000..c281244928 Binary files /dev/null and b/public/images/emoji/google/first_quarter_moon_with_face.png differ diff --git a/public/images/emoji/google/fish.png b/public/images/emoji/google/fish.png new file mode 100644 index 0000000000..20d7fcd20b Binary files /dev/null and b/public/images/emoji/google/fish.png differ diff --git a/public/images/emoji/google/fish_cake.png b/public/images/emoji/google/fish_cake.png new file mode 100644 index 0000000000..5d943adc6d Binary files /dev/null and b/public/images/emoji/google/fish_cake.png differ diff --git a/public/images/emoji/google/fishing_pole_and_fish.png b/public/images/emoji/google/fishing_pole_and_fish.png new file mode 100644 index 0000000000..b5a0c98598 Binary files /dev/null and b/public/images/emoji/google/fishing_pole_and_fish.png differ diff --git a/public/images/emoji/google/fist.png b/public/images/emoji/google/fist.png new file mode 100644 index 0000000000..0378851128 Binary files /dev/null and b/public/images/emoji/google/fist.png differ diff --git a/public/images/emoji/google/five.png b/public/images/emoji/google/five.png new file mode 100644 index 0000000000..8e317fbdfd Binary files /dev/null and b/public/images/emoji/google/five.png differ diff --git a/public/images/emoji/google/flags.png b/public/images/emoji/google/flags.png new file mode 100644 index 0000000000..c831fdacf6 Binary files /dev/null and b/public/images/emoji/google/flags.png differ diff --git a/public/images/emoji/google/flashlight.png b/public/images/emoji/google/flashlight.png new file mode 100644 index 0000000000..d6ca0cd511 Binary files /dev/null and b/public/images/emoji/google/flashlight.png differ diff --git a/public/images/emoji/google/flipper.png b/public/images/emoji/google/flipper.png new file mode 100644 index 0000000000..61bf80ae9a Binary files /dev/null and b/public/images/emoji/google/flipper.png differ diff --git a/public/images/emoji/google/floppy_disk.png b/public/images/emoji/google/floppy_disk.png new file mode 100644 index 0000000000..070b7ce14f Binary files /dev/null and b/public/images/emoji/google/floppy_disk.png differ diff --git a/public/images/emoji/google/flower_playing_cards.png b/public/images/emoji/google/flower_playing_cards.png new file mode 100644 index 0000000000..cbe1cd412b Binary files /dev/null and b/public/images/emoji/google/flower_playing_cards.png differ diff --git a/public/images/emoji/google/flushed.png b/public/images/emoji/google/flushed.png new file mode 100644 index 0000000000..e1f63b828c Binary files /dev/null and b/public/images/emoji/google/flushed.png differ diff --git a/public/images/emoji/google/foggy.png b/public/images/emoji/google/foggy.png new file mode 100644 index 0000000000..14ccca19ca Binary files /dev/null and b/public/images/emoji/google/foggy.png differ diff --git a/public/images/emoji/google/football.png b/public/images/emoji/google/football.png new file mode 100644 index 0000000000..590cf19c91 Binary files /dev/null and b/public/images/emoji/google/football.png differ diff --git a/public/images/emoji/google/footprints.png b/public/images/emoji/google/footprints.png new file mode 100644 index 0000000000..9d8a4fb2f3 Binary files /dev/null and b/public/images/emoji/google/footprints.png differ diff --git a/public/images/emoji/google/fork_and_knife.png b/public/images/emoji/google/fork_and_knife.png new file mode 100644 index 0000000000..c80d53a6b9 Binary files /dev/null and b/public/images/emoji/google/fork_and_knife.png differ diff --git a/public/images/emoji/google/fountain.png b/public/images/emoji/google/fountain.png new file mode 100644 index 0000000000..a88e6cc9c0 Binary files /dev/null and b/public/images/emoji/google/fountain.png differ diff --git a/public/images/emoji/google/four.png b/public/images/emoji/google/four.png new file mode 100644 index 0000000000..81367f1b71 Binary files /dev/null and b/public/images/emoji/google/four.png differ diff --git a/public/images/emoji/google/four_leaf_clover.png b/public/images/emoji/google/four_leaf_clover.png new file mode 100644 index 0000000000..82b3eaeb0a Binary files /dev/null and b/public/images/emoji/google/four_leaf_clover.png differ diff --git a/public/images/emoji/google/fr.png b/public/images/emoji/google/fr.png new file mode 100644 index 0000000000..0d83f0573b Binary files /dev/null and b/public/images/emoji/google/fr.png differ diff --git a/public/images/emoji/google/free.png b/public/images/emoji/google/free.png new file mode 100644 index 0000000000..7bc78dda16 Binary files /dev/null and b/public/images/emoji/google/free.png differ diff --git a/public/images/emoji/google/fried_shrimp.png b/public/images/emoji/google/fried_shrimp.png new file mode 100644 index 0000000000..85a685c497 Binary files /dev/null and b/public/images/emoji/google/fried_shrimp.png differ diff --git a/public/images/emoji/google/fries.png b/public/images/emoji/google/fries.png new file mode 100644 index 0000000000..991c4a5cee Binary files /dev/null and b/public/images/emoji/google/fries.png differ diff --git a/public/images/emoji/google/frog.png b/public/images/emoji/google/frog.png new file mode 100644 index 0000000000..89337e9c9a Binary files /dev/null and b/public/images/emoji/google/frog.png differ diff --git a/public/images/emoji/google/frowning.png b/public/images/emoji/google/frowning.png new file mode 100644 index 0000000000..1252be4242 Binary files /dev/null and b/public/images/emoji/google/frowning.png differ diff --git a/public/images/emoji/google/fuelpump.png b/public/images/emoji/google/fuelpump.png new file mode 100644 index 0000000000..f31b6c4b9f Binary files /dev/null and b/public/images/emoji/google/fuelpump.png differ diff --git a/public/images/emoji/google/full_moon.png b/public/images/emoji/google/full_moon.png new file mode 100644 index 0000000000..51514a42a5 Binary files /dev/null and b/public/images/emoji/google/full_moon.png differ diff --git a/public/images/emoji/google/full_moon_with_face.png b/public/images/emoji/google/full_moon_with_face.png new file mode 100644 index 0000000000..620ee9d714 Binary files /dev/null and b/public/images/emoji/google/full_moon_with_face.png differ diff --git a/public/images/emoji/google/game_die.png b/public/images/emoji/google/game_die.png new file mode 100644 index 0000000000..cc23ef9e6f Binary files /dev/null and b/public/images/emoji/google/game_die.png differ diff --git a/public/images/emoji/google/gb.png b/public/images/emoji/google/gb.png new file mode 100644 index 0000000000..a0f834b4c6 Binary files /dev/null and b/public/images/emoji/google/gb.png differ diff --git a/public/images/emoji/google/gem.png b/public/images/emoji/google/gem.png new file mode 100644 index 0000000000..ba0944f9fd Binary files /dev/null and b/public/images/emoji/google/gem.png differ diff --git a/public/images/emoji/google/gemini.png b/public/images/emoji/google/gemini.png new file mode 100644 index 0000000000..f6189014c9 Binary files /dev/null and b/public/images/emoji/google/gemini.png differ diff --git a/public/images/emoji/google/ghost.png b/public/images/emoji/google/ghost.png new file mode 100644 index 0000000000..02f53de91d Binary files /dev/null and b/public/images/emoji/google/ghost.png differ diff --git a/public/images/emoji/google/gift.png b/public/images/emoji/google/gift.png new file mode 100644 index 0000000000..c2b77b8f0f Binary files /dev/null and b/public/images/emoji/google/gift.png differ diff --git a/public/images/emoji/google/gift_heart.png b/public/images/emoji/google/gift_heart.png new file mode 100644 index 0000000000..ab79751077 Binary files /dev/null and b/public/images/emoji/google/gift_heart.png differ diff --git a/public/images/emoji/google/girl.png b/public/images/emoji/google/girl.png new file mode 100644 index 0000000000..425e9682e6 Binary files /dev/null and b/public/images/emoji/google/girl.png differ diff --git a/public/images/emoji/google/globe_with_meridians.png b/public/images/emoji/google/globe_with_meridians.png new file mode 100644 index 0000000000..c622f98b6b Binary files /dev/null and b/public/images/emoji/google/globe_with_meridians.png differ diff --git a/public/images/emoji/google/goat.png b/public/images/emoji/google/goat.png new file mode 100644 index 0000000000..9f9ba8f57a Binary files /dev/null and b/public/images/emoji/google/goat.png differ diff --git a/public/images/emoji/google/golf.png b/public/images/emoji/google/golf.png new file mode 100644 index 0000000000..1c4eef3cdf Binary files /dev/null and b/public/images/emoji/google/golf.png differ diff --git a/public/images/emoji/google/grapes.png b/public/images/emoji/google/grapes.png new file mode 100644 index 0000000000..1ef015cc32 Binary files /dev/null and b/public/images/emoji/google/grapes.png differ diff --git a/public/images/emoji/google/green_apple.png b/public/images/emoji/google/green_apple.png new file mode 100644 index 0000000000..783790de73 Binary files /dev/null and b/public/images/emoji/google/green_apple.png differ diff --git a/public/images/emoji/google/green_book.png b/public/images/emoji/google/green_book.png new file mode 100644 index 0000000000..92406449e4 Binary files /dev/null and b/public/images/emoji/google/green_book.png differ diff --git a/public/images/emoji/google/green_heart.png b/public/images/emoji/google/green_heart.png new file mode 100644 index 0000000000..4256f5ef8d Binary files /dev/null and b/public/images/emoji/google/green_heart.png differ diff --git a/public/images/emoji/google/grey_exclamation.png b/public/images/emoji/google/grey_exclamation.png new file mode 100644 index 0000000000..566da09c3b Binary files /dev/null and b/public/images/emoji/google/grey_exclamation.png differ diff --git a/public/images/emoji/google/grey_question.png b/public/images/emoji/google/grey_question.png new file mode 100644 index 0000000000..57f55dcd7a Binary files /dev/null and b/public/images/emoji/google/grey_question.png differ diff --git a/public/images/emoji/google/grimacing.png b/public/images/emoji/google/grimacing.png new file mode 100644 index 0000000000..a7d5a96f0c Binary files /dev/null and b/public/images/emoji/google/grimacing.png differ diff --git a/public/images/emoji/google/grin.png b/public/images/emoji/google/grin.png new file mode 100644 index 0000000000..60dee4453f Binary files /dev/null and b/public/images/emoji/google/grin.png differ diff --git a/public/images/emoji/google/grinning.png b/public/images/emoji/google/grinning.png new file mode 100644 index 0000000000..879c3f2681 Binary files /dev/null and b/public/images/emoji/google/grinning.png differ diff --git a/public/images/emoji/google/guardsman.png b/public/images/emoji/google/guardsman.png new file mode 100644 index 0000000000..ad57db0576 Binary files /dev/null and b/public/images/emoji/google/guardsman.png differ diff --git a/public/images/emoji/google/guitar.png b/public/images/emoji/google/guitar.png new file mode 100644 index 0000000000..e5dead90eb Binary files /dev/null and b/public/images/emoji/google/guitar.png differ diff --git a/public/images/emoji/google/gun.png b/public/images/emoji/google/gun.png new file mode 100644 index 0000000000..144a52e4ad Binary files /dev/null and b/public/images/emoji/google/gun.png differ diff --git a/public/images/emoji/google/haircut.png b/public/images/emoji/google/haircut.png new file mode 100644 index 0000000000..af0580979e Binary files /dev/null and b/public/images/emoji/google/haircut.png differ diff --git a/public/images/emoji/google/hamburger.png b/public/images/emoji/google/hamburger.png new file mode 100644 index 0000000000..8408b75fed Binary files /dev/null and b/public/images/emoji/google/hamburger.png differ diff --git a/public/images/emoji/google/hammer.png b/public/images/emoji/google/hammer.png new file mode 100644 index 0000000000..4183b10675 Binary files /dev/null and b/public/images/emoji/google/hammer.png differ diff --git a/public/images/emoji/google/hamster.png b/public/images/emoji/google/hamster.png new file mode 100644 index 0000000000..328891dbbf Binary files /dev/null and b/public/images/emoji/google/hamster.png differ diff --git a/public/images/emoji/google/hand.png b/public/images/emoji/google/hand.png new file mode 100644 index 0000000000..312415894c Binary files /dev/null and b/public/images/emoji/google/hand.png differ diff --git a/public/images/emoji/google/handbag.png b/public/images/emoji/google/handbag.png new file mode 100644 index 0000000000..da8faaa8a7 Binary files /dev/null and b/public/images/emoji/google/handbag.png differ diff --git a/public/images/emoji/google/hankey.png b/public/images/emoji/google/hankey.png new file mode 100644 index 0000000000..aa2424539c Binary files /dev/null and b/public/images/emoji/google/hankey.png differ diff --git a/public/images/emoji/google/hash.png b/public/images/emoji/google/hash.png new file mode 100644 index 0000000000..a73cf231ca Binary files /dev/null and b/public/images/emoji/google/hash.png differ diff --git a/public/images/emoji/google/hatched_chick.png b/public/images/emoji/google/hatched_chick.png new file mode 100644 index 0000000000..f5c6d1c3ba Binary files /dev/null and b/public/images/emoji/google/hatched_chick.png differ diff --git a/public/images/emoji/google/hatching_chick.png b/public/images/emoji/google/hatching_chick.png new file mode 100644 index 0000000000..42019c3597 Binary files /dev/null and b/public/images/emoji/google/hatching_chick.png differ diff --git a/public/images/emoji/google/headphones.png b/public/images/emoji/google/headphones.png new file mode 100644 index 0000000000..d90a4ffb74 Binary files /dev/null and b/public/images/emoji/google/headphones.png differ diff --git a/public/images/emoji/google/hear_no_evil.png b/public/images/emoji/google/hear_no_evil.png new file mode 100644 index 0000000000..abd6edd7e3 Binary files /dev/null and b/public/images/emoji/google/hear_no_evil.png differ diff --git a/public/images/emoji/google/heart.png b/public/images/emoji/google/heart.png new file mode 100644 index 0000000000..5318c5c9fd Binary files /dev/null and b/public/images/emoji/google/heart.png differ diff --git a/public/images/emoji/google/heart_decoration.png b/public/images/emoji/google/heart_decoration.png new file mode 100644 index 0000000000..a728f60e14 Binary files /dev/null and b/public/images/emoji/google/heart_decoration.png differ diff --git a/public/images/emoji/google/heart_eyes.png b/public/images/emoji/google/heart_eyes.png new file mode 100644 index 0000000000..734f5599fd Binary files /dev/null and b/public/images/emoji/google/heart_eyes.png differ diff --git a/public/images/emoji/google/heart_eyes_cat.png b/public/images/emoji/google/heart_eyes_cat.png new file mode 100644 index 0000000000..2c5a92b47c Binary files /dev/null and b/public/images/emoji/google/heart_eyes_cat.png differ diff --git a/public/images/emoji/google/heartbeat.png b/public/images/emoji/google/heartbeat.png new file mode 100644 index 0000000000..2c5acc6c49 Binary files /dev/null and b/public/images/emoji/google/heartbeat.png differ diff --git a/public/images/emoji/google/heartpulse.png b/public/images/emoji/google/heartpulse.png new file mode 100644 index 0000000000..d2e706153c Binary files /dev/null and b/public/images/emoji/google/heartpulse.png differ diff --git a/public/images/emoji/google/hearts.png b/public/images/emoji/google/hearts.png new file mode 100644 index 0000000000..1383393cc5 Binary files /dev/null and b/public/images/emoji/google/hearts.png differ diff --git a/public/images/emoji/google/heavy_check_mark.png b/public/images/emoji/google/heavy_check_mark.png new file mode 100644 index 0000000000..45e1749c3c Binary files /dev/null and b/public/images/emoji/google/heavy_check_mark.png differ diff --git a/public/images/emoji/google/heavy_division_sign.png b/public/images/emoji/google/heavy_division_sign.png new file mode 100644 index 0000000000..b4868970f6 Binary files /dev/null and b/public/images/emoji/google/heavy_division_sign.png differ diff --git a/public/images/emoji/google/heavy_dollar_sign.png b/public/images/emoji/google/heavy_dollar_sign.png new file mode 100644 index 0000000000..0ef1cccd55 Binary files /dev/null and b/public/images/emoji/google/heavy_dollar_sign.png differ diff --git a/public/images/emoji/google/heavy_exclamation_mark.png b/public/images/emoji/google/heavy_exclamation_mark.png new file mode 100644 index 0000000000..db65e8d05c Binary files /dev/null and b/public/images/emoji/google/heavy_exclamation_mark.png differ diff --git a/public/images/emoji/google/heavy_minus_sign.png b/public/images/emoji/google/heavy_minus_sign.png new file mode 100644 index 0000000000..4a1236772a Binary files /dev/null and b/public/images/emoji/google/heavy_minus_sign.png differ diff --git a/public/images/emoji/google/heavy_multiplication_x.png b/public/images/emoji/google/heavy_multiplication_x.png new file mode 100644 index 0000000000..95bdaec6cf Binary files /dev/null and b/public/images/emoji/google/heavy_multiplication_x.png differ diff --git a/public/images/emoji/google/heavy_plus_sign.png b/public/images/emoji/google/heavy_plus_sign.png new file mode 100644 index 0000000000..930b0fffb3 Binary files /dev/null and b/public/images/emoji/google/heavy_plus_sign.png differ diff --git a/public/images/emoji/google/helicopter.png b/public/images/emoji/google/helicopter.png new file mode 100644 index 0000000000..3981eaa43d Binary files /dev/null and b/public/images/emoji/google/helicopter.png differ diff --git a/public/images/emoji/google/herb.png b/public/images/emoji/google/herb.png new file mode 100644 index 0000000000..84e09672e9 Binary files /dev/null and b/public/images/emoji/google/herb.png differ diff --git a/public/images/emoji/google/hibiscus.png b/public/images/emoji/google/hibiscus.png new file mode 100644 index 0000000000..eed24d38d7 Binary files /dev/null and b/public/images/emoji/google/hibiscus.png differ diff --git a/public/images/emoji/google/high_brightness.png b/public/images/emoji/google/high_brightness.png new file mode 100644 index 0000000000..17fc27997f Binary files /dev/null and b/public/images/emoji/google/high_brightness.png differ diff --git a/public/images/emoji/google/high_heel.png b/public/images/emoji/google/high_heel.png new file mode 100644 index 0000000000..c790678c5b Binary files /dev/null and b/public/images/emoji/google/high_heel.png differ diff --git a/public/images/emoji/google/hocho.png b/public/images/emoji/google/hocho.png new file mode 100644 index 0000000000..a683b75922 Binary files /dev/null and b/public/images/emoji/google/hocho.png differ diff --git a/public/images/emoji/google/honey_pot.png b/public/images/emoji/google/honey_pot.png new file mode 100644 index 0000000000..eb2403cf57 Binary files /dev/null and b/public/images/emoji/google/honey_pot.png differ diff --git a/public/images/emoji/google/honeybee.png b/public/images/emoji/google/honeybee.png new file mode 100644 index 0000000000..20a86eb54b Binary files /dev/null and b/public/images/emoji/google/honeybee.png differ diff --git a/public/images/emoji/google/horse.png b/public/images/emoji/google/horse.png new file mode 100644 index 0000000000..edc3358716 Binary files /dev/null and b/public/images/emoji/google/horse.png differ diff --git a/public/images/emoji/google/horse_racing.png b/public/images/emoji/google/horse_racing.png new file mode 100644 index 0000000000..c9272e3a58 Binary files /dev/null and b/public/images/emoji/google/horse_racing.png differ diff --git a/public/images/emoji/google/hospital.png b/public/images/emoji/google/hospital.png new file mode 100644 index 0000000000..110abed009 Binary files /dev/null and b/public/images/emoji/google/hospital.png differ diff --git a/public/images/emoji/google/hotel.png b/public/images/emoji/google/hotel.png new file mode 100644 index 0000000000..9c75d63d1f Binary files /dev/null and b/public/images/emoji/google/hotel.png differ diff --git a/public/images/emoji/google/hotsprings.png b/public/images/emoji/google/hotsprings.png new file mode 100644 index 0000000000..5a5cc1fb6c Binary files /dev/null and b/public/images/emoji/google/hotsprings.png differ diff --git a/public/images/emoji/google/hourglass.png b/public/images/emoji/google/hourglass.png new file mode 100644 index 0000000000..da4733970b Binary files /dev/null and b/public/images/emoji/google/hourglass.png differ diff --git a/public/images/emoji/google/hourglass_flowing_sand.png b/public/images/emoji/google/hourglass_flowing_sand.png new file mode 100644 index 0000000000..7f8671a0f4 Binary files /dev/null and b/public/images/emoji/google/hourglass_flowing_sand.png differ diff --git a/public/images/emoji/google/house.png b/public/images/emoji/google/house.png new file mode 100644 index 0000000000..bde5870fa7 Binary files /dev/null and b/public/images/emoji/google/house.png differ diff --git a/public/images/emoji/google/house_with_garden.png b/public/images/emoji/google/house_with_garden.png new file mode 100644 index 0000000000..d6733cb4b1 Binary files /dev/null and b/public/images/emoji/google/house_with_garden.png differ diff --git a/public/images/emoji/google/hushed.png b/public/images/emoji/google/hushed.png new file mode 100644 index 0000000000..53d02a0de5 Binary files /dev/null and b/public/images/emoji/google/hushed.png differ diff --git a/public/images/emoji/google/ice_cream.png b/public/images/emoji/google/ice_cream.png new file mode 100644 index 0000000000..a52197c796 Binary files /dev/null and b/public/images/emoji/google/ice_cream.png differ diff --git a/public/images/emoji/google/icecream.png b/public/images/emoji/google/icecream.png new file mode 100644 index 0000000000..e995d400a7 Binary files /dev/null and b/public/images/emoji/google/icecream.png differ diff --git a/public/images/emoji/google/id.png b/public/images/emoji/google/id.png new file mode 100644 index 0000000000..8d527b4f8e Binary files /dev/null and b/public/images/emoji/google/id.png differ diff --git a/public/images/emoji/google/ideograph_advantage.png b/public/images/emoji/google/ideograph_advantage.png new file mode 100644 index 0000000000..4e6694a62f Binary files /dev/null and b/public/images/emoji/google/ideograph_advantage.png differ diff --git a/public/images/emoji/google/imp.png b/public/images/emoji/google/imp.png new file mode 100644 index 0000000000..0530c4bad4 Binary files /dev/null and b/public/images/emoji/google/imp.png differ diff --git a/public/images/emoji/google/inbox_tray.png b/public/images/emoji/google/inbox_tray.png new file mode 100644 index 0000000000..c95f8e342e Binary files /dev/null and b/public/images/emoji/google/inbox_tray.png differ diff --git a/public/images/emoji/google/incoming_envelope.png b/public/images/emoji/google/incoming_envelope.png new file mode 100644 index 0000000000..4a13d29794 Binary files /dev/null and b/public/images/emoji/google/incoming_envelope.png differ diff --git a/public/images/emoji/google/information_desk_person.png b/public/images/emoji/google/information_desk_person.png new file mode 100644 index 0000000000..85605a45ed Binary files /dev/null and b/public/images/emoji/google/information_desk_person.png differ diff --git a/public/images/emoji/google/information_source.png b/public/images/emoji/google/information_source.png new file mode 100644 index 0000000000..85377331d5 Binary files /dev/null and b/public/images/emoji/google/information_source.png differ diff --git a/public/images/emoji/google/innocent.png b/public/images/emoji/google/innocent.png new file mode 100644 index 0000000000..7e33f010ed Binary files /dev/null and b/public/images/emoji/google/innocent.png differ diff --git a/public/images/emoji/google/interrobang.png b/public/images/emoji/google/interrobang.png new file mode 100644 index 0000000000..8fe3253a73 Binary files /dev/null and b/public/images/emoji/google/interrobang.png differ diff --git a/public/images/emoji/google/iphone.png b/public/images/emoji/google/iphone.png new file mode 100644 index 0000000000..815c4ba49a Binary files /dev/null and b/public/images/emoji/google/iphone.png differ diff --git a/public/images/emoji/google/it.png b/public/images/emoji/google/it.png new file mode 100644 index 0000000000..6aa2808e73 Binary files /dev/null and b/public/images/emoji/google/it.png differ diff --git a/public/images/emoji/google/izakaya_lantern.png b/public/images/emoji/google/izakaya_lantern.png new file mode 100644 index 0000000000..372e5a7ff1 Binary files /dev/null and b/public/images/emoji/google/izakaya_lantern.png differ diff --git a/public/images/emoji/google/jack_o_lantern.png b/public/images/emoji/google/jack_o_lantern.png new file mode 100644 index 0000000000..b7da90c547 Binary files /dev/null and b/public/images/emoji/google/jack_o_lantern.png differ diff --git a/public/images/emoji/google/japan.png b/public/images/emoji/google/japan.png new file mode 100644 index 0000000000..2e6274ac26 Binary files /dev/null and b/public/images/emoji/google/japan.png differ diff --git a/public/images/emoji/google/japanese_castle.png b/public/images/emoji/google/japanese_castle.png new file mode 100644 index 0000000000..542d54cc4d Binary files /dev/null and b/public/images/emoji/google/japanese_castle.png differ diff --git a/public/images/emoji/google/japanese_goblin.png b/public/images/emoji/google/japanese_goblin.png new file mode 100644 index 0000000000..c2294edd21 Binary files /dev/null and b/public/images/emoji/google/japanese_goblin.png differ diff --git a/public/images/emoji/google/japanese_ogre.png b/public/images/emoji/google/japanese_ogre.png new file mode 100644 index 0000000000..2738475fe8 Binary files /dev/null and b/public/images/emoji/google/japanese_ogre.png differ diff --git a/public/images/emoji/google/jeans.png b/public/images/emoji/google/jeans.png new file mode 100644 index 0000000000..cd1e59b858 Binary files /dev/null and b/public/images/emoji/google/jeans.png differ diff --git a/public/images/emoji/google/joy.png b/public/images/emoji/google/joy.png new file mode 100644 index 0000000000..d20afd7c83 Binary files /dev/null and b/public/images/emoji/google/joy.png differ diff --git a/public/images/emoji/google/joy_cat.png b/public/images/emoji/google/joy_cat.png new file mode 100644 index 0000000000..8aaeb6375c Binary files /dev/null and b/public/images/emoji/google/joy_cat.png differ diff --git a/public/images/emoji/google/jp.png b/public/images/emoji/google/jp.png new file mode 100644 index 0000000000..197cac2f6e Binary files /dev/null and b/public/images/emoji/google/jp.png differ diff --git a/public/images/emoji/google/key.png b/public/images/emoji/google/key.png new file mode 100644 index 0000000000..8bf0af58ed Binary files /dev/null and b/public/images/emoji/google/key.png differ diff --git a/public/images/emoji/google/keycap_ten.png b/public/images/emoji/google/keycap_ten.png new file mode 100644 index 0000000000..641d9c310a Binary files /dev/null and b/public/images/emoji/google/keycap_ten.png differ diff --git a/public/images/emoji/google/kimono.png b/public/images/emoji/google/kimono.png new file mode 100644 index 0000000000..910314266f Binary files /dev/null and b/public/images/emoji/google/kimono.png differ diff --git a/public/images/emoji/google/kiss.png b/public/images/emoji/google/kiss.png new file mode 100644 index 0000000000..8339ecedfb Binary files /dev/null and b/public/images/emoji/google/kiss.png differ diff --git a/public/images/emoji/google/kissing.png b/public/images/emoji/google/kissing.png new file mode 100644 index 0000000000..7abe264003 Binary files /dev/null and b/public/images/emoji/google/kissing.png differ diff --git a/public/images/emoji/google/kissing_cat.png b/public/images/emoji/google/kissing_cat.png new file mode 100644 index 0000000000..8a5daa6765 Binary files /dev/null and b/public/images/emoji/google/kissing_cat.png differ diff --git a/public/images/emoji/google/kissing_closed_eyes.png b/public/images/emoji/google/kissing_closed_eyes.png new file mode 100644 index 0000000000..75bf81fa49 Binary files /dev/null and b/public/images/emoji/google/kissing_closed_eyes.png differ diff --git a/public/images/emoji/google/kissing_heart.png b/public/images/emoji/google/kissing_heart.png new file mode 100644 index 0000000000..0fe423407b Binary files /dev/null and b/public/images/emoji/google/kissing_heart.png differ diff --git a/public/images/emoji/google/kissing_smiling_eyes.png b/public/images/emoji/google/kissing_smiling_eyes.png new file mode 100644 index 0000000000..d7f6437fe5 Binary files /dev/null and b/public/images/emoji/google/kissing_smiling_eyes.png differ diff --git a/public/images/emoji/google/knife.png b/public/images/emoji/google/knife.png new file mode 100644 index 0000000000..34460cab6f Binary files /dev/null and b/public/images/emoji/google/knife.png differ diff --git a/public/images/emoji/google/koala.png b/public/images/emoji/google/koala.png new file mode 100644 index 0000000000..a939772dd1 Binary files /dev/null and b/public/images/emoji/google/koala.png differ diff --git a/public/images/emoji/google/koko.png b/public/images/emoji/google/koko.png new file mode 100644 index 0000000000..9b230b456b Binary files /dev/null and b/public/images/emoji/google/koko.png differ diff --git a/public/images/emoji/google/kr.png b/public/images/emoji/google/kr.png new file mode 100644 index 0000000000..78ea7279ad Binary files /dev/null and b/public/images/emoji/google/kr.png differ diff --git a/public/images/emoji/google/lantern.png b/public/images/emoji/google/lantern.png new file mode 100644 index 0000000000..0756e34d4f Binary files /dev/null and b/public/images/emoji/google/lantern.png differ diff --git a/public/images/emoji/google/large_blue_circle.png b/public/images/emoji/google/large_blue_circle.png new file mode 100644 index 0000000000..95f57715a0 Binary files /dev/null and b/public/images/emoji/google/large_blue_circle.png differ diff --git a/public/images/emoji/google/large_blue_diamond.png b/public/images/emoji/google/large_blue_diamond.png new file mode 100644 index 0000000000..ceba550804 Binary files /dev/null and b/public/images/emoji/google/large_blue_diamond.png differ diff --git a/public/images/emoji/google/large_orange_diamond.png b/public/images/emoji/google/large_orange_diamond.png new file mode 100644 index 0000000000..49205b2f82 Binary files /dev/null and b/public/images/emoji/google/large_orange_diamond.png differ diff --git a/public/images/emoji/google/last_quarter_moon.png b/public/images/emoji/google/last_quarter_moon.png new file mode 100644 index 0000000000..d36e213b53 Binary files /dev/null and b/public/images/emoji/google/last_quarter_moon.png differ diff --git a/public/images/emoji/google/last_quarter_moon_with_face.png b/public/images/emoji/google/last_quarter_moon_with_face.png new file mode 100644 index 0000000000..b88abbb4db Binary files /dev/null and b/public/images/emoji/google/last_quarter_moon_with_face.png differ diff --git a/public/images/emoji/google/laughing.png b/public/images/emoji/google/laughing.png new file mode 100644 index 0000000000..8265a2e20c Binary files /dev/null and b/public/images/emoji/google/laughing.png differ diff --git a/public/images/emoji/google/leaves.png b/public/images/emoji/google/leaves.png new file mode 100644 index 0000000000..b59a4b0e6c Binary files /dev/null and b/public/images/emoji/google/leaves.png differ diff --git a/public/images/emoji/google/ledger.png b/public/images/emoji/google/ledger.png new file mode 100644 index 0000000000..85a9b8458b Binary files /dev/null and b/public/images/emoji/google/ledger.png differ diff --git a/public/images/emoji/google/left_luggage.png b/public/images/emoji/google/left_luggage.png new file mode 100644 index 0000000000..3855da52a7 Binary files /dev/null and b/public/images/emoji/google/left_luggage.png differ diff --git a/public/images/emoji/google/left_right_arrow.png b/public/images/emoji/google/left_right_arrow.png new file mode 100644 index 0000000000..5c3e2bb770 Binary files /dev/null and b/public/images/emoji/google/left_right_arrow.png differ diff --git a/public/images/emoji/google/leftwards_arrow_with_hook.png b/public/images/emoji/google/leftwards_arrow_with_hook.png new file mode 100644 index 0000000000..8598160976 Binary files /dev/null and b/public/images/emoji/google/leftwards_arrow_with_hook.png differ diff --git a/public/images/emoji/google/lemon.png b/public/images/emoji/google/lemon.png new file mode 100644 index 0000000000..87c9614d08 Binary files /dev/null and b/public/images/emoji/google/lemon.png differ diff --git a/public/images/emoji/google/leo.png b/public/images/emoji/google/leo.png new file mode 100644 index 0000000000..cbdc7f5710 Binary files /dev/null and b/public/images/emoji/google/leo.png differ diff --git a/public/images/emoji/google/leopard.png b/public/images/emoji/google/leopard.png new file mode 100644 index 0000000000..d01138abfc Binary files /dev/null and b/public/images/emoji/google/leopard.png differ diff --git a/public/images/emoji/google/libra.png b/public/images/emoji/google/libra.png new file mode 100644 index 0000000000..bc6a1c8af4 Binary files /dev/null and b/public/images/emoji/google/libra.png differ diff --git a/public/images/emoji/google/light_rail.png b/public/images/emoji/google/light_rail.png new file mode 100644 index 0000000000..9ff5f20ff1 Binary files /dev/null and b/public/images/emoji/google/light_rail.png differ diff --git a/public/images/emoji/google/link.png b/public/images/emoji/google/link.png new file mode 100644 index 0000000000..2be94b5bfa Binary files /dev/null and b/public/images/emoji/google/link.png differ diff --git a/public/images/emoji/google/lips.png b/public/images/emoji/google/lips.png new file mode 100644 index 0000000000..c37cf47159 Binary files /dev/null and b/public/images/emoji/google/lips.png differ diff --git a/public/images/emoji/google/lipstick.png b/public/images/emoji/google/lipstick.png new file mode 100644 index 0000000000..871a697a23 Binary files /dev/null and b/public/images/emoji/google/lipstick.png differ diff --git a/public/images/emoji/google/lock.png b/public/images/emoji/google/lock.png new file mode 100644 index 0000000000..f4bb547eaf Binary files /dev/null and b/public/images/emoji/google/lock.png differ diff --git a/public/images/emoji/google/lock_with_ink_pen.png b/public/images/emoji/google/lock_with_ink_pen.png new file mode 100644 index 0000000000..78c3032202 Binary files /dev/null and b/public/images/emoji/google/lock_with_ink_pen.png differ diff --git a/public/images/emoji/google/lollipop.png b/public/images/emoji/google/lollipop.png new file mode 100644 index 0000000000..de011193d2 Binary files /dev/null and b/public/images/emoji/google/lollipop.png differ diff --git a/public/images/emoji/google/loop.png b/public/images/emoji/google/loop.png new file mode 100644 index 0000000000..c8d62e68a7 Binary files /dev/null and b/public/images/emoji/google/loop.png differ diff --git a/public/images/emoji/google/loud_sound.png b/public/images/emoji/google/loud_sound.png new file mode 100644 index 0000000000..1424ff834c Binary files /dev/null and b/public/images/emoji/google/loud_sound.png differ diff --git a/public/images/emoji/google/loudspeaker.png b/public/images/emoji/google/loudspeaker.png new file mode 100644 index 0000000000..39d5afa9dd Binary files /dev/null and b/public/images/emoji/google/loudspeaker.png differ diff --git a/public/images/emoji/google/love_hotel.png b/public/images/emoji/google/love_hotel.png new file mode 100644 index 0000000000..3e714a2944 Binary files /dev/null and b/public/images/emoji/google/love_hotel.png differ diff --git a/public/images/emoji/google/love_letter.png b/public/images/emoji/google/love_letter.png new file mode 100644 index 0000000000..c8ea4c7ee8 Binary files /dev/null and b/public/images/emoji/google/love_letter.png differ diff --git a/public/images/emoji/google/low_brightness.png b/public/images/emoji/google/low_brightness.png new file mode 100644 index 0000000000..d6630f805b Binary files /dev/null and b/public/images/emoji/google/low_brightness.png differ diff --git a/public/images/emoji/google/m.png b/public/images/emoji/google/m.png new file mode 100644 index 0000000000..c6d07b2141 Binary files /dev/null and b/public/images/emoji/google/m.png differ diff --git a/public/images/emoji/google/mag.png b/public/images/emoji/google/mag.png new file mode 100644 index 0000000000..2691482279 Binary files /dev/null and b/public/images/emoji/google/mag.png differ diff --git a/public/images/emoji/google/mag_right.png b/public/images/emoji/google/mag_right.png new file mode 100644 index 0000000000..a7eea48a97 Binary files /dev/null and b/public/images/emoji/google/mag_right.png differ diff --git a/public/images/emoji/google/mahjong.png b/public/images/emoji/google/mahjong.png new file mode 100644 index 0000000000..90e33121d0 Binary files /dev/null and b/public/images/emoji/google/mahjong.png differ diff --git a/public/images/emoji/google/mailbox.png b/public/images/emoji/google/mailbox.png new file mode 100644 index 0000000000..96977e2302 Binary files /dev/null and b/public/images/emoji/google/mailbox.png differ diff --git a/public/images/emoji/google/mailbox_closed.png b/public/images/emoji/google/mailbox_closed.png new file mode 100644 index 0000000000..02868e7294 Binary files /dev/null and b/public/images/emoji/google/mailbox_closed.png differ diff --git a/public/images/emoji/google/mailbox_with_mail.png b/public/images/emoji/google/mailbox_with_mail.png new file mode 100644 index 0000000000..f1efa012b6 Binary files /dev/null and b/public/images/emoji/google/mailbox_with_mail.png differ diff --git a/public/images/emoji/google/mailbox_with_no_mail.png b/public/images/emoji/google/mailbox_with_no_mail.png new file mode 100644 index 0000000000..fb0942a113 Binary files /dev/null and b/public/images/emoji/google/mailbox_with_no_mail.png differ diff --git a/public/images/emoji/google/man.png b/public/images/emoji/google/man.png new file mode 100644 index 0000000000..620635122a Binary files /dev/null and b/public/images/emoji/google/man.png differ diff --git a/public/images/emoji/google/man_with_gua_pi_mao.png b/public/images/emoji/google/man_with_gua_pi_mao.png new file mode 100644 index 0000000000..09fba51b5c Binary files /dev/null and b/public/images/emoji/google/man_with_gua_pi_mao.png differ diff --git a/public/images/emoji/google/man_with_turban.png b/public/images/emoji/google/man_with_turban.png new file mode 100644 index 0000000000..8e5b9de8b9 Binary files /dev/null and b/public/images/emoji/google/man_with_turban.png differ diff --git a/public/images/emoji/google/mans_shoe.png b/public/images/emoji/google/mans_shoe.png new file mode 100644 index 0000000000..9c811a82bd Binary files /dev/null and b/public/images/emoji/google/mans_shoe.png differ diff --git a/public/images/emoji/google/maple_leaf.png b/public/images/emoji/google/maple_leaf.png new file mode 100644 index 0000000000..9c6825ee3a Binary files /dev/null and b/public/images/emoji/google/maple_leaf.png differ diff --git a/public/images/emoji/google/mask.png b/public/images/emoji/google/mask.png new file mode 100644 index 0000000000..b724685200 Binary files /dev/null and b/public/images/emoji/google/mask.png differ diff --git a/public/images/emoji/google/massage.png b/public/images/emoji/google/massage.png new file mode 100644 index 0000000000..df1ba61493 Binary files /dev/null and b/public/images/emoji/google/massage.png differ diff --git a/public/images/emoji/google/meat_on_bone.png b/public/images/emoji/google/meat_on_bone.png new file mode 100644 index 0000000000..8595de4c78 Binary files /dev/null and b/public/images/emoji/google/meat_on_bone.png differ diff --git a/public/images/emoji/google/mega.png b/public/images/emoji/google/mega.png new file mode 100644 index 0000000000..c72c2be357 Binary files /dev/null and b/public/images/emoji/google/mega.png differ diff --git a/public/images/emoji/google/melon.png b/public/images/emoji/google/melon.png new file mode 100644 index 0000000000..077bf80e83 Binary files /dev/null and b/public/images/emoji/google/melon.png differ diff --git a/public/images/emoji/google/memo.png b/public/images/emoji/google/memo.png new file mode 100644 index 0000000000..6ed3a14147 Binary files /dev/null and b/public/images/emoji/google/memo.png differ diff --git a/public/images/emoji/google/mens.png b/public/images/emoji/google/mens.png new file mode 100644 index 0000000000..2cba6e063f Binary files /dev/null and b/public/images/emoji/google/mens.png differ diff --git a/public/images/emoji/google/metro.png b/public/images/emoji/google/metro.png new file mode 100644 index 0000000000..4ee633bd5d Binary files /dev/null and b/public/images/emoji/google/metro.png differ diff --git a/public/images/emoji/google/microphone.png b/public/images/emoji/google/microphone.png new file mode 100644 index 0000000000..5bceb3f8cc Binary files /dev/null and b/public/images/emoji/google/microphone.png differ diff --git a/public/images/emoji/google/microscope.png b/public/images/emoji/google/microscope.png new file mode 100644 index 0000000000..21b5c1f12c Binary files /dev/null and b/public/images/emoji/google/microscope.png differ diff --git a/public/images/emoji/google/milky_way.png b/public/images/emoji/google/milky_way.png new file mode 100644 index 0000000000..830ae3f000 Binary files /dev/null and b/public/images/emoji/google/milky_way.png differ diff --git a/public/images/emoji/google/minibus.png b/public/images/emoji/google/minibus.png new file mode 100644 index 0000000000..65b536d1aa Binary files /dev/null and b/public/images/emoji/google/minibus.png differ diff --git a/public/images/emoji/google/minidisc.png b/public/images/emoji/google/minidisc.png new file mode 100644 index 0000000000..6532a4ed7f Binary files /dev/null and b/public/images/emoji/google/minidisc.png differ diff --git a/public/images/emoji/google/mobile_phone_off.png b/public/images/emoji/google/mobile_phone_off.png new file mode 100644 index 0000000000..3fd9fab04f Binary files /dev/null and b/public/images/emoji/google/mobile_phone_off.png differ diff --git a/public/images/emoji/google/money_with_wings.png b/public/images/emoji/google/money_with_wings.png new file mode 100644 index 0000000000..45b6814f31 Binary files /dev/null and b/public/images/emoji/google/money_with_wings.png differ diff --git a/public/images/emoji/google/moneybag.png b/public/images/emoji/google/moneybag.png new file mode 100644 index 0000000000..aebb8d4390 Binary files /dev/null and b/public/images/emoji/google/moneybag.png differ diff --git a/public/images/emoji/google/monkey.png b/public/images/emoji/google/monkey.png new file mode 100644 index 0000000000..2311b2a677 Binary files /dev/null and b/public/images/emoji/google/monkey.png differ diff --git a/public/images/emoji/google/monkey_face.png b/public/images/emoji/google/monkey_face.png new file mode 100644 index 0000000000..caea91db35 Binary files /dev/null and b/public/images/emoji/google/monkey_face.png differ diff --git a/public/images/emoji/google/monorail.png b/public/images/emoji/google/monorail.png new file mode 100644 index 0000000000..e7197d7e87 Binary files /dev/null and b/public/images/emoji/google/monorail.png differ diff --git a/public/images/emoji/google/moon.png b/public/images/emoji/google/moon.png new file mode 100644 index 0000000000..4828995c5d Binary files /dev/null and b/public/images/emoji/google/moon.png differ diff --git a/public/images/emoji/google/mortar_board.png b/public/images/emoji/google/mortar_board.png new file mode 100644 index 0000000000..8a3720a9fa Binary files /dev/null and b/public/images/emoji/google/mortar_board.png differ diff --git a/public/images/emoji/google/mount_fuji.png b/public/images/emoji/google/mount_fuji.png new file mode 100644 index 0000000000..8ed0604fba Binary files /dev/null and b/public/images/emoji/google/mount_fuji.png differ diff --git a/public/images/emoji/google/mountain_bicyclist.png b/public/images/emoji/google/mountain_bicyclist.png new file mode 100644 index 0000000000..60aeec8aec Binary files /dev/null and b/public/images/emoji/google/mountain_bicyclist.png differ diff --git a/public/images/emoji/google/mountain_cableway.png b/public/images/emoji/google/mountain_cableway.png new file mode 100644 index 0000000000..6b71d2656d Binary files /dev/null and b/public/images/emoji/google/mountain_cableway.png differ diff --git a/public/images/emoji/google/mountain_railway.png b/public/images/emoji/google/mountain_railway.png new file mode 100644 index 0000000000..5f460d66b1 Binary files /dev/null and b/public/images/emoji/google/mountain_railway.png differ diff --git a/public/images/emoji/google/mouse.png b/public/images/emoji/google/mouse.png new file mode 100644 index 0000000000..0c5d9b3b10 Binary files /dev/null and b/public/images/emoji/google/mouse.png differ diff --git a/public/images/emoji/google/mouse2.png b/public/images/emoji/google/mouse2.png new file mode 100644 index 0000000000..32089e9d9d Binary files /dev/null and b/public/images/emoji/google/mouse2.png differ diff --git a/public/images/emoji/google/movie_camera.png b/public/images/emoji/google/movie_camera.png new file mode 100644 index 0000000000..d8db67d776 Binary files /dev/null and b/public/images/emoji/google/movie_camera.png differ diff --git a/public/images/emoji/google/moyai.png b/public/images/emoji/google/moyai.png new file mode 100644 index 0000000000..f308131cba Binary files /dev/null and b/public/images/emoji/google/moyai.png differ diff --git a/public/images/emoji/google/muscle.png b/public/images/emoji/google/muscle.png new file mode 100644 index 0000000000..b446b01c94 Binary files /dev/null and b/public/images/emoji/google/muscle.png differ diff --git a/public/images/emoji/google/mushroom.png b/public/images/emoji/google/mushroom.png new file mode 100644 index 0000000000..5701b528cc Binary files /dev/null and b/public/images/emoji/google/mushroom.png differ diff --git a/public/images/emoji/google/musical_keyboard.png b/public/images/emoji/google/musical_keyboard.png new file mode 100644 index 0000000000..398331fc9f Binary files /dev/null and b/public/images/emoji/google/musical_keyboard.png differ diff --git a/public/images/emoji/google/musical_note.png b/public/images/emoji/google/musical_note.png new file mode 100644 index 0000000000..d15e9644f5 Binary files /dev/null and b/public/images/emoji/google/musical_note.png differ diff --git a/public/images/emoji/google/musical_score.png b/public/images/emoji/google/musical_score.png new file mode 100644 index 0000000000..af675be514 Binary files /dev/null and b/public/images/emoji/google/musical_score.png differ diff --git a/public/images/emoji/google/mute.png b/public/images/emoji/google/mute.png new file mode 100644 index 0000000000..ee4cba2b42 Binary files /dev/null and b/public/images/emoji/google/mute.png differ diff --git a/public/images/emoji/google/nail_care.png b/public/images/emoji/google/nail_care.png new file mode 100644 index 0000000000..5238de682d Binary files /dev/null and b/public/images/emoji/google/nail_care.png differ diff --git a/public/images/emoji/google/name_badge.png b/public/images/emoji/google/name_badge.png new file mode 100644 index 0000000000..89972980e3 Binary files /dev/null and b/public/images/emoji/google/name_badge.png differ diff --git a/public/images/emoji/google/necktie.png b/public/images/emoji/google/necktie.png new file mode 100644 index 0000000000..cca52c8aea Binary files /dev/null and b/public/images/emoji/google/necktie.png differ diff --git a/public/images/emoji/google/negative_squared_cross_mark.png b/public/images/emoji/google/negative_squared_cross_mark.png new file mode 100644 index 0000000000..4a71381ddd Binary files /dev/null and b/public/images/emoji/google/negative_squared_cross_mark.png differ diff --git a/public/images/emoji/google/neutral_face.png b/public/images/emoji/google/neutral_face.png new file mode 100644 index 0000000000..e9cf28e58e Binary files /dev/null and b/public/images/emoji/google/neutral_face.png differ diff --git a/public/images/emoji/google/new.png b/public/images/emoji/google/new.png new file mode 100644 index 0000000000..a794c6928f Binary files /dev/null and b/public/images/emoji/google/new.png differ diff --git a/public/images/emoji/google/new_moon.png b/public/images/emoji/google/new_moon.png new file mode 100644 index 0000000000..ae3175c0ef Binary files /dev/null and b/public/images/emoji/google/new_moon.png differ diff --git a/public/images/emoji/google/new_moon_with_face.png b/public/images/emoji/google/new_moon_with_face.png new file mode 100644 index 0000000000..66e5647940 Binary files /dev/null and b/public/images/emoji/google/new_moon_with_face.png differ diff --git a/public/images/emoji/google/newspaper.png b/public/images/emoji/google/newspaper.png new file mode 100644 index 0000000000..3545e4fe97 Binary files /dev/null and b/public/images/emoji/google/newspaper.png differ diff --git a/public/images/emoji/google/ng.png b/public/images/emoji/google/ng.png new file mode 100644 index 0000000000..16dd308963 Binary files /dev/null and b/public/images/emoji/google/ng.png differ diff --git a/public/images/emoji/google/night_with_stars.png b/public/images/emoji/google/night_with_stars.png new file mode 100644 index 0000000000..1ac4b9761d Binary files /dev/null and b/public/images/emoji/google/night_with_stars.png differ diff --git a/public/images/emoji/google/nine.png b/public/images/emoji/google/nine.png new file mode 100644 index 0000000000..702e20b9c0 Binary files /dev/null and b/public/images/emoji/google/nine.png differ diff --git a/public/images/emoji/google/no_bell.png b/public/images/emoji/google/no_bell.png new file mode 100644 index 0000000000..48b2a867d9 Binary files /dev/null and b/public/images/emoji/google/no_bell.png differ diff --git a/public/images/emoji/google/no_bicycles.png b/public/images/emoji/google/no_bicycles.png new file mode 100644 index 0000000000..9464d12a26 Binary files /dev/null and b/public/images/emoji/google/no_bicycles.png differ diff --git a/public/images/emoji/google/no_entry.png b/public/images/emoji/google/no_entry.png new file mode 100644 index 0000000000..0820299a73 Binary files /dev/null and b/public/images/emoji/google/no_entry.png differ diff --git a/public/images/emoji/google/no_entry_sign.png b/public/images/emoji/google/no_entry_sign.png new file mode 100644 index 0000000000..e7f37677e4 Binary files /dev/null and b/public/images/emoji/google/no_entry_sign.png differ diff --git a/public/images/emoji/google/no_good.png b/public/images/emoji/google/no_good.png new file mode 100644 index 0000000000..ac08d33530 Binary files /dev/null and b/public/images/emoji/google/no_good.png differ diff --git a/public/images/emoji/google/no_mobile_phones.png b/public/images/emoji/google/no_mobile_phones.png new file mode 100644 index 0000000000..7bd5edbbee Binary files /dev/null and b/public/images/emoji/google/no_mobile_phones.png differ diff --git a/public/images/emoji/google/no_mouth.png b/public/images/emoji/google/no_mouth.png new file mode 100644 index 0000000000..c91368ffae Binary files /dev/null and b/public/images/emoji/google/no_mouth.png differ diff --git a/public/images/emoji/google/no_pedestrians.png b/public/images/emoji/google/no_pedestrians.png new file mode 100644 index 0000000000..4aaf7e0ea1 Binary files /dev/null and b/public/images/emoji/google/no_pedestrians.png differ diff --git a/public/images/emoji/google/no_smoking.png b/public/images/emoji/google/no_smoking.png new file mode 100644 index 0000000000..345dcf2c8b Binary files /dev/null and b/public/images/emoji/google/no_smoking.png differ diff --git a/public/images/emoji/google/non-potable_water.png b/public/images/emoji/google/non-potable_water.png new file mode 100644 index 0000000000..85267b5920 Binary files /dev/null and b/public/images/emoji/google/non-potable_water.png differ diff --git a/public/images/emoji/google/nose.png b/public/images/emoji/google/nose.png new file mode 100644 index 0000000000..de3d25a7bc Binary files /dev/null and b/public/images/emoji/google/nose.png differ diff --git a/public/images/emoji/google/notebook.png b/public/images/emoji/google/notebook.png new file mode 100644 index 0000000000..68808bbd0d Binary files /dev/null and b/public/images/emoji/google/notebook.png differ diff --git a/public/images/emoji/google/notebook_with_decorative_cover.png b/public/images/emoji/google/notebook_with_decorative_cover.png new file mode 100644 index 0000000000..692508cf20 Binary files /dev/null and b/public/images/emoji/google/notebook_with_decorative_cover.png differ diff --git a/public/images/emoji/google/notes.png b/public/images/emoji/google/notes.png new file mode 100644 index 0000000000..faf6da3668 Binary files /dev/null and b/public/images/emoji/google/notes.png differ diff --git a/public/images/emoji/google/nut_and_bolt.png b/public/images/emoji/google/nut_and_bolt.png new file mode 100644 index 0000000000..67f7323f46 Binary files /dev/null and b/public/images/emoji/google/nut_and_bolt.png differ diff --git a/public/images/emoji/google/o.png b/public/images/emoji/google/o.png new file mode 100644 index 0000000000..e0d08de45e Binary files /dev/null and b/public/images/emoji/google/o.png differ diff --git a/public/images/emoji/google/o2.png b/public/images/emoji/google/o2.png new file mode 100644 index 0000000000..03cf43d5af Binary files /dev/null and b/public/images/emoji/google/o2.png differ diff --git a/public/images/emoji/google/ocean.png b/public/images/emoji/google/ocean.png new file mode 100644 index 0000000000..c24ecc65bd Binary files /dev/null and b/public/images/emoji/google/ocean.png differ diff --git a/public/images/emoji/google/octopus.png b/public/images/emoji/google/octopus.png new file mode 100644 index 0000000000..35875ccdd7 Binary files /dev/null and b/public/images/emoji/google/octopus.png differ diff --git a/public/images/emoji/google/oden.png b/public/images/emoji/google/oden.png new file mode 100644 index 0000000000..0f0c1e426b Binary files /dev/null and b/public/images/emoji/google/oden.png differ diff --git a/public/images/emoji/google/office.png b/public/images/emoji/google/office.png new file mode 100644 index 0000000000..678d6d4b19 Binary files /dev/null and b/public/images/emoji/google/office.png differ diff --git a/public/images/emoji/google/ok.png b/public/images/emoji/google/ok.png new file mode 100644 index 0000000000..5071f9e5ed Binary files /dev/null and b/public/images/emoji/google/ok.png differ diff --git a/public/images/emoji/google/ok_hand.png b/public/images/emoji/google/ok_hand.png new file mode 100644 index 0000000000..1c3eefddac Binary files /dev/null and b/public/images/emoji/google/ok_hand.png differ diff --git a/public/images/emoji/google/ok_woman.png b/public/images/emoji/google/ok_woman.png new file mode 100644 index 0000000000..edfb0e6f28 Binary files /dev/null and b/public/images/emoji/google/ok_woman.png differ diff --git a/public/images/emoji/google/older_man.png b/public/images/emoji/google/older_man.png new file mode 100644 index 0000000000..07706ffd86 Binary files /dev/null and b/public/images/emoji/google/older_man.png differ diff --git a/public/images/emoji/google/older_woman.png b/public/images/emoji/google/older_woman.png new file mode 100644 index 0000000000..5179486912 Binary files /dev/null and b/public/images/emoji/google/older_woman.png differ diff --git a/public/images/emoji/google/on.png b/public/images/emoji/google/on.png new file mode 100644 index 0000000000..7423312480 Binary files /dev/null and b/public/images/emoji/google/on.png differ diff --git a/public/images/emoji/google/oncoming_automobile.png b/public/images/emoji/google/oncoming_automobile.png new file mode 100644 index 0000000000..cac7e1043e Binary files /dev/null and b/public/images/emoji/google/oncoming_automobile.png differ diff --git a/public/images/emoji/google/oncoming_bus.png b/public/images/emoji/google/oncoming_bus.png new file mode 100644 index 0000000000..c96b3e28ba Binary files /dev/null and b/public/images/emoji/google/oncoming_bus.png differ diff --git a/public/images/emoji/google/oncoming_police_car.png b/public/images/emoji/google/oncoming_police_car.png new file mode 100644 index 0000000000..6b7f10eab7 Binary files /dev/null and b/public/images/emoji/google/oncoming_police_car.png differ diff --git a/public/images/emoji/google/oncoming_taxi.png b/public/images/emoji/google/oncoming_taxi.png new file mode 100644 index 0000000000..20a3741a8a Binary files /dev/null and b/public/images/emoji/google/oncoming_taxi.png differ diff --git a/public/images/emoji/google/one.png b/public/images/emoji/google/one.png new file mode 100644 index 0000000000..771c6a92ef Binary files /dev/null and b/public/images/emoji/google/one.png differ diff --git a/public/images/emoji/google/open_book.png b/public/images/emoji/google/open_book.png new file mode 100644 index 0000000000..77ee57a108 Binary files /dev/null and b/public/images/emoji/google/open_book.png differ diff --git a/public/images/emoji/google/open_file_folder.png b/public/images/emoji/google/open_file_folder.png new file mode 100644 index 0000000000..48c23a7041 Binary files /dev/null and b/public/images/emoji/google/open_file_folder.png differ diff --git a/public/images/emoji/google/open_hands.png b/public/images/emoji/google/open_hands.png new file mode 100644 index 0000000000..5836c119d0 Binary files /dev/null and b/public/images/emoji/google/open_hands.png differ diff --git a/public/images/emoji/google/open_mouth.png b/public/images/emoji/google/open_mouth.png new file mode 100644 index 0000000000..b341b36892 Binary files /dev/null and b/public/images/emoji/google/open_mouth.png differ diff --git a/public/images/emoji/google/ophiuchus.png b/public/images/emoji/google/ophiuchus.png new file mode 100644 index 0000000000..5b90e66367 Binary files /dev/null and b/public/images/emoji/google/ophiuchus.png differ diff --git a/public/images/emoji/google/orange_book.png b/public/images/emoji/google/orange_book.png new file mode 100644 index 0000000000..fefef00672 Binary files /dev/null and b/public/images/emoji/google/orange_book.png differ diff --git a/public/images/emoji/google/outbox_tray.png b/public/images/emoji/google/outbox_tray.png new file mode 100644 index 0000000000..b22fd44fea Binary files /dev/null and b/public/images/emoji/google/outbox_tray.png differ diff --git a/public/images/emoji/google/ox.png b/public/images/emoji/google/ox.png new file mode 100644 index 0000000000..22df911bf0 Binary files /dev/null and b/public/images/emoji/google/ox.png differ diff --git a/public/images/emoji/google/package.png b/public/images/emoji/google/package.png new file mode 100644 index 0000000000..22990fe0f0 Binary files /dev/null and b/public/images/emoji/google/package.png differ diff --git a/public/images/emoji/google/page_facing_up.png b/public/images/emoji/google/page_facing_up.png new file mode 100644 index 0000000000..3289cc0720 Binary files /dev/null and b/public/images/emoji/google/page_facing_up.png differ diff --git a/public/images/emoji/google/page_with_curl.png b/public/images/emoji/google/page_with_curl.png new file mode 100644 index 0000000000..cb649547fa Binary files /dev/null and b/public/images/emoji/google/page_with_curl.png differ diff --git a/public/images/emoji/google/pager.png b/public/images/emoji/google/pager.png new file mode 100644 index 0000000000..a6d4483f48 Binary files /dev/null and b/public/images/emoji/google/pager.png differ diff --git a/public/images/emoji/google/palm_tree.png b/public/images/emoji/google/palm_tree.png new file mode 100644 index 0000000000..0026d5abae Binary files /dev/null and b/public/images/emoji/google/palm_tree.png differ diff --git a/public/images/emoji/google/panda_face.png b/public/images/emoji/google/panda_face.png new file mode 100644 index 0000000000..fdbf57e686 Binary files /dev/null and b/public/images/emoji/google/panda_face.png differ diff --git a/public/images/emoji/google/paperclip.png b/public/images/emoji/google/paperclip.png new file mode 100644 index 0000000000..01eee99b8a Binary files /dev/null and b/public/images/emoji/google/paperclip.png differ diff --git a/public/images/emoji/google/parking.png b/public/images/emoji/google/parking.png new file mode 100644 index 0000000000..c7f775cef7 Binary files /dev/null and b/public/images/emoji/google/parking.png differ diff --git a/public/images/emoji/google/part_alternation_mark.png b/public/images/emoji/google/part_alternation_mark.png new file mode 100644 index 0000000000..cdf5367211 Binary files /dev/null and b/public/images/emoji/google/part_alternation_mark.png differ diff --git a/public/images/emoji/google/partly_sunny.png b/public/images/emoji/google/partly_sunny.png new file mode 100644 index 0000000000..09c4a02cc0 Binary files /dev/null and b/public/images/emoji/google/partly_sunny.png differ diff --git a/public/images/emoji/google/passport_control.png b/public/images/emoji/google/passport_control.png new file mode 100644 index 0000000000..7f03c51bda Binary files /dev/null and b/public/images/emoji/google/passport_control.png differ diff --git a/public/images/emoji/google/paw_prints.png b/public/images/emoji/google/paw_prints.png new file mode 100644 index 0000000000..3f88932e4a Binary files /dev/null and b/public/images/emoji/google/paw_prints.png differ diff --git a/public/images/emoji/google/peach.png b/public/images/emoji/google/peach.png new file mode 100644 index 0000000000..cd84170fb6 Binary files /dev/null and b/public/images/emoji/google/peach.png differ diff --git a/public/images/emoji/google/pear.png b/public/images/emoji/google/pear.png new file mode 100644 index 0000000000..d865e4a9fb Binary files /dev/null and b/public/images/emoji/google/pear.png differ diff --git a/public/images/emoji/google/pencil.png b/public/images/emoji/google/pencil.png new file mode 100644 index 0000000000..068ed18ff8 Binary files /dev/null and b/public/images/emoji/google/pencil.png differ diff --git a/public/images/emoji/google/pencil2.png b/public/images/emoji/google/pencil2.png new file mode 100644 index 0000000000..3cd7a6b6f1 Binary files /dev/null and b/public/images/emoji/google/pencil2.png differ diff --git a/public/images/emoji/google/penguin.png b/public/images/emoji/google/penguin.png new file mode 100644 index 0000000000..6e81134705 Binary files /dev/null and b/public/images/emoji/google/penguin.png differ diff --git a/public/images/emoji/google/pensive.png b/public/images/emoji/google/pensive.png new file mode 100644 index 0000000000..0443c84d03 Binary files /dev/null and b/public/images/emoji/google/pensive.png differ diff --git a/public/images/emoji/google/performing_arts.png b/public/images/emoji/google/performing_arts.png new file mode 100644 index 0000000000..d09fe8f5ff Binary files /dev/null and b/public/images/emoji/google/performing_arts.png differ diff --git a/public/images/emoji/google/persevere.png b/public/images/emoji/google/persevere.png new file mode 100644 index 0000000000..62b1c9541e Binary files /dev/null and b/public/images/emoji/google/persevere.png differ diff --git a/public/images/emoji/google/person_frowning.png b/public/images/emoji/google/person_frowning.png new file mode 100644 index 0000000000..a33e2caf66 Binary files /dev/null and b/public/images/emoji/google/person_frowning.png differ diff --git a/public/images/emoji/google/person_with_blond_hair.png b/public/images/emoji/google/person_with_blond_hair.png new file mode 100644 index 0000000000..2a2f1493fe Binary files /dev/null and b/public/images/emoji/google/person_with_blond_hair.png differ diff --git a/public/images/emoji/google/person_with_pouting_face.png b/public/images/emoji/google/person_with_pouting_face.png new file mode 100644 index 0000000000..f7e7280bbd Binary files /dev/null and b/public/images/emoji/google/person_with_pouting_face.png differ diff --git a/public/images/emoji/google/phone.png b/public/images/emoji/google/phone.png new file mode 100644 index 0000000000..36500f2030 Binary files /dev/null and b/public/images/emoji/google/phone.png differ diff --git a/public/images/emoji/google/pig.png b/public/images/emoji/google/pig.png new file mode 100644 index 0000000000..a95b84730b Binary files /dev/null and b/public/images/emoji/google/pig.png differ diff --git a/public/images/emoji/google/pig2.png b/public/images/emoji/google/pig2.png new file mode 100644 index 0000000000..8e78feae9d Binary files /dev/null and b/public/images/emoji/google/pig2.png differ diff --git a/public/images/emoji/google/pig_nose.png b/public/images/emoji/google/pig_nose.png new file mode 100644 index 0000000000..5afe033f19 Binary files /dev/null and b/public/images/emoji/google/pig_nose.png differ diff --git a/public/images/emoji/google/pill.png b/public/images/emoji/google/pill.png new file mode 100644 index 0000000000..7189d2f8e3 Binary files /dev/null and b/public/images/emoji/google/pill.png differ diff --git a/public/images/emoji/google/pineapple.png b/public/images/emoji/google/pineapple.png new file mode 100644 index 0000000000..2417a411a0 Binary files /dev/null and b/public/images/emoji/google/pineapple.png differ diff --git a/public/images/emoji/google/pisces.png b/public/images/emoji/google/pisces.png new file mode 100644 index 0000000000..bfd44f5d9e Binary files /dev/null and b/public/images/emoji/google/pisces.png differ diff --git a/public/images/emoji/google/pizza.png b/public/images/emoji/google/pizza.png new file mode 100644 index 0000000000..4d1f51a728 Binary files /dev/null and b/public/images/emoji/google/pizza.png differ diff --git a/public/images/emoji/google/point_down.png b/public/images/emoji/google/point_down.png new file mode 100644 index 0000000000..3f0342b0a4 Binary files /dev/null and b/public/images/emoji/google/point_down.png differ diff --git a/public/images/emoji/google/point_left.png b/public/images/emoji/google/point_left.png new file mode 100644 index 0000000000..70036882a5 Binary files /dev/null and b/public/images/emoji/google/point_left.png differ diff --git a/public/images/emoji/google/point_right.png b/public/images/emoji/google/point_right.png new file mode 100644 index 0000000000..594d8b1af3 Binary files /dev/null and b/public/images/emoji/google/point_right.png differ diff --git a/public/images/emoji/google/point_up.png b/public/images/emoji/google/point_up.png new file mode 100644 index 0000000000..decda3bd03 Binary files /dev/null and b/public/images/emoji/google/point_up.png differ diff --git a/public/images/emoji/google/point_up_2.png b/public/images/emoji/google/point_up_2.png new file mode 100644 index 0000000000..32a1386542 Binary files /dev/null and b/public/images/emoji/google/point_up_2.png differ diff --git a/public/images/emoji/google/police_car.png b/public/images/emoji/google/police_car.png new file mode 100644 index 0000000000..1bd7b0acc0 Binary files /dev/null and b/public/images/emoji/google/police_car.png differ diff --git a/public/images/emoji/google/poodle.png b/public/images/emoji/google/poodle.png new file mode 100644 index 0000000000..bccec8b545 Binary files /dev/null and b/public/images/emoji/google/poodle.png differ diff --git a/public/images/emoji/google/poop.png b/public/images/emoji/google/poop.png new file mode 100644 index 0000000000..18b0c41e54 Binary files /dev/null and b/public/images/emoji/google/poop.png differ diff --git a/public/images/emoji/google/post_office.png b/public/images/emoji/google/post_office.png new file mode 100644 index 0000000000..e534a046ed Binary files /dev/null and b/public/images/emoji/google/post_office.png differ diff --git a/public/images/emoji/google/postal_horn.png b/public/images/emoji/google/postal_horn.png new file mode 100644 index 0000000000..98ab78b89f Binary files /dev/null and b/public/images/emoji/google/postal_horn.png differ diff --git a/public/images/emoji/google/postbox.png b/public/images/emoji/google/postbox.png new file mode 100644 index 0000000000..1043a035c2 Binary files /dev/null and b/public/images/emoji/google/postbox.png differ diff --git a/public/images/emoji/google/potable_water.png b/public/images/emoji/google/potable_water.png new file mode 100644 index 0000000000..286138e798 Binary files /dev/null and b/public/images/emoji/google/potable_water.png differ diff --git a/public/images/emoji/google/pouch.png b/public/images/emoji/google/pouch.png new file mode 100644 index 0000000000..f40337403d Binary files /dev/null and b/public/images/emoji/google/pouch.png differ diff --git a/public/images/emoji/google/poultry_leg.png b/public/images/emoji/google/poultry_leg.png new file mode 100644 index 0000000000..05e1307fbd Binary files /dev/null and b/public/images/emoji/google/poultry_leg.png differ diff --git a/public/images/emoji/google/pound.png b/public/images/emoji/google/pound.png new file mode 100644 index 0000000000..fdb0efd176 Binary files /dev/null and b/public/images/emoji/google/pound.png differ diff --git a/public/images/emoji/google/pouting_cat.png b/public/images/emoji/google/pouting_cat.png new file mode 100644 index 0000000000..339a872dc8 Binary files /dev/null and b/public/images/emoji/google/pouting_cat.png differ diff --git a/public/images/emoji/google/pray.png b/public/images/emoji/google/pray.png new file mode 100644 index 0000000000..2c617f2993 Binary files /dev/null and b/public/images/emoji/google/pray.png differ diff --git a/public/images/emoji/google/princess.png b/public/images/emoji/google/princess.png new file mode 100644 index 0000000000..c6991b3e3a Binary files /dev/null and b/public/images/emoji/google/princess.png differ diff --git a/public/images/emoji/google/punch.png b/public/images/emoji/google/punch.png new file mode 100644 index 0000000000..96672f8da3 Binary files /dev/null and b/public/images/emoji/google/punch.png differ diff --git a/public/images/emoji/google/purple_heart.png b/public/images/emoji/google/purple_heart.png new file mode 100644 index 0000000000..32206b9ef3 Binary files /dev/null and b/public/images/emoji/google/purple_heart.png differ diff --git a/public/images/emoji/google/purse.png b/public/images/emoji/google/purse.png new file mode 100644 index 0000000000..2546187cce Binary files /dev/null and b/public/images/emoji/google/purse.png differ diff --git a/public/images/emoji/google/pushpin.png b/public/images/emoji/google/pushpin.png new file mode 100644 index 0000000000..077ab406ea Binary files /dev/null and b/public/images/emoji/google/pushpin.png differ diff --git a/public/images/emoji/google/put_litter_in_its_place.png b/public/images/emoji/google/put_litter_in_its_place.png new file mode 100644 index 0000000000..0b2cb59c2e Binary files /dev/null and b/public/images/emoji/google/put_litter_in_its_place.png differ diff --git a/public/images/emoji/google/question.png b/public/images/emoji/google/question.png new file mode 100644 index 0000000000..bbcf3deef4 Binary files /dev/null and b/public/images/emoji/google/question.png differ diff --git a/public/images/emoji/google/rabbit.png b/public/images/emoji/google/rabbit.png new file mode 100644 index 0000000000..49aeb25403 Binary files /dev/null and b/public/images/emoji/google/rabbit.png differ diff --git a/public/images/emoji/google/rabbit2.png b/public/images/emoji/google/rabbit2.png new file mode 100644 index 0000000000..447e4192c9 Binary files /dev/null and b/public/images/emoji/google/rabbit2.png differ diff --git a/public/images/emoji/google/racehorse.png b/public/images/emoji/google/racehorse.png new file mode 100644 index 0000000000..f3d352d9d5 Binary files /dev/null and b/public/images/emoji/google/racehorse.png differ diff --git a/public/images/emoji/google/radio.png b/public/images/emoji/google/radio.png new file mode 100644 index 0000000000..e922eb87fd Binary files /dev/null and b/public/images/emoji/google/radio.png differ diff --git a/public/images/emoji/google/radio_button.png b/public/images/emoji/google/radio_button.png new file mode 100644 index 0000000000..d9cd8e03e5 Binary files /dev/null and b/public/images/emoji/google/radio_button.png differ diff --git a/public/images/emoji/google/rage.png b/public/images/emoji/google/rage.png new file mode 100644 index 0000000000..0117a490b1 Binary files /dev/null and b/public/images/emoji/google/rage.png differ diff --git a/public/images/emoji/google/railway_car.png b/public/images/emoji/google/railway_car.png new file mode 100644 index 0000000000..8fdb8a71ad Binary files /dev/null and b/public/images/emoji/google/railway_car.png differ diff --git a/public/images/emoji/google/rainbow.png b/public/images/emoji/google/rainbow.png new file mode 100644 index 0000000000..4747144d82 Binary files /dev/null and b/public/images/emoji/google/rainbow.png differ diff --git a/public/images/emoji/google/raised_hand.png b/public/images/emoji/google/raised_hand.png new file mode 100644 index 0000000000..e8b03e0c20 Binary files /dev/null and b/public/images/emoji/google/raised_hand.png differ diff --git a/public/images/emoji/google/raised_hands.png b/public/images/emoji/google/raised_hands.png new file mode 100644 index 0000000000..5969b78e65 Binary files /dev/null and b/public/images/emoji/google/raised_hands.png differ diff --git a/public/images/emoji/google/raising_hand.png b/public/images/emoji/google/raising_hand.png new file mode 100644 index 0000000000..94b7cff59b Binary files /dev/null and b/public/images/emoji/google/raising_hand.png differ diff --git a/public/images/emoji/google/ram.png b/public/images/emoji/google/ram.png new file mode 100644 index 0000000000..86a12ecf05 Binary files /dev/null and b/public/images/emoji/google/ram.png differ diff --git a/public/images/emoji/google/ramen.png b/public/images/emoji/google/ramen.png new file mode 100644 index 0000000000..b627e7ac82 Binary files /dev/null and b/public/images/emoji/google/ramen.png differ diff --git a/public/images/emoji/google/rat.png b/public/images/emoji/google/rat.png new file mode 100644 index 0000000000..a6dda927a9 Binary files /dev/null and b/public/images/emoji/google/rat.png differ diff --git a/public/images/emoji/google/recycle.png b/public/images/emoji/google/recycle.png new file mode 100644 index 0000000000..6be86eaea7 Binary files /dev/null and b/public/images/emoji/google/recycle.png differ diff --git a/public/images/emoji/google/red_car.png b/public/images/emoji/google/red_car.png new file mode 100644 index 0000000000..f1be3d95ae Binary files /dev/null and b/public/images/emoji/google/red_car.png differ diff --git a/public/images/emoji/google/red_circle.png b/public/images/emoji/google/red_circle.png new file mode 100644 index 0000000000..7bce494238 Binary files /dev/null and b/public/images/emoji/google/red_circle.png differ diff --git a/public/images/emoji/google/registered.png b/public/images/emoji/google/registered.png new file mode 100644 index 0000000000..9dc942e92b Binary files /dev/null and b/public/images/emoji/google/registered.png differ diff --git a/public/images/emoji/google/relaxed.png b/public/images/emoji/google/relaxed.png new file mode 100644 index 0000000000..5f0cec556f Binary files /dev/null and b/public/images/emoji/google/relaxed.png differ diff --git a/public/images/emoji/google/relieved.png b/public/images/emoji/google/relieved.png new file mode 100644 index 0000000000..020b4ed57b Binary files /dev/null and b/public/images/emoji/google/relieved.png differ diff --git a/public/images/emoji/google/repeat.png b/public/images/emoji/google/repeat.png new file mode 100644 index 0000000000..c0952f02f3 Binary files /dev/null and b/public/images/emoji/google/repeat.png differ diff --git a/public/images/emoji/google/repeat_one.png b/public/images/emoji/google/repeat_one.png new file mode 100644 index 0000000000..a3e0d64150 Binary files /dev/null and b/public/images/emoji/google/repeat_one.png differ diff --git a/public/images/emoji/google/restroom.png b/public/images/emoji/google/restroom.png new file mode 100644 index 0000000000..1166e06aa5 Binary files /dev/null and b/public/images/emoji/google/restroom.png differ diff --git a/public/images/emoji/google/revolving_hearts.png b/public/images/emoji/google/revolving_hearts.png new file mode 100644 index 0000000000..9a9f6c1b74 Binary files /dev/null and b/public/images/emoji/google/revolving_hearts.png differ diff --git a/public/images/emoji/google/rewind.png b/public/images/emoji/google/rewind.png new file mode 100644 index 0000000000..6d850b98f9 Binary files /dev/null and b/public/images/emoji/google/rewind.png differ diff --git a/public/images/emoji/google/ribbon.png b/public/images/emoji/google/ribbon.png new file mode 100644 index 0000000000..cfaa698528 Binary files /dev/null and b/public/images/emoji/google/ribbon.png differ diff --git a/public/images/emoji/google/rice.png b/public/images/emoji/google/rice.png new file mode 100644 index 0000000000..3404afb438 Binary files /dev/null and b/public/images/emoji/google/rice.png differ diff --git a/public/images/emoji/google/rice_ball.png b/public/images/emoji/google/rice_ball.png new file mode 100644 index 0000000000..8a92252f29 Binary files /dev/null and b/public/images/emoji/google/rice_ball.png differ diff --git a/public/images/emoji/google/rice_cracker.png b/public/images/emoji/google/rice_cracker.png new file mode 100644 index 0000000000..68957de535 Binary files /dev/null and b/public/images/emoji/google/rice_cracker.png differ diff --git a/public/images/emoji/google/rice_scene.png b/public/images/emoji/google/rice_scene.png new file mode 100644 index 0000000000..42b7f321b5 Binary files /dev/null and b/public/images/emoji/google/rice_scene.png differ diff --git a/public/images/emoji/google/ring.png b/public/images/emoji/google/ring.png new file mode 100644 index 0000000000..2047eb036a Binary files /dev/null and b/public/images/emoji/google/ring.png differ diff --git a/public/images/emoji/google/rocket.png b/public/images/emoji/google/rocket.png new file mode 100644 index 0000000000..c0cba9ff8e Binary files /dev/null and b/public/images/emoji/google/rocket.png differ diff --git a/public/images/emoji/google/roller_coaster.png b/public/images/emoji/google/roller_coaster.png new file mode 100644 index 0000000000..1f9d046991 Binary files /dev/null and b/public/images/emoji/google/roller_coaster.png differ diff --git a/public/images/emoji/google/rooster.png b/public/images/emoji/google/rooster.png new file mode 100644 index 0000000000..90754020bc Binary files /dev/null and b/public/images/emoji/google/rooster.png differ diff --git a/public/images/emoji/google/rose.png b/public/images/emoji/google/rose.png new file mode 100644 index 0000000000..e312838596 Binary files /dev/null and b/public/images/emoji/google/rose.png differ diff --git a/public/images/emoji/google/rotating_light.png b/public/images/emoji/google/rotating_light.png new file mode 100644 index 0000000000..3481fa444f Binary files /dev/null and b/public/images/emoji/google/rotating_light.png differ diff --git a/public/images/emoji/google/round_pushpin.png b/public/images/emoji/google/round_pushpin.png new file mode 100644 index 0000000000..99d774b2dc Binary files /dev/null and b/public/images/emoji/google/round_pushpin.png differ diff --git a/public/images/emoji/google/rowboat.png b/public/images/emoji/google/rowboat.png new file mode 100644 index 0000000000..bcef277f1d Binary files /dev/null and b/public/images/emoji/google/rowboat.png differ diff --git a/public/images/emoji/google/ru.png b/public/images/emoji/google/ru.png new file mode 100644 index 0000000000..d7db8a6d22 Binary files /dev/null and b/public/images/emoji/google/ru.png differ diff --git a/public/images/emoji/google/rugby_football.png b/public/images/emoji/google/rugby_football.png new file mode 100644 index 0000000000..5494fc9a7b Binary files /dev/null and b/public/images/emoji/google/rugby_football.png differ diff --git a/public/images/emoji/google/runner.png b/public/images/emoji/google/runner.png new file mode 100644 index 0000000000..af6a23e44c Binary files /dev/null and b/public/images/emoji/google/runner.png differ diff --git a/public/images/emoji/google/running.png b/public/images/emoji/google/running.png new file mode 100644 index 0000000000..af6a23e44c Binary files /dev/null and b/public/images/emoji/google/running.png differ diff --git a/public/images/emoji/google/running_shirt_with_sash.png b/public/images/emoji/google/running_shirt_with_sash.png new file mode 100644 index 0000000000..1c8f965330 Binary files /dev/null and b/public/images/emoji/google/running_shirt_with_sash.png differ diff --git a/public/images/emoji/google/sa.png b/public/images/emoji/google/sa.png new file mode 100644 index 0000000000..393ac91d54 Binary files /dev/null and b/public/images/emoji/google/sa.png differ diff --git a/public/images/emoji/google/sagittarius.png b/public/images/emoji/google/sagittarius.png new file mode 100644 index 0000000000..3f5adbcfdb Binary files /dev/null and b/public/images/emoji/google/sagittarius.png differ diff --git a/public/images/emoji/google/sailboat.png b/public/images/emoji/google/sailboat.png new file mode 100644 index 0000000000..1a34dcba48 Binary files /dev/null and b/public/images/emoji/google/sailboat.png differ diff --git a/public/images/emoji/google/sake.png b/public/images/emoji/google/sake.png new file mode 100644 index 0000000000..259c4a5bc4 Binary files /dev/null and b/public/images/emoji/google/sake.png differ diff --git a/public/images/emoji/google/sandal.png b/public/images/emoji/google/sandal.png new file mode 100644 index 0000000000..16548990b2 Binary files /dev/null and b/public/images/emoji/google/sandal.png differ diff --git a/public/images/emoji/google/santa.png b/public/images/emoji/google/santa.png new file mode 100644 index 0000000000..66076b595e Binary files /dev/null and b/public/images/emoji/google/santa.png differ diff --git a/public/images/emoji/google/satellite.png b/public/images/emoji/google/satellite.png new file mode 100644 index 0000000000..8c7ad35366 Binary files /dev/null and b/public/images/emoji/google/satellite.png differ diff --git a/public/images/emoji/google/satisfied.png b/public/images/emoji/google/satisfied.png new file mode 100644 index 0000000000..8265a2e20c Binary files /dev/null and b/public/images/emoji/google/satisfied.png differ diff --git a/public/images/emoji/google/saxophone.png b/public/images/emoji/google/saxophone.png new file mode 100644 index 0000000000..688a0e2343 Binary files /dev/null and b/public/images/emoji/google/saxophone.png differ diff --git a/public/images/emoji/google/school.png b/public/images/emoji/google/school.png new file mode 100644 index 0000000000..1f1fe610ad Binary files /dev/null and b/public/images/emoji/google/school.png differ diff --git a/public/images/emoji/google/school_satchel.png b/public/images/emoji/google/school_satchel.png new file mode 100644 index 0000000000..bf4abcb2a1 Binary files /dev/null and b/public/images/emoji/google/school_satchel.png differ diff --git a/public/images/emoji/google/scissors.png b/public/images/emoji/google/scissors.png new file mode 100644 index 0000000000..95659681b7 Binary files /dev/null and b/public/images/emoji/google/scissors.png differ diff --git a/public/images/emoji/google/scorpius.png b/public/images/emoji/google/scorpius.png new file mode 100644 index 0000000000..e02f2f99c3 Binary files /dev/null and b/public/images/emoji/google/scorpius.png differ diff --git a/public/images/emoji/google/scream.png b/public/images/emoji/google/scream.png new file mode 100644 index 0000000000..d8f44d95e0 Binary files /dev/null and b/public/images/emoji/google/scream.png differ diff --git a/public/images/emoji/google/scream_cat.png b/public/images/emoji/google/scream_cat.png new file mode 100644 index 0000000000..c472c67a7d Binary files /dev/null and b/public/images/emoji/google/scream_cat.png differ diff --git a/public/images/emoji/google/scroll.png b/public/images/emoji/google/scroll.png new file mode 100644 index 0000000000..8c07ec25ec Binary files /dev/null and b/public/images/emoji/google/scroll.png differ diff --git a/public/images/emoji/google/seat.png b/public/images/emoji/google/seat.png new file mode 100644 index 0000000000..7248d889d7 Binary files /dev/null and b/public/images/emoji/google/seat.png differ diff --git a/public/images/emoji/google/secret.png b/public/images/emoji/google/secret.png new file mode 100644 index 0000000000..71399b9030 Binary files /dev/null and b/public/images/emoji/google/secret.png differ diff --git a/public/images/emoji/google/see_no_evil.png b/public/images/emoji/google/see_no_evil.png new file mode 100644 index 0000000000..db583b81ff Binary files /dev/null and b/public/images/emoji/google/see_no_evil.png differ diff --git a/public/images/emoji/google/seedling.png b/public/images/emoji/google/seedling.png new file mode 100644 index 0000000000..f349d7ab5b Binary files /dev/null and b/public/images/emoji/google/seedling.png differ diff --git a/public/images/emoji/google/seven.png b/public/images/emoji/google/seven.png new file mode 100644 index 0000000000..b244dbc291 Binary files /dev/null and b/public/images/emoji/google/seven.png differ diff --git a/public/images/emoji/google/shaved_ice.png b/public/images/emoji/google/shaved_ice.png new file mode 100644 index 0000000000..0aecf2373a Binary files /dev/null and b/public/images/emoji/google/shaved_ice.png differ diff --git a/public/images/emoji/google/sheep.png b/public/images/emoji/google/sheep.png new file mode 100644 index 0000000000..af3e6fd9d4 Binary files /dev/null and b/public/images/emoji/google/sheep.png differ diff --git a/public/images/emoji/google/shell.png b/public/images/emoji/google/shell.png new file mode 100644 index 0000000000..4a253dce59 Binary files /dev/null and b/public/images/emoji/google/shell.png differ diff --git a/public/images/emoji/google/ship.png b/public/images/emoji/google/ship.png new file mode 100644 index 0000000000..9bbd3498f3 Binary files /dev/null and b/public/images/emoji/google/ship.png differ diff --git a/public/images/emoji/google/shirt.png b/public/images/emoji/google/shirt.png new file mode 100644 index 0000000000..cb0fd1dd08 Binary files /dev/null and b/public/images/emoji/google/shirt.png differ diff --git a/public/images/emoji/google/shit.png b/public/images/emoji/google/shit.png new file mode 100644 index 0000000000..61b946b0c3 Binary files /dev/null and b/public/images/emoji/google/shit.png differ diff --git a/public/images/emoji/google/shoe.png b/public/images/emoji/google/shoe.png new file mode 100644 index 0000000000..9c811a82bd Binary files /dev/null and b/public/images/emoji/google/shoe.png differ diff --git a/public/images/emoji/google/shower.png b/public/images/emoji/google/shower.png new file mode 100644 index 0000000000..339975ab73 Binary files /dev/null and b/public/images/emoji/google/shower.png differ diff --git a/public/images/emoji/google/signal_strength.png b/public/images/emoji/google/signal_strength.png new file mode 100644 index 0000000000..6b69fbc8cb Binary files /dev/null and b/public/images/emoji/google/signal_strength.png differ diff --git a/public/images/emoji/google/six.png b/public/images/emoji/google/six.png new file mode 100644 index 0000000000..08c6a28303 Binary files /dev/null and b/public/images/emoji/google/six.png differ diff --git a/public/images/emoji/google/six_pointed_star.png b/public/images/emoji/google/six_pointed_star.png new file mode 100644 index 0000000000..32e74c8f72 Binary files /dev/null and b/public/images/emoji/google/six_pointed_star.png differ diff --git a/public/images/emoji/google/ski.png b/public/images/emoji/google/ski.png new file mode 100644 index 0000000000..47d1fecc7c Binary files /dev/null and b/public/images/emoji/google/ski.png differ diff --git a/public/images/emoji/google/skull.png b/public/images/emoji/google/skull.png new file mode 100644 index 0000000000..120d72188f Binary files /dev/null and b/public/images/emoji/google/skull.png differ diff --git a/public/images/emoji/google/sleeping.png b/public/images/emoji/google/sleeping.png new file mode 100644 index 0000000000..c610c05e81 Binary files /dev/null and b/public/images/emoji/google/sleeping.png differ diff --git a/public/images/emoji/google/sleepy.png b/public/images/emoji/google/sleepy.png new file mode 100644 index 0000000000..3b85d64ad7 Binary files /dev/null and b/public/images/emoji/google/sleepy.png differ diff --git a/public/images/emoji/google/slot_machine.png b/public/images/emoji/google/slot_machine.png new file mode 100644 index 0000000000..7918b10a87 Binary files /dev/null and b/public/images/emoji/google/slot_machine.png differ diff --git a/public/images/emoji/google/small_blue_diamond.png b/public/images/emoji/google/small_blue_diamond.png new file mode 100644 index 0000000000..75aa555d39 Binary files /dev/null and b/public/images/emoji/google/small_blue_diamond.png differ diff --git a/public/images/emoji/google/small_orange_diamond.png b/public/images/emoji/google/small_orange_diamond.png new file mode 100644 index 0000000000..95a93da54e Binary files /dev/null and b/public/images/emoji/google/small_orange_diamond.png differ diff --git a/public/images/emoji/google/small_red_triangle.png b/public/images/emoji/google/small_red_triangle.png new file mode 100644 index 0000000000..b9b2819103 Binary files /dev/null and b/public/images/emoji/google/small_red_triangle.png differ diff --git a/public/images/emoji/google/small_red_triangle_down.png b/public/images/emoji/google/small_red_triangle_down.png new file mode 100644 index 0000000000..6c039f7f5c Binary files /dev/null and b/public/images/emoji/google/small_red_triangle_down.png differ diff --git a/public/images/emoji/google/smile.png b/public/images/emoji/google/smile.png new file mode 100644 index 0000000000..ba9476ed85 Binary files /dev/null and b/public/images/emoji/google/smile.png differ diff --git a/public/images/emoji/google/smile_cat.png b/public/images/emoji/google/smile_cat.png new file mode 100644 index 0000000000..8aff6c2364 Binary files /dev/null and b/public/images/emoji/google/smile_cat.png differ diff --git a/public/images/emoji/google/smiley.png b/public/images/emoji/google/smiley.png new file mode 100644 index 0000000000..68b715e626 Binary files /dev/null and b/public/images/emoji/google/smiley.png differ diff --git a/public/images/emoji/google/smiley_cat.png b/public/images/emoji/google/smiley_cat.png new file mode 100644 index 0000000000..e4a568cb27 Binary files /dev/null and b/public/images/emoji/google/smiley_cat.png differ diff --git a/public/images/emoji/google/smiling_imp.png b/public/images/emoji/google/smiling_imp.png new file mode 100644 index 0000000000..426ac8713f Binary files /dev/null and b/public/images/emoji/google/smiling_imp.png differ diff --git a/public/images/emoji/google/smirk.png b/public/images/emoji/google/smirk.png new file mode 100644 index 0000000000..f4eb5c8986 Binary files /dev/null and b/public/images/emoji/google/smirk.png differ diff --git a/public/images/emoji/google/smirk_cat.png b/public/images/emoji/google/smirk_cat.png new file mode 100644 index 0000000000..fd67cc8f30 Binary files /dev/null and b/public/images/emoji/google/smirk_cat.png differ diff --git a/public/images/emoji/google/smoking.png b/public/images/emoji/google/smoking.png new file mode 100644 index 0000000000..5a71169dcb Binary files /dev/null and b/public/images/emoji/google/smoking.png differ diff --git a/public/images/emoji/google/snail.png b/public/images/emoji/google/snail.png new file mode 100644 index 0000000000..a5b877b60d Binary files /dev/null and b/public/images/emoji/google/snail.png differ diff --git a/public/images/emoji/google/snake.png b/public/images/emoji/google/snake.png new file mode 100644 index 0000000000..91900d3f37 Binary files /dev/null and b/public/images/emoji/google/snake.png differ diff --git a/public/images/emoji/google/snowboarder.png b/public/images/emoji/google/snowboarder.png new file mode 100644 index 0000000000..087134fbec Binary files /dev/null and b/public/images/emoji/google/snowboarder.png differ diff --git a/public/images/emoji/google/snowflake.png b/public/images/emoji/google/snowflake.png new file mode 100644 index 0000000000..750a022686 Binary files /dev/null and b/public/images/emoji/google/snowflake.png differ diff --git a/public/images/emoji/google/snowman.png b/public/images/emoji/google/snowman.png new file mode 100644 index 0000000000..5ab294556d Binary files /dev/null and b/public/images/emoji/google/snowman.png differ diff --git a/public/images/emoji/google/sob.png b/public/images/emoji/google/sob.png new file mode 100644 index 0000000000..818f19f607 Binary files /dev/null and b/public/images/emoji/google/sob.png differ diff --git a/public/images/emoji/google/soccer.png b/public/images/emoji/google/soccer.png new file mode 100644 index 0000000000..70c8be0510 Binary files /dev/null and b/public/images/emoji/google/soccer.png differ diff --git a/public/images/emoji/google/soon.png b/public/images/emoji/google/soon.png new file mode 100644 index 0000000000..12ffedaa28 Binary files /dev/null and b/public/images/emoji/google/soon.png differ diff --git a/public/images/emoji/google/sos.png b/public/images/emoji/google/sos.png new file mode 100644 index 0000000000..14b149c44d Binary files /dev/null and b/public/images/emoji/google/sos.png differ diff --git a/public/images/emoji/google/sound.png b/public/images/emoji/google/sound.png new file mode 100644 index 0000000000..93c2660fab Binary files /dev/null and b/public/images/emoji/google/sound.png differ diff --git a/public/images/emoji/google/space_invader.png b/public/images/emoji/google/space_invader.png new file mode 100644 index 0000000000..76175a5575 Binary files /dev/null and b/public/images/emoji/google/space_invader.png differ diff --git a/public/images/emoji/google/spades.png b/public/images/emoji/google/spades.png new file mode 100644 index 0000000000..410d28af68 Binary files /dev/null and b/public/images/emoji/google/spades.png differ diff --git a/public/images/emoji/google/spaghetti.png b/public/images/emoji/google/spaghetti.png new file mode 100644 index 0000000000..5998902ccc Binary files /dev/null and b/public/images/emoji/google/spaghetti.png differ diff --git a/public/images/emoji/google/sparkle.png b/public/images/emoji/google/sparkle.png new file mode 100644 index 0000000000..f40a3af355 Binary files /dev/null and b/public/images/emoji/google/sparkle.png differ diff --git a/public/images/emoji/google/sparkler.png b/public/images/emoji/google/sparkler.png new file mode 100644 index 0000000000..b8fcaf8d8c Binary files /dev/null and b/public/images/emoji/google/sparkler.png differ diff --git a/public/images/emoji/google/sparkles.png b/public/images/emoji/google/sparkles.png new file mode 100644 index 0000000000..a62c4161e4 Binary files /dev/null and b/public/images/emoji/google/sparkles.png differ diff --git a/public/images/emoji/google/sparkling_heart.png b/public/images/emoji/google/sparkling_heart.png new file mode 100644 index 0000000000..e79f23170b Binary files /dev/null and b/public/images/emoji/google/sparkling_heart.png differ diff --git a/public/images/emoji/google/speak_no_evil.png b/public/images/emoji/google/speak_no_evil.png new file mode 100644 index 0000000000..7e3e1ec495 Binary files /dev/null and b/public/images/emoji/google/speak_no_evil.png differ diff --git a/public/images/emoji/google/speaker.png b/public/images/emoji/google/speaker.png new file mode 100644 index 0000000000..05c1af37a5 Binary files /dev/null and b/public/images/emoji/google/speaker.png differ diff --git a/public/images/emoji/google/speech_balloon.png b/public/images/emoji/google/speech_balloon.png new file mode 100644 index 0000000000..53e4d4ed84 Binary files /dev/null and b/public/images/emoji/google/speech_balloon.png differ diff --git a/public/images/emoji/google/speedboat.png b/public/images/emoji/google/speedboat.png new file mode 100644 index 0000000000..10f8200d23 Binary files /dev/null and b/public/images/emoji/google/speedboat.png differ diff --git a/public/images/emoji/google/star.png b/public/images/emoji/google/star.png new file mode 100644 index 0000000000..db351e9c01 Binary files /dev/null and b/public/images/emoji/google/star.png differ diff --git a/public/images/emoji/google/star2.png b/public/images/emoji/google/star2.png new file mode 100644 index 0000000000..b74aaae1d1 Binary files /dev/null and b/public/images/emoji/google/star2.png differ diff --git a/public/images/emoji/google/stars.png b/public/images/emoji/google/stars.png new file mode 100644 index 0000000000..13788ae007 Binary files /dev/null and b/public/images/emoji/google/stars.png differ diff --git a/public/images/emoji/google/station.png b/public/images/emoji/google/station.png new file mode 100644 index 0000000000..b0a0f2071f Binary files /dev/null and b/public/images/emoji/google/station.png differ diff --git a/public/images/emoji/google/statue_of_liberty.png b/public/images/emoji/google/statue_of_liberty.png new file mode 100644 index 0000000000..f87a30b672 Binary files /dev/null and b/public/images/emoji/google/statue_of_liberty.png differ diff --git a/public/images/emoji/google/steam_locomotive.png b/public/images/emoji/google/steam_locomotive.png new file mode 100644 index 0000000000..1d45d39f46 Binary files /dev/null and b/public/images/emoji/google/steam_locomotive.png differ diff --git a/public/images/emoji/google/stew.png b/public/images/emoji/google/stew.png new file mode 100644 index 0000000000..46e5a24668 Binary files /dev/null and b/public/images/emoji/google/stew.png differ diff --git a/public/images/emoji/google/straight_ruler.png b/public/images/emoji/google/straight_ruler.png new file mode 100644 index 0000000000..1c518f9fa6 Binary files /dev/null and b/public/images/emoji/google/straight_ruler.png differ diff --git a/public/images/emoji/google/strawberry.png b/public/images/emoji/google/strawberry.png new file mode 100644 index 0000000000..deec02d51c Binary files /dev/null and b/public/images/emoji/google/strawberry.png differ diff --git a/public/images/emoji/google/stuck_out_tongue.png b/public/images/emoji/google/stuck_out_tongue.png new file mode 100644 index 0000000000..905eedb300 Binary files /dev/null and b/public/images/emoji/google/stuck_out_tongue.png differ diff --git a/public/images/emoji/google/stuck_out_tongue_closed_eyes.png b/public/images/emoji/google/stuck_out_tongue_closed_eyes.png new file mode 100644 index 0000000000..66981bc17e Binary files /dev/null and b/public/images/emoji/google/stuck_out_tongue_closed_eyes.png differ diff --git a/public/images/emoji/google/stuck_out_tongue_winking_eye.png b/public/images/emoji/google/stuck_out_tongue_winking_eye.png new file mode 100644 index 0000000000..fb2667d364 Binary files /dev/null and b/public/images/emoji/google/stuck_out_tongue_winking_eye.png differ diff --git a/public/images/emoji/google/sun_with_face.png b/public/images/emoji/google/sun_with_face.png new file mode 100644 index 0000000000..047bf940e1 Binary files /dev/null and b/public/images/emoji/google/sun_with_face.png differ diff --git a/public/images/emoji/google/sunflower.png b/public/images/emoji/google/sunflower.png new file mode 100644 index 0000000000..3ae96689b3 Binary files /dev/null and b/public/images/emoji/google/sunflower.png differ diff --git a/public/images/emoji/google/sunglasses.png b/public/images/emoji/google/sunglasses.png new file mode 100644 index 0000000000..2603a2c9d0 Binary files /dev/null and b/public/images/emoji/google/sunglasses.png differ diff --git a/public/images/emoji/google/sunny.png b/public/images/emoji/google/sunny.png new file mode 100644 index 0000000000..7976bb50a7 Binary files /dev/null and b/public/images/emoji/google/sunny.png differ diff --git a/public/images/emoji/google/sunrise.png b/public/images/emoji/google/sunrise.png new file mode 100644 index 0000000000..d82cbf70c6 Binary files /dev/null and b/public/images/emoji/google/sunrise.png differ diff --git a/public/images/emoji/google/sunrise_over_mountains.png b/public/images/emoji/google/sunrise_over_mountains.png new file mode 100644 index 0000000000..fdfea703e5 Binary files /dev/null and b/public/images/emoji/google/sunrise_over_mountains.png differ diff --git a/public/images/emoji/google/surfer.png b/public/images/emoji/google/surfer.png new file mode 100644 index 0000000000..af5fbfb251 Binary files /dev/null and b/public/images/emoji/google/surfer.png differ diff --git a/public/images/emoji/google/sushi.png b/public/images/emoji/google/sushi.png new file mode 100644 index 0000000000..88a4f1b3ca Binary files /dev/null and b/public/images/emoji/google/sushi.png differ diff --git a/public/images/emoji/google/suspension_railway.png b/public/images/emoji/google/suspension_railway.png new file mode 100644 index 0000000000..b0abb8761d Binary files /dev/null and b/public/images/emoji/google/suspension_railway.png differ diff --git a/public/images/emoji/google/sweat.png b/public/images/emoji/google/sweat.png new file mode 100644 index 0000000000..f59c14b3d9 Binary files /dev/null and b/public/images/emoji/google/sweat.png differ diff --git a/public/images/emoji/google/sweat_drops.png b/public/images/emoji/google/sweat_drops.png new file mode 100644 index 0000000000..b711c631a5 Binary files /dev/null and b/public/images/emoji/google/sweat_drops.png differ diff --git a/public/images/emoji/google/sweat_smile.png b/public/images/emoji/google/sweat_smile.png new file mode 100644 index 0000000000..f75f054d97 Binary files /dev/null and b/public/images/emoji/google/sweat_smile.png differ diff --git a/public/images/emoji/google/sweet_potato.png b/public/images/emoji/google/sweet_potato.png new file mode 100644 index 0000000000..c55a85a2e3 Binary files /dev/null and b/public/images/emoji/google/sweet_potato.png differ diff --git a/public/images/emoji/google/swimmer.png b/public/images/emoji/google/swimmer.png new file mode 100644 index 0000000000..6c02ad0471 Binary files /dev/null and b/public/images/emoji/google/swimmer.png differ diff --git a/public/images/emoji/google/symbols.png b/public/images/emoji/google/symbols.png new file mode 100644 index 0000000000..1079a745e8 Binary files /dev/null and b/public/images/emoji/google/symbols.png differ diff --git a/public/images/emoji/google/syringe.png b/public/images/emoji/google/syringe.png new file mode 100644 index 0000000000..921249967e Binary files /dev/null and b/public/images/emoji/google/syringe.png differ diff --git a/public/images/emoji/google/tada.png b/public/images/emoji/google/tada.png new file mode 100644 index 0000000000..b2922e39c3 Binary files /dev/null and b/public/images/emoji/google/tada.png differ diff --git a/public/images/emoji/google/tanabata_tree.png b/public/images/emoji/google/tanabata_tree.png new file mode 100644 index 0000000000..9b7d115766 Binary files /dev/null and b/public/images/emoji/google/tanabata_tree.png differ diff --git a/public/images/emoji/google/tangerine.png b/public/images/emoji/google/tangerine.png new file mode 100644 index 0000000000..efdacb0598 Binary files /dev/null and b/public/images/emoji/google/tangerine.png differ diff --git a/public/images/emoji/google/taurus.png b/public/images/emoji/google/taurus.png new file mode 100644 index 0000000000..1c3f9cfc13 Binary files /dev/null and b/public/images/emoji/google/taurus.png differ diff --git a/public/images/emoji/google/taxi.png b/public/images/emoji/google/taxi.png new file mode 100644 index 0000000000..13c7dcace7 Binary files /dev/null and b/public/images/emoji/google/taxi.png differ diff --git a/public/images/emoji/google/tea.png b/public/images/emoji/google/tea.png new file mode 100644 index 0000000000..bd0cc6ac9f Binary files /dev/null and b/public/images/emoji/google/tea.png differ diff --git a/public/images/emoji/google/telephone.png b/public/images/emoji/google/telephone.png new file mode 100644 index 0000000000..a775ccf56f Binary files /dev/null and b/public/images/emoji/google/telephone.png differ diff --git a/public/images/emoji/google/telephone_receiver.png b/public/images/emoji/google/telephone_receiver.png new file mode 100644 index 0000000000..28d73fd594 Binary files /dev/null and b/public/images/emoji/google/telephone_receiver.png differ diff --git a/public/images/emoji/google/telescope.png b/public/images/emoji/google/telescope.png new file mode 100644 index 0000000000..dc453d9476 Binary files /dev/null and b/public/images/emoji/google/telescope.png differ diff --git a/public/images/emoji/google/tennis.png b/public/images/emoji/google/tennis.png new file mode 100644 index 0000000000..f97c711ddf Binary files /dev/null and b/public/images/emoji/google/tennis.png differ diff --git a/public/images/emoji/google/tent.png b/public/images/emoji/google/tent.png new file mode 100644 index 0000000000..c525883b1b Binary files /dev/null and b/public/images/emoji/google/tent.png differ diff --git a/public/images/emoji/google/thought_balloon.png b/public/images/emoji/google/thought_balloon.png new file mode 100644 index 0000000000..07d9bd70cc Binary files /dev/null and b/public/images/emoji/google/thought_balloon.png differ diff --git a/public/images/emoji/google/three.png b/public/images/emoji/google/three.png new file mode 100644 index 0000000000..fcab72179a Binary files /dev/null and b/public/images/emoji/google/three.png differ diff --git a/public/images/emoji/google/thumbsdown.png b/public/images/emoji/google/thumbsdown.png new file mode 100644 index 0000000000..bd9730bfa4 Binary files /dev/null and b/public/images/emoji/google/thumbsdown.png differ diff --git a/public/images/emoji/google/thumbsup.png b/public/images/emoji/google/thumbsup.png new file mode 100644 index 0000000000..b997ff3e9f Binary files /dev/null and b/public/images/emoji/google/thumbsup.png differ diff --git a/public/images/emoji/google/ticket.png b/public/images/emoji/google/ticket.png new file mode 100644 index 0000000000..318adbc2c8 Binary files /dev/null and b/public/images/emoji/google/ticket.png differ diff --git a/public/images/emoji/google/tiger.png b/public/images/emoji/google/tiger.png new file mode 100644 index 0000000000..64d0947765 Binary files /dev/null and b/public/images/emoji/google/tiger.png differ diff --git a/public/images/emoji/google/tiger2.png b/public/images/emoji/google/tiger2.png new file mode 100644 index 0000000000..d7aaff011f Binary files /dev/null and b/public/images/emoji/google/tiger2.png differ diff --git a/public/images/emoji/google/tired_face.png b/public/images/emoji/google/tired_face.png new file mode 100644 index 0000000000..bbd28b4c9e Binary files /dev/null and b/public/images/emoji/google/tired_face.png differ diff --git a/public/images/emoji/google/tm.png b/public/images/emoji/google/tm.png new file mode 100644 index 0000000000..4aba4b57d4 Binary files /dev/null and b/public/images/emoji/google/tm.png differ diff --git a/public/images/emoji/google/toilet.png b/public/images/emoji/google/toilet.png new file mode 100644 index 0000000000..fd3782b5b8 Binary files /dev/null and b/public/images/emoji/google/toilet.png differ diff --git a/public/images/emoji/google/tokyo_tower.png b/public/images/emoji/google/tokyo_tower.png new file mode 100644 index 0000000000..28b1c2e5e9 Binary files /dev/null and b/public/images/emoji/google/tokyo_tower.png differ diff --git a/public/images/emoji/google/tomato.png b/public/images/emoji/google/tomato.png new file mode 100644 index 0000000000..70d6fc7aac Binary files /dev/null and b/public/images/emoji/google/tomato.png differ diff --git a/public/images/emoji/google/tongue.png b/public/images/emoji/google/tongue.png new file mode 100644 index 0000000000..e97d3bbb55 Binary files /dev/null and b/public/images/emoji/google/tongue.png differ diff --git a/public/images/emoji/google/top.png b/public/images/emoji/google/top.png new file mode 100644 index 0000000000..d2797c0d25 Binary files /dev/null and b/public/images/emoji/google/top.png differ diff --git a/public/images/emoji/google/tophat.png b/public/images/emoji/google/tophat.png new file mode 100644 index 0000000000..208ae3a3f4 Binary files /dev/null and b/public/images/emoji/google/tophat.png differ diff --git a/public/images/emoji/google/tractor.png b/public/images/emoji/google/tractor.png new file mode 100644 index 0000000000..d343d8126b Binary files /dev/null and b/public/images/emoji/google/tractor.png differ diff --git a/public/images/emoji/google/traffic_light.png b/public/images/emoji/google/traffic_light.png new file mode 100644 index 0000000000..52a22092c0 Binary files /dev/null and b/public/images/emoji/google/traffic_light.png differ diff --git a/public/images/emoji/google/train.png b/public/images/emoji/google/train.png new file mode 100644 index 0000000000..3e81171455 Binary files /dev/null and b/public/images/emoji/google/train.png differ diff --git a/public/images/emoji/google/train2.png b/public/images/emoji/google/train2.png new file mode 100644 index 0000000000..3bacea89f3 Binary files /dev/null and b/public/images/emoji/google/train2.png differ diff --git a/public/images/emoji/google/tram.png b/public/images/emoji/google/tram.png new file mode 100644 index 0000000000..318a68d7d7 Binary files /dev/null and b/public/images/emoji/google/tram.png differ diff --git a/public/images/emoji/google/triangular_flag_on_post.png b/public/images/emoji/google/triangular_flag_on_post.png new file mode 100644 index 0000000000..5c76e1f5a2 Binary files /dev/null and b/public/images/emoji/google/triangular_flag_on_post.png differ diff --git a/public/images/emoji/google/triangular_ruler.png b/public/images/emoji/google/triangular_ruler.png new file mode 100644 index 0000000000..813b4025d8 Binary files /dev/null and b/public/images/emoji/google/triangular_ruler.png differ diff --git a/public/images/emoji/google/trident.png b/public/images/emoji/google/trident.png new file mode 100644 index 0000000000..48a35be55f Binary files /dev/null and b/public/images/emoji/google/trident.png differ diff --git a/public/images/emoji/google/triumph.png b/public/images/emoji/google/triumph.png new file mode 100644 index 0000000000..985c466483 Binary files /dev/null and b/public/images/emoji/google/triumph.png differ diff --git a/public/images/emoji/google/trolleybus.png b/public/images/emoji/google/trolleybus.png new file mode 100644 index 0000000000..686482badd Binary files /dev/null and b/public/images/emoji/google/trolleybus.png differ diff --git a/public/images/emoji/google/trophy.png b/public/images/emoji/google/trophy.png new file mode 100644 index 0000000000..1e09a20fec Binary files /dev/null and b/public/images/emoji/google/trophy.png differ diff --git a/public/images/emoji/google/tropical_drink.png b/public/images/emoji/google/tropical_drink.png new file mode 100644 index 0000000000..37a51dad67 Binary files /dev/null and b/public/images/emoji/google/tropical_drink.png differ diff --git a/public/images/emoji/google/tropical_fish.png b/public/images/emoji/google/tropical_fish.png new file mode 100644 index 0000000000..e86eb1c246 Binary files /dev/null and b/public/images/emoji/google/tropical_fish.png differ diff --git a/public/images/emoji/google/truck.png b/public/images/emoji/google/truck.png new file mode 100644 index 0000000000..a765f53a00 Binary files /dev/null and b/public/images/emoji/google/truck.png differ diff --git a/public/images/emoji/google/trumpet.png b/public/images/emoji/google/trumpet.png new file mode 100644 index 0000000000..a424006195 Binary files /dev/null and b/public/images/emoji/google/trumpet.png differ diff --git a/public/images/emoji/google/tshirt.png b/public/images/emoji/google/tshirt.png new file mode 100644 index 0000000000..004c3ae918 Binary files /dev/null and b/public/images/emoji/google/tshirt.png differ diff --git a/public/images/emoji/google/tulip.png b/public/images/emoji/google/tulip.png new file mode 100644 index 0000000000..b51c03e4d3 Binary files /dev/null and b/public/images/emoji/google/tulip.png differ diff --git a/public/images/emoji/google/turtle.png b/public/images/emoji/google/turtle.png new file mode 100644 index 0000000000..1ccf443ed3 Binary files /dev/null and b/public/images/emoji/google/turtle.png differ diff --git a/public/images/emoji/google/tv.png b/public/images/emoji/google/tv.png new file mode 100644 index 0000000000..288f167885 Binary files /dev/null and b/public/images/emoji/google/tv.png differ diff --git a/public/images/emoji/google/twisted_rightwards_arrows.png b/public/images/emoji/google/twisted_rightwards_arrows.png new file mode 100644 index 0000000000..e1ea7c6f8c Binary files /dev/null and b/public/images/emoji/google/twisted_rightwards_arrows.png differ diff --git a/public/images/emoji/google/two.png b/public/images/emoji/google/two.png new file mode 100644 index 0000000000..dbe81f63eb Binary files /dev/null and b/public/images/emoji/google/two.png differ diff --git a/public/images/emoji/google/two_hearts.png b/public/images/emoji/google/two_hearts.png new file mode 100644 index 0000000000..b636d6508d Binary files /dev/null and b/public/images/emoji/google/two_hearts.png differ diff --git a/public/images/emoji/google/two_men_holding_hands.png b/public/images/emoji/google/two_men_holding_hands.png new file mode 100644 index 0000000000..efc1c64943 Binary files /dev/null and b/public/images/emoji/google/two_men_holding_hands.png differ diff --git a/public/images/emoji/google/two_women_holding_hands.png b/public/images/emoji/google/two_women_holding_hands.png new file mode 100644 index 0000000000..1866b86b40 Binary files /dev/null and b/public/images/emoji/google/two_women_holding_hands.png differ diff --git a/public/images/emoji/google/u5272.png b/public/images/emoji/google/u5272.png new file mode 100644 index 0000000000..9c1c17efc2 Binary files /dev/null and b/public/images/emoji/google/u5272.png differ diff --git a/public/images/emoji/google/u5408.png b/public/images/emoji/google/u5408.png new file mode 100644 index 0000000000..5efaab77f9 Binary files /dev/null and b/public/images/emoji/google/u5408.png differ diff --git a/public/images/emoji/google/u55b6.png b/public/images/emoji/google/u55b6.png new file mode 100644 index 0000000000..313eb5666d Binary files /dev/null and b/public/images/emoji/google/u55b6.png differ diff --git a/public/images/emoji/google/u6307.png b/public/images/emoji/google/u6307.png new file mode 100644 index 0000000000..3a2e409d5c Binary files /dev/null and b/public/images/emoji/google/u6307.png differ diff --git a/public/images/emoji/google/u6708.png b/public/images/emoji/google/u6708.png new file mode 100644 index 0000000000..aa1d978bcd Binary files /dev/null and b/public/images/emoji/google/u6708.png differ diff --git a/public/images/emoji/google/u6709.png b/public/images/emoji/google/u6709.png new file mode 100644 index 0000000000..09d14b8490 Binary files /dev/null and b/public/images/emoji/google/u6709.png differ diff --git a/public/images/emoji/google/u6e80.png b/public/images/emoji/google/u6e80.png new file mode 100644 index 0000000000..48f03773c4 Binary files /dev/null and b/public/images/emoji/google/u6e80.png differ diff --git a/public/images/emoji/google/u7121.png b/public/images/emoji/google/u7121.png new file mode 100644 index 0000000000..6ffa619f43 Binary files /dev/null and b/public/images/emoji/google/u7121.png differ diff --git a/public/images/emoji/google/u7533.png b/public/images/emoji/google/u7533.png new file mode 100644 index 0000000000..7b0fec5a3c Binary files /dev/null and b/public/images/emoji/google/u7533.png differ diff --git a/public/images/emoji/google/u7981.png b/public/images/emoji/google/u7981.png new file mode 100644 index 0000000000..54bd719c37 Binary files /dev/null and b/public/images/emoji/google/u7981.png differ diff --git a/public/images/emoji/google/u7a7a.png b/public/images/emoji/google/u7a7a.png new file mode 100644 index 0000000000..e816813090 Binary files /dev/null and b/public/images/emoji/google/u7a7a.png differ diff --git a/public/images/emoji/google/uk.png b/public/images/emoji/google/uk.png new file mode 100644 index 0000000000..94d5fe9e1b Binary files /dev/null and b/public/images/emoji/google/uk.png differ diff --git a/public/images/emoji/google/umbrella.png b/public/images/emoji/google/umbrella.png new file mode 100644 index 0000000000..cd669cbc20 Binary files /dev/null and b/public/images/emoji/google/umbrella.png differ diff --git a/public/images/emoji/google/unamused.png b/public/images/emoji/google/unamused.png new file mode 100644 index 0000000000..f29c569cfc Binary files /dev/null and b/public/images/emoji/google/unamused.png differ diff --git a/public/images/emoji/google/underage.png b/public/images/emoji/google/underage.png new file mode 100644 index 0000000000..c826df2096 Binary files /dev/null and b/public/images/emoji/google/underage.png differ diff --git a/public/images/emoji/google/unlock.png b/public/images/emoji/google/unlock.png new file mode 100644 index 0000000000..bbc707999c Binary files /dev/null and b/public/images/emoji/google/unlock.png differ diff --git a/public/images/emoji/google/up.png b/public/images/emoji/google/up.png new file mode 100644 index 0000000000..3626d6b8a4 Binary files /dev/null and b/public/images/emoji/google/up.png differ diff --git a/public/images/emoji/google/us.png b/public/images/emoji/google/us.png new file mode 100644 index 0000000000..ab1c5d28de Binary files /dev/null and b/public/images/emoji/google/us.png differ diff --git a/public/images/emoji/google/v.png b/public/images/emoji/google/v.png new file mode 100644 index 0000000000..7bcb204771 Binary files /dev/null and b/public/images/emoji/google/v.png differ diff --git a/public/images/emoji/google/vertical_traffic_light.png b/public/images/emoji/google/vertical_traffic_light.png new file mode 100644 index 0000000000..08f87fb681 Binary files /dev/null and b/public/images/emoji/google/vertical_traffic_light.png differ diff --git a/public/images/emoji/google/vhs.png b/public/images/emoji/google/vhs.png new file mode 100644 index 0000000000..85f80e4aa5 Binary files /dev/null and b/public/images/emoji/google/vhs.png differ diff --git a/public/images/emoji/google/vibration_mode.png b/public/images/emoji/google/vibration_mode.png new file mode 100644 index 0000000000..e5eb51ddd0 Binary files /dev/null and b/public/images/emoji/google/vibration_mode.png differ diff --git a/public/images/emoji/google/video_camera.png b/public/images/emoji/google/video_camera.png new file mode 100644 index 0000000000..133e4f9de2 Binary files /dev/null and b/public/images/emoji/google/video_camera.png differ diff --git a/public/images/emoji/google/video_game.png b/public/images/emoji/google/video_game.png new file mode 100644 index 0000000000..66697ce495 Binary files /dev/null and b/public/images/emoji/google/video_game.png differ diff --git a/public/images/emoji/google/violin.png b/public/images/emoji/google/violin.png new file mode 100644 index 0000000000..d1e3f9bc5a Binary files /dev/null and b/public/images/emoji/google/violin.png differ diff --git a/public/images/emoji/google/virgo.png b/public/images/emoji/google/virgo.png new file mode 100644 index 0000000000..2e24c061fe Binary files /dev/null and b/public/images/emoji/google/virgo.png differ diff --git a/public/images/emoji/google/volcano.png b/public/images/emoji/google/volcano.png new file mode 100644 index 0000000000..f8f97c90ee Binary files /dev/null and b/public/images/emoji/google/volcano.png differ diff --git a/public/images/emoji/google/vs.png b/public/images/emoji/google/vs.png new file mode 100644 index 0000000000..977c7db3ec Binary files /dev/null and b/public/images/emoji/google/vs.png differ diff --git a/public/images/emoji/google/walking.png b/public/images/emoji/google/walking.png new file mode 100644 index 0000000000..7dc8388449 Binary files /dev/null and b/public/images/emoji/google/walking.png differ diff --git a/public/images/emoji/google/waning_crescent_moon.png b/public/images/emoji/google/waning_crescent_moon.png new file mode 100644 index 0000000000..87472993a2 Binary files /dev/null and b/public/images/emoji/google/waning_crescent_moon.png differ diff --git a/public/images/emoji/google/waning_gibbous_moon.png b/public/images/emoji/google/waning_gibbous_moon.png new file mode 100644 index 0000000000..07724889d2 Binary files /dev/null and b/public/images/emoji/google/waning_gibbous_moon.png differ diff --git a/public/images/emoji/google/warning.png b/public/images/emoji/google/warning.png new file mode 100644 index 0000000000..15043c5a4a Binary files /dev/null and b/public/images/emoji/google/warning.png differ diff --git a/public/images/emoji/google/watch.png b/public/images/emoji/google/watch.png new file mode 100644 index 0000000000..86dc4fc3d8 Binary files /dev/null and b/public/images/emoji/google/watch.png differ diff --git a/public/images/emoji/google/water_buffalo.png b/public/images/emoji/google/water_buffalo.png new file mode 100644 index 0000000000..d89bc7129a Binary files /dev/null and b/public/images/emoji/google/water_buffalo.png differ diff --git a/public/images/emoji/google/watermelon.png b/public/images/emoji/google/watermelon.png new file mode 100644 index 0000000000..47d520261b Binary files /dev/null and b/public/images/emoji/google/watermelon.png differ diff --git a/public/images/emoji/google/wave.png b/public/images/emoji/google/wave.png new file mode 100644 index 0000000000..63d03f7ae1 Binary files /dev/null and b/public/images/emoji/google/wave.png differ diff --git a/public/images/emoji/google/wavy_dash.png b/public/images/emoji/google/wavy_dash.png new file mode 100644 index 0000000000..ea0f2a1ecf Binary files /dev/null and b/public/images/emoji/google/wavy_dash.png differ diff --git a/public/images/emoji/google/waxing_crescent_moon.png b/public/images/emoji/google/waxing_crescent_moon.png new file mode 100644 index 0000000000..76c0069294 Binary files /dev/null and b/public/images/emoji/google/waxing_crescent_moon.png differ diff --git a/public/images/emoji/google/waxing_gibbous_moon.png b/public/images/emoji/google/waxing_gibbous_moon.png new file mode 100644 index 0000000000..a8a36e3a98 Binary files /dev/null and b/public/images/emoji/google/waxing_gibbous_moon.png differ diff --git a/public/images/emoji/google/wc.png b/public/images/emoji/google/wc.png new file mode 100644 index 0000000000..65f034fd48 Binary files /dev/null and b/public/images/emoji/google/wc.png differ diff --git a/public/images/emoji/google/weary.png b/public/images/emoji/google/weary.png new file mode 100644 index 0000000000..57d4aee336 Binary files /dev/null and b/public/images/emoji/google/weary.png differ diff --git a/public/images/emoji/google/wedding.png b/public/images/emoji/google/wedding.png new file mode 100644 index 0000000000..4946a36a0a Binary files /dev/null and b/public/images/emoji/google/wedding.png differ diff --git a/public/images/emoji/google/whale.png b/public/images/emoji/google/whale.png new file mode 100644 index 0000000000..919a70ccd6 Binary files /dev/null and b/public/images/emoji/google/whale.png differ diff --git a/public/images/emoji/google/whale2.png b/public/images/emoji/google/whale2.png new file mode 100644 index 0000000000..ee0aa3bda6 Binary files /dev/null and b/public/images/emoji/google/whale2.png differ diff --git a/public/images/emoji/google/wheelchair.png b/public/images/emoji/google/wheelchair.png new file mode 100644 index 0000000000..c8623f5c26 Binary files /dev/null and b/public/images/emoji/google/wheelchair.png differ diff --git a/public/images/emoji/google/white_check_mark.png b/public/images/emoji/google/white_check_mark.png new file mode 100644 index 0000000000..1b27de2f9a Binary files /dev/null and b/public/images/emoji/google/white_check_mark.png differ diff --git a/public/images/emoji/google/white_circle.png b/public/images/emoji/google/white_circle.png new file mode 100644 index 0000000000..3542330385 Binary files /dev/null and b/public/images/emoji/google/white_circle.png differ diff --git a/public/images/emoji/google/white_flower.png b/public/images/emoji/google/white_flower.png new file mode 100644 index 0000000000..dc86563299 Binary files /dev/null and b/public/images/emoji/google/white_flower.png differ diff --git a/public/images/emoji/google/white_large_square.png b/public/images/emoji/google/white_large_square.png new file mode 100644 index 0000000000..3b74c3e35d Binary files /dev/null and b/public/images/emoji/google/white_large_square.png differ diff --git a/public/images/emoji/google/white_medium_small_square.png b/public/images/emoji/google/white_medium_small_square.png new file mode 100644 index 0000000000..452d997fc2 Binary files /dev/null and b/public/images/emoji/google/white_medium_small_square.png differ diff --git a/public/images/emoji/google/white_medium_square.png b/public/images/emoji/google/white_medium_square.png new file mode 100644 index 0000000000..a90b30d217 Binary files /dev/null and b/public/images/emoji/google/white_medium_square.png differ diff --git a/public/images/emoji/google/white_small_square.png b/public/images/emoji/google/white_small_square.png new file mode 100644 index 0000000000..570280afe5 Binary files /dev/null and b/public/images/emoji/google/white_small_square.png differ diff --git a/public/images/emoji/google/white_square_button.png b/public/images/emoji/google/white_square_button.png new file mode 100644 index 0000000000..e01000cbe3 Binary files /dev/null and b/public/images/emoji/google/white_square_button.png differ diff --git a/public/images/emoji/google/wind_chime.png b/public/images/emoji/google/wind_chime.png new file mode 100644 index 0000000000..bfc5ffce79 Binary files /dev/null and b/public/images/emoji/google/wind_chime.png differ diff --git a/public/images/emoji/google/wine_glass.png b/public/images/emoji/google/wine_glass.png new file mode 100644 index 0000000000..7ae47dae80 Binary files /dev/null and b/public/images/emoji/google/wine_glass.png differ diff --git a/public/images/emoji/google/wink.png b/public/images/emoji/google/wink.png new file mode 100644 index 0000000000..fa82c48e21 Binary files /dev/null and b/public/images/emoji/google/wink.png differ diff --git a/public/images/emoji/google/wolf.png b/public/images/emoji/google/wolf.png new file mode 100644 index 0000000000..aeac39b121 Binary files /dev/null and b/public/images/emoji/google/wolf.png differ diff --git a/public/images/emoji/google/woman.png b/public/images/emoji/google/woman.png new file mode 100644 index 0000000000..bcfb1d2423 Binary files /dev/null and b/public/images/emoji/google/woman.png differ diff --git a/public/images/emoji/google/womans_clothes.png b/public/images/emoji/google/womans_clothes.png new file mode 100644 index 0000000000..eff3724530 Binary files /dev/null and b/public/images/emoji/google/womans_clothes.png differ diff --git a/public/images/emoji/google/womans_hat.png b/public/images/emoji/google/womans_hat.png new file mode 100644 index 0000000000..a80039dcc5 Binary files /dev/null and b/public/images/emoji/google/womans_hat.png differ diff --git a/public/images/emoji/google/womens.png b/public/images/emoji/google/womens.png new file mode 100644 index 0000000000..15977627b6 Binary files /dev/null and b/public/images/emoji/google/womens.png differ diff --git a/public/images/emoji/google/worried.png b/public/images/emoji/google/worried.png new file mode 100644 index 0000000000..2335681672 Binary files /dev/null and b/public/images/emoji/google/worried.png differ diff --git a/public/images/emoji/google/wrench.png b/public/images/emoji/google/wrench.png new file mode 100644 index 0000000000..7933aa8d6a Binary files /dev/null and b/public/images/emoji/google/wrench.png differ diff --git a/public/images/emoji/google/x.png b/public/images/emoji/google/x.png new file mode 100644 index 0000000000..9532e64fb7 Binary files /dev/null and b/public/images/emoji/google/x.png differ diff --git a/public/images/emoji/google/yellow_heart.png b/public/images/emoji/google/yellow_heart.png new file mode 100644 index 0000000000..b108aeb4b7 Binary files /dev/null and b/public/images/emoji/google/yellow_heart.png differ diff --git a/public/images/emoji/google/yen.png b/public/images/emoji/google/yen.png new file mode 100644 index 0000000000..1164cb561c Binary files /dev/null and b/public/images/emoji/google/yen.png differ diff --git a/public/images/emoji/google/yum.png b/public/images/emoji/google/yum.png new file mode 100644 index 0000000000..11e71c662e Binary files /dev/null and b/public/images/emoji/google/yum.png differ diff --git a/public/images/emoji/google/zap.png b/public/images/emoji/google/zap.png new file mode 100644 index 0000000000..b180e1c6f8 Binary files /dev/null and b/public/images/emoji/google/zap.png differ diff --git a/public/images/emoji/google/zero.png b/public/images/emoji/google/zero.png new file mode 100644 index 0000000000..e1f59f64ea Binary files /dev/null and b/public/images/emoji/google/zero.png differ diff --git a/public/images/emoji/google/zzz.png b/public/images/emoji/google/zzz.png new file mode 100644 index 0000000000..bbb8726591 Binary files /dev/null and b/public/images/emoji/google/zzz.png differ diff --git a/public/images/emoji/twitter/+1.png b/public/images/emoji/twitter/+1.png new file mode 100644 index 0000000000..f4bafe3cc1 Binary files /dev/null and b/public/images/emoji/twitter/+1.png differ diff --git a/public/images/emoji/twitter/-1.png b/public/images/emoji/twitter/-1.png new file mode 100644 index 0000000000..892f5cf1ee Binary files /dev/null and b/public/images/emoji/twitter/-1.png differ diff --git a/public/images/emoji/twitter/100.png b/public/images/emoji/twitter/100.png new file mode 100644 index 0000000000..7a9b91830f Binary files /dev/null and b/public/images/emoji/twitter/100.png differ diff --git a/public/images/emoji/twitter/1234.png b/public/images/emoji/twitter/1234.png new file mode 100644 index 0000000000..076d4526e7 Binary files /dev/null and b/public/images/emoji/twitter/1234.png differ diff --git a/public/images/emoji/twitter/8ball.png b/public/images/emoji/twitter/8ball.png new file mode 100644 index 0000000000..8f53cd375e Binary files /dev/null and b/public/images/emoji/twitter/8ball.png differ diff --git a/public/images/emoji/twitter/a.png b/public/images/emoji/twitter/a.png new file mode 100644 index 0000000000..4910a18289 Binary files /dev/null and b/public/images/emoji/twitter/a.png differ diff --git a/public/images/emoji/twitter/ab.png b/public/images/emoji/twitter/ab.png new file mode 100644 index 0000000000..629ccf6410 Binary files /dev/null and b/public/images/emoji/twitter/ab.png differ diff --git a/public/images/emoji/twitter/abc.png b/public/images/emoji/twitter/abc.png new file mode 100644 index 0000000000..54907e5029 Binary files /dev/null and b/public/images/emoji/twitter/abc.png differ diff --git a/public/images/emoji/twitter/abcd.png b/public/images/emoji/twitter/abcd.png new file mode 100644 index 0000000000..9d867149d7 Binary files /dev/null and b/public/images/emoji/twitter/abcd.png differ diff --git a/public/images/emoji/twitter/accept.png b/public/images/emoji/twitter/accept.png new file mode 100644 index 0000000000..90a727b1e1 Binary files /dev/null and b/public/images/emoji/twitter/accept.png differ diff --git a/public/images/emoji/twitter/aerial_tramway.png b/public/images/emoji/twitter/aerial_tramway.png new file mode 100644 index 0000000000..40d2b1c91f Binary files /dev/null and b/public/images/emoji/twitter/aerial_tramway.png differ diff --git a/public/images/emoji/twitter/airplane.png b/public/images/emoji/twitter/airplane.png new file mode 100644 index 0000000000..5d2406ab3c Binary files /dev/null and b/public/images/emoji/twitter/airplane.png differ diff --git a/public/images/emoji/twitter/alarm_clock.png b/public/images/emoji/twitter/alarm_clock.png new file mode 100644 index 0000000000..214b12ccd8 Binary files /dev/null and b/public/images/emoji/twitter/alarm_clock.png differ diff --git a/public/images/emoji/twitter/alien.png b/public/images/emoji/twitter/alien.png new file mode 100644 index 0000000000..704ba0a078 Binary files /dev/null and b/public/images/emoji/twitter/alien.png differ diff --git a/public/images/emoji/twitter/ambulance.png b/public/images/emoji/twitter/ambulance.png new file mode 100644 index 0000000000..2c912f0370 Binary files /dev/null and b/public/images/emoji/twitter/ambulance.png differ diff --git a/public/images/emoji/twitter/anchor.png b/public/images/emoji/twitter/anchor.png new file mode 100644 index 0000000000..a64e24ed27 Binary files /dev/null and b/public/images/emoji/twitter/anchor.png differ diff --git a/public/images/emoji/twitter/angel.png b/public/images/emoji/twitter/angel.png new file mode 100644 index 0000000000..cdcec5f51a Binary files /dev/null and b/public/images/emoji/twitter/angel.png differ diff --git a/public/images/emoji/twitter/anger.png b/public/images/emoji/twitter/anger.png new file mode 100644 index 0000000000..14f5a6d970 Binary files /dev/null and b/public/images/emoji/twitter/anger.png differ diff --git a/public/images/emoji/twitter/angry.png b/public/images/emoji/twitter/angry.png new file mode 100644 index 0000000000..6c69d9b0d1 Binary files /dev/null and b/public/images/emoji/twitter/angry.png differ diff --git a/public/images/emoji/twitter/anguished.png b/public/images/emoji/twitter/anguished.png new file mode 100644 index 0000000000..70219ed597 Binary files /dev/null and b/public/images/emoji/twitter/anguished.png differ diff --git a/public/images/emoji/twitter/ant.png b/public/images/emoji/twitter/ant.png new file mode 100644 index 0000000000..74c43359fe Binary files /dev/null and b/public/images/emoji/twitter/ant.png differ diff --git a/public/images/emoji/twitter/apple.png b/public/images/emoji/twitter/apple.png new file mode 100644 index 0000000000..f1c6758569 Binary files /dev/null and b/public/images/emoji/twitter/apple.png differ diff --git a/public/images/emoji/twitter/aquarius.png b/public/images/emoji/twitter/aquarius.png new file mode 100644 index 0000000000..ccf4bccc66 Binary files /dev/null and b/public/images/emoji/twitter/aquarius.png differ diff --git a/public/images/emoji/twitter/aries.png b/public/images/emoji/twitter/aries.png new file mode 100644 index 0000000000..53c1b5cb92 Binary files /dev/null and b/public/images/emoji/twitter/aries.png differ diff --git a/public/images/emoji/twitter/arrow_backward.png b/public/images/emoji/twitter/arrow_backward.png new file mode 100644 index 0000000000..52cfb55fe8 Binary files /dev/null and b/public/images/emoji/twitter/arrow_backward.png differ diff --git a/public/images/emoji/twitter/arrow_double_down.png b/public/images/emoji/twitter/arrow_double_down.png new file mode 100644 index 0000000000..a206660489 Binary files /dev/null and b/public/images/emoji/twitter/arrow_double_down.png differ diff --git a/public/images/emoji/twitter/arrow_double_up.png b/public/images/emoji/twitter/arrow_double_up.png new file mode 100644 index 0000000000..a8eaf8886d Binary files /dev/null and b/public/images/emoji/twitter/arrow_double_up.png differ diff --git a/public/images/emoji/twitter/arrow_down.png b/public/images/emoji/twitter/arrow_down.png new file mode 100644 index 0000000000..c8fce77f66 Binary files /dev/null and b/public/images/emoji/twitter/arrow_down.png differ diff --git a/public/images/emoji/twitter/arrow_down_small.png b/public/images/emoji/twitter/arrow_down_small.png new file mode 100644 index 0000000000..ca171752a0 Binary files /dev/null and b/public/images/emoji/twitter/arrow_down_small.png differ diff --git a/public/images/emoji/twitter/arrow_forward.png b/public/images/emoji/twitter/arrow_forward.png new file mode 100644 index 0000000000..318fc58f36 Binary files /dev/null and b/public/images/emoji/twitter/arrow_forward.png differ diff --git a/public/images/emoji/twitter/arrow_heading_down.png b/public/images/emoji/twitter/arrow_heading_down.png new file mode 100644 index 0000000000..3cc4353e1a Binary files /dev/null and b/public/images/emoji/twitter/arrow_heading_down.png differ diff --git a/public/images/emoji/twitter/arrow_heading_up.png b/public/images/emoji/twitter/arrow_heading_up.png new file mode 100644 index 0000000000..1980877413 Binary files /dev/null and b/public/images/emoji/twitter/arrow_heading_up.png differ diff --git a/public/images/emoji/twitter/arrow_left.png b/public/images/emoji/twitter/arrow_left.png new file mode 100644 index 0000000000..163b53501b Binary files /dev/null and b/public/images/emoji/twitter/arrow_left.png differ diff --git a/public/images/emoji/twitter/arrow_lower_left.png b/public/images/emoji/twitter/arrow_lower_left.png new file mode 100644 index 0000000000..30b784363d Binary files /dev/null and b/public/images/emoji/twitter/arrow_lower_left.png differ diff --git a/public/images/emoji/twitter/arrow_lower_right.png b/public/images/emoji/twitter/arrow_lower_right.png new file mode 100644 index 0000000000..75d2b8ef92 Binary files /dev/null and b/public/images/emoji/twitter/arrow_lower_right.png differ diff --git a/public/images/emoji/twitter/arrow_right.png b/public/images/emoji/twitter/arrow_right.png new file mode 100644 index 0000000000..8c13e19853 Binary files /dev/null and b/public/images/emoji/twitter/arrow_right.png differ diff --git a/public/images/emoji/twitter/arrow_right_hook.png b/public/images/emoji/twitter/arrow_right_hook.png new file mode 100644 index 0000000000..586e0d48b7 Binary files /dev/null and b/public/images/emoji/twitter/arrow_right_hook.png differ diff --git a/public/images/emoji/twitter/arrow_up.png b/public/images/emoji/twitter/arrow_up.png new file mode 100644 index 0000000000..561899cb6e Binary files /dev/null and b/public/images/emoji/twitter/arrow_up.png differ diff --git a/public/images/emoji/twitter/arrow_up_down.png b/public/images/emoji/twitter/arrow_up_down.png new file mode 100644 index 0000000000..72622939e0 Binary files /dev/null and b/public/images/emoji/twitter/arrow_up_down.png differ diff --git a/public/images/emoji/twitter/arrow_up_small.png b/public/images/emoji/twitter/arrow_up_small.png new file mode 100644 index 0000000000..878ffcfc22 Binary files /dev/null and b/public/images/emoji/twitter/arrow_up_small.png differ diff --git a/public/images/emoji/twitter/arrow_upper_left.png b/public/images/emoji/twitter/arrow_upper_left.png new file mode 100644 index 0000000000..88a8c8c65c Binary files /dev/null and b/public/images/emoji/twitter/arrow_upper_left.png differ diff --git a/public/images/emoji/twitter/arrow_upper_right.png b/public/images/emoji/twitter/arrow_upper_right.png new file mode 100644 index 0000000000..422fe6c47c Binary files /dev/null and b/public/images/emoji/twitter/arrow_upper_right.png differ diff --git a/public/images/emoji/twitter/arrows_clockwise.png b/public/images/emoji/twitter/arrows_clockwise.png new file mode 100644 index 0000000000..79bb1681ac Binary files /dev/null and b/public/images/emoji/twitter/arrows_clockwise.png differ diff --git a/public/images/emoji/twitter/arrows_counterclockwise.png b/public/images/emoji/twitter/arrows_counterclockwise.png new file mode 100644 index 0000000000..3ebc731519 Binary files /dev/null and b/public/images/emoji/twitter/arrows_counterclockwise.png differ diff --git a/public/images/emoji/twitter/art.png b/public/images/emoji/twitter/art.png new file mode 100644 index 0000000000..c22a9f342b Binary files /dev/null and b/public/images/emoji/twitter/art.png differ diff --git a/public/images/emoji/twitter/articulated_lorry.png b/public/images/emoji/twitter/articulated_lorry.png new file mode 100644 index 0000000000..ebc5d3fd7e Binary files /dev/null and b/public/images/emoji/twitter/articulated_lorry.png differ diff --git a/public/images/emoji/twitter/astonished.png b/public/images/emoji/twitter/astonished.png new file mode 100644 index 0000000000..273454344c Binary files /dev/null and b/public/images/emoji/twitter/astonished.png differ diff --git a/public/images/emoji/twitter/athletic_shoe.png b/public/images/emoji/twitter/athletic_shoe.png new file mode 100644 index 0000000000..302effc614 Binary files /dev/null and b/public/images/emoji/twitter/athletic_shoe.png differ diff --git a/public/images/emoji/twitter/atm.png b/public/images/emoji/twitter/atm.png new file mode 100644 index 0000000000..02517ef06c Binary files /dev/null and b/public/images/emoji/twitter/atm.png differ diff --git a/public/images/emoji/twitter/b.png b/public/images/emoji/twitter/b.png new file mode 100644 index 0000000000..9ba297423c Binary files /dev/null and b/public/images/emoji/twitter/b.png differ diff --git a/public/images/emoji/twitter/baby.png b/public/images/emoji/twitter/baby.png new file mode 100644 index 0000000000..6508b64e5b Binary files /dev/null and b/public/images/emoji/twitter/baby.png differ diff --git a/public/images/emoji/twitter/baby_bottle.png b/public/images/emoji/twitter/baby_bottle.png new file mode 100644 index 0000000000..1650232730 Binary files /dev/null and b/public/images/emoji/twitter/baby_bottle.png differ diff --git a/public/images/emoji/twitter/baby_chick.png b/public/images/emoji/twitter/baby_chick.png new file mode 100644 index 0000000000..4e27df5424 Binary files /dev/null and b/public/images/emoji/twitter/baby_chick.png differ diff --git a/public/images/emoji/twitter/baby_symbol.png b/public/images/emoji/twitter/baby_symbol.png new file mode 100644 index 0000000000..e9e43ccfec Binary files /dev/null and b/public/images/emoji/twitter/baby_symbol.png differ diff --git a/public/images/emoji/twitter/back.png b/public/images/emoji/twitter/back.png new file mode 100644 index 0000000000..06d2d85547 Binary files /dev/null and b/public/images/emoji/twitter/back.png differ diff --git a/public/images/emoji/twitter/baggage_claim.png b/public/images/emoji/twitter/baggage_claim.png new file mode 100644 index 0000000000..f83a918a69 Binary files /dev/null and b/public/images/emoji/twitter/baggage_claim.png differ diff --git a/public/images/emoji/twitter/balloon.png b/public/images/emoji/twitter/balloon.png new file mode 100644 index 0000000000..bb152dc6d7 Binary files /dev/null and b/public/images/emoji/twitter/balloon.png differ diff --git a/public/images/emoji/twitter/ballot_box_with_check.png b/public/images/emoji/twitter/ballot_box_with_check.png new file mode 100644 index 0000000000..202beb96e1 Binary files /dev/null and b/public/images/emoji/twitter/ballot_box_with_check.png differ diff --git a/public/images/emoji/twitter/bamboo.png b/public/images/emoji/twitter/bamboo.png new file mode 100644 index 0000000000..17595aca58 Binary files /dev/null and b/public/images/emoji/twitter/bamboo.png differ diff --git a/public/images/emoji/twitter/banana.png b/public/images/emoji/twitter/banana.png new file mode 100644 index 0000000000..d007ebaff5 Binary files /dev/null and b/public/images/emoji/twitter/banana.png differ diff --git a/public/images/emoji/twitter/bangbang.png b/public/images/emoji/twitter/bangbang.png new file mode 100644 index 0000000000..b7c7c51827 Binary files /dev/null and b/public/images/emoji/twitter/bangbang.png differ diff --git a/public/images/emoji/twitter/bank.png b/public/images/emoji/twitter/bank.png new file mode 100644 index 0000000000..6bc054d19b Binary files /dev/null and b/public/images/emoji/twitter/bank.png differ diff --git a/public/images/emoji/twitter/bar_chart.png b/public/images/emoji/twitter/bar_chart.png new file mode 100644 index 0000000000..335ba0f3c5 Binary files /dev/null and b/public/images/emoji/twitter/bar_chart.png differ diff --git a/public/images/emoji/twitter/barber.png b/public/images/emoji/twitter/barber.png new file mode 100644 index 0000000000..19f432af95 Binary files /dev/null and b/public/images/emoji/twitter/barber.png differ diff --git a/public/images/emoji/twitter/baseball.png b/public/images/emoji/twitter/baseball.png new file mode 100644 index 0000000000..5dbda2836d Binary files /dev/null and b/public/images/emoji/twitter/baseball.png differ diff --git a/public/images/emoji/twitter/basketball.png b/public/images/emoji/twitter/basketball.png new file mode 100644 index 0000000000..20e25daebf Binary files /dev/null and b/public/images/emoji/twitter/basketball.png differ diff --git a/public/images/emoji/twitter/bath.png b/public/images/emoji/twitter/bath.png new file mode 100644 index 0000000000..a7dc17b723 Binary files /dev/null and b/public/images/emoji/twitter/bath.png differ diff --git a/public/images/emoji/twitter/bathtub.png b/public/images/emoji/twitter/bathtub.png new file mode 100644 index 0000000000..0580a6d24b Binary files /dev/null and b/public/images/emoji/twitter/bathtub.png differ diff --git a/public/images/emoji/twitter/battery.png b/public/images/emoji/twitter/battery.png new file mode 100644 index 0000000000..4baada5260 Binary files /dev/null and b/public/images/emoji/twitter/battery.png differ diff --git a/public/images/emoji/twitter/bear.png b/public/images/emoji/twitter/bear.png new file mode 100644 index 0000000000..e3321e290f Binary files /dev/null and b/public/images/emoji/twitter/bear.png differ diff --git a/public/images/emoji/twitter/bee.png b/public/images/emoji/twitter/bee.png new file mode 100644 index 0000000000..5e73fc3687 Binary files /dev/null and b/public/images/emoji/twitter/bee.png differ diff --git a/public/images/emoji/twitter/beer.png b/public/images/emoji/twitter/beer.png new file mode 100644 index 0000000000..3c3e944260 Binary files /dev/null and b/public/images/emoji/twitter/beer.png differ diff --git a/public/images/emoji/twitter/beers.png b/public/images/emoji/twitter/beers.png new file mode 100644 index 0000000000..723b1cc5c6 Binary files /dev/null and b/public/images/emoji/twitter/beers.png differ diff --git a/public/images/emoji/twitter/beetle.png b/public/images/emoji/twitter/beetle.png new file mode 100644 index 0000000000..35c8d0d864 Binary files /dev/null and b/public/images/emoji/twitter/beetle.png differ diff --git a/public/images/emoji/twitter/beginner.png b/public/images/emoji/twitter/beginner.png new file mode 100644 index 0000000000..412af52ada Binary files /dev/null and b/public/images/emoji/twitter/beginner.png differ diff --git a/public/images/emoji/twitter/bell.png b/public/images/emoji/twitter/bell.png new file mode 100644 index 0000000000..08483cc3c3 Binary files /dev/null and b/public/images/emoji/twitter/bell.png differ diff --git a/public/images/emoji/twitter/bento.png b/public/images/emoji/twitter/bento.png new file mode 100644 index 0000000000..b36a703ba9 Binary files /dev/null and b/public/images/emoji/twitter/bento.png differ diff --git a/public/images/emoji/twitter/bicyclist.png b/public/images/emoji/twitter/bicyclist.png new file mode 100644 index 0000000000..80800d3da6 Binary files /dev/null and b/public/images/emoji/twitter/bicyclist.png differ diff --git a/public/images/emoji/twitter/bike.png b/public/images/emoji/twitter/bike.png new file mode 100644 index 0000000000..7128411a50 Binary files /dev/null and b/public/images/emoji/twitter/bike.png differ diff --git a/public/images/emoji/twitter/bikini.png b/public/images/emoji/twitter/bikini.png new file mode 100644 index 0000000000..79c08d1d71 Binary files /dev/null and b/public/images/emoji/twitter/bikini.png differ diff --git a/public/images/emoji/twitter/bird.png b/public/images/emoji/twitter/bird.png new file mode 100644 index 0000000000..c73fa88a78 Binary files /dev/null and b/public/images/emoji/twitter/bird.png differ diff --git a/public/images/emoji/twitter/birthday.png b/public/images/emoji/twitter/birthday.png new file mode 100644 index 0000000000..961239034f Binary files /dev/null and b/public/images/emoji/twitter/birthday.png differ diff --git a/public/images/emoji/twitter/black_circle.png b/public/images/emoji/twitter/black_circle.png new file mode 100644 index 0000000000..c763980938 Binary files /dev/null and b/public/images/emoji/twitter/black_circle.png differ diff --git a/public/images/emoji/twitter/black_joker.png b/public/images/emoji/twitter/black_joker.png new file mode 100644 index 0000000000..57c236aa49 Binary files /dev/null and b/public/images/emoji/twitter/black_joker.png differ diff --git a/public/images/emoji/twitter/black_large_square.png b/public/images/emoji/twitter/black_large_square.png new file mode 100644 index 0000000000..6465bde614 Binary files /dev/null and b/public/images/emoji/twitter/black_large_square.png differ diff --git a/public/images/emoji/twitter/black_medium_small_square.png b/public/images/emoji/twitter/black_medium_small_square.png new file mode 100644 index 0000000000..0955daf448 Binary files /dev/null and b/public/images/emoji/twitter/black_medium_small_square.png differ diff --git a/public/images/emoji/twitter/black_medium_square.png b/public/images/emoji/twitter/black_medium_square.png new file mode 100644 index 0000000000..5ce46828ec Binary files /dev/null and b/public/images/emoji/twitter/black_medium_square.png differ diff --git a/public/images/emoji/twitter/black_nib.png b/public/images/emoji/twitter/black_nib.png new file mode 100644 index 0000000000..66b2c663d2 Binary files /dev/null and b/public/images/emoji/twitter/black_nib.png differ diff --git a/public/images/emoji/twitter/black_small_square.png b/public/images/emoji/twitter/black_small_square.png new file mode 100644 index 0000000000..203ce77c1a Binary files /dev/null and b/public/images/emoji/twitter/black_small_square.png differ diff --git a/public/images/emoji/twitter/black_square_button.png b/public/images/emoji/twitter/black_square_button.png new file mode 100644 index 0000000000..0e7f6888ea Binary files /dev/null and b/public/images/emoji/twitter/black_square_button.png differ diff --git a/public/images/emoji/twitter/blossom.png b/public/images/emoji/twitter/blossom.png new file mode 100644 index 0000000000..5bc24065c6 Binary files /dev/null and b/public/images/emoji/twitter/blossom.png differ diff --git a/public/images/emoji/twitter/blowfish.png b/public/images/emoji/twitter/blowfish.png new file mode 100644 index 0000000000..d77f9f52a3 Binary files /dev/null and b/public/images/emoji/twitter/blowfish.png differ diff --git a/public/images/emoji/twitter/blue_book.png b/public/images/emoji/twitter/blue_book.png new file mode 100644 index 0000000000..367697f718 Binary files /dev/null and b/public/images/emoji/twitter/blue_book.png differ diff --git a/public/images/emoji/twitter/blue_car.png b/public/images/emoji/twitter/blue_car.png new file mode 100644 index 0000000000..f8320178a1 Binary files /dev/null and b/public/images/emoji/twitter/blue_car.png differ diff --git a/public/images/emoji/twitter/blue_heart.png b/public/images/emoji/twitter/blue_heart.png new file mode 100644 index 0000000000..f58f9bc137 Binary files /dev/null and b/public/images/emoji/twitter/blue_heart.png differ diff --git a/public/images/emoji/twitter/blush.png b/public/images/emoji/twitter/blush.png new file mode 100644 index 0000000000..6e7bb4be4c Binary files /dev/null and b/public/images/emoji/twitter/blush.png differ diff --git a/public/images/emoji/twitter/boar.png b/public/images/emoji/twitter/boar.png new file mode 100644 index 0000000000..fe9ceb0a62 Binary files /dev/null and b/public/images/emoji/twitter/boar.png differ diff --git a/public/images/emoji/twitter/boat.png b/public/images/emoji/twitter/boat.png new file mode 100644 index 0000000000..7b4a289ed2 Binary files /dev/null and b/public/images/emoji/twitter/boat.png differ diff --git a/public/images/emoji/twitter/bomb.png b/public/images/emoji/twitter/bomb.png new file mode 100644 index 0000000000..4c833e9753 Binary files /dev/null and b/public/images/emoji/twitter/bomb.png differ diff --git a/public/images/emoji/twitter/book.png b/public/images/emoji/twitter/book.png new file mode 100644 index 0000000000..e62882acb6 Binary files /dev/null and b/public/images/emoji/twitter/book.png differ diff --git a/public/images/emoji/twitter/bookmark.png b/public/images/emoji/twitter/bookmark.png new file mode 100644 index 0000000000..19a9fff2e6 Binary files /dev/null and b/public/images/emoji/twitter/bookmark.png differ diff --git a/public/images/emoji/twitter/bookmark_tabs.png b/public/images/emoji/twitter/bookmark_tabs.png new file mode 100644 index 0000000000..0c8e6848bd Binary files /dev/null and b/public/images/emoji/twitter/bookmark_tabs.png differ diff --git a/public/images/emoji/twitter/books.png b/public/images/emoji/twitter/books.png new file mode 100644 index 0000000000..d987bf6f87 Binary files /dev/null and b/public/images/emoji/twitter/books.png differ diff --git a/public/images/emoji/twitter/boom.png b/public/images/emoji/twitter/boom.png new file mode 100644 index 0000000000..a7f1e2a5eb Binary files /dev/null and b/public/images/emoji/twitter/boom.png differ diff --git a/public/images/emoji/twitter/boot.png b/public/images/emoji/twitter/boot.png new file mode 100644 index 0000000000..f2e89fcfdd Binary files /dev/null and b/public/images/emoji/twitter/boot.png differ diff --git a/public/images/emoji/twitter/bouquet.png b/public/images/emoji/twitter/bouquet.png new file mode 100644 index 0000000000..14d6645c7d Binary files /dev/null and b/public/images/emoji/twitter/bouquet.png differ diff --git a/public/images/emoji/twitter/bow.png b/public/images/emoji/twitter/bow.png new file mode 100644 index 0000000000..44739d36b1 Binary files /dev/null and b/public/images/emoji/twitter/bow.png differ diff --git a/public/images/emoji/twitter/bowling.png b/public/images/emoji/twitter/bowling.png new file mode 100644 index 0000000000..19e52cf0db Binary files /dev/null and b/public/images/emoji/twitter/bowling.png differ diff --git a/public/images/emoji/twitter/boy.png b/public/images/emoji/twitter/boy.png new file mode 100644 index 0000000000..ebe4770d04 Binary files /dev/null and b/public/images/emoji/twitter/boy.png differ diff --git a/public/images/emoji/twitter/bread.png b/public/images/emoji/twitter/bread.png new file mode 100644 index 0000000000..0e5c0a70a5 Binary files /dev/null and b/public/images/emoji/twitter/bread.png differ diff --git a/public/images/emoji/twitter/bride_with_veil.png b/public/images/emoji/twitter/bride_with_veil.png new file mode 100644 index 0000000000..cf753cdb8a Binary files /dev/null and b/public/images/emoji/twitter/bride_with_veil.png differ diff --git a/public/images/emoji/twitter/bridge_at_night.png b/public/images/emoji/twitter/bridge_at_night.png new file mode 100644 index 0000000000..48fc34293f Binary files /dev/null and b/public/images/emoji/twitter/bridge_at_night.png differ diff --git a/public/images/emoji/twitter/briefcase.png b/public/images/emoji/twitter/briefcase.png new file mode 100644 index 0000000000..be6b0777a0 Binary files /dev/null and b/public/images/emoji/twitter/briefcase.png differ diff --git a/public/images/emoji/twitter/broken_heart.png b/public/images/emoji/twitter/broken_heart.png new file mode 100644 index 0000000000..9666f04844 Binary files /dev/null and b/public/images/emoji/twitter/broken_heart.png differ diff --git a/public/images/emoji/twitter/bug.png b/public/images/emoji/twitter/bug.png new file mode 100644 index 0000000000..f37a34af0b Binary files /dev/null and b/public/images/emoji/twitter/bug.png differ diff --git a/public/images/emoji/twitter/bulb.png b/public/images/emoji/twitter/bulb.png new file mode 100644 index 0000000000..993543ec2f Binary files /dev/null and b/public/images/emoji/twitter/bulb.png differ diff --git a/public/images/emoji/twitter/bullettrain_front.png b/public/images/emoji/twitter/bullettrain_front.png new file mode 100644 index 0000000000..0ddc6ac8bc Binary files /dev/null and b/public/images/emoji/twitter/bullettrain_front.png differ diff --git a/public/images/emoji/twitter/bullettrain_side.png b/public/images/emoji/twitter/bullettrain_side.png new file mode 100644 index 0000000000..6b8be83f3d Binary files /dev/null and b/public/images/emoji/twitter/bullettrain_side.png differ diff --git a/public/images/emoji/twitter/bus.png b/public/images/emoji/twitter/bus.png new file mode 100644 index 0000000000..49789c073e Binary files /dev/null and b/public/images/emoji/twitter/bus.png differ diff --git a/public/images/emoji/twitter/busstop.png b/public/images/emoji/twitter/busstop.png new file mode 100644 index 0000000000..6bc1f9fd94 Binary files /dev/null and b/public/images/emoji/twitter/busstop.png differ diff --git a/public/images/emoji/twitter/bust_in_silhouette.png b/public/images/emoji/twitter/bust_in_silhouette.png new file mode 100644 index 0000000000..07d1fb4eef Binary files /dev/null and b/public/images/emoji/twitter/bust_in_silhouette.png differ diff --git a/public/images/emoji/twitter/busts_in_silhouette.png b/public/images/emoji/twitter/busts_in_silhouette.png new file mode 100644 index 0000000000..15f824b9ec Binary files /dev/null and b/public/images/emoji/twitter/busts_in_silhouette.png differ diff --git a/public/images/emoji/twitter/cactus.png b/public/images/emoji/twitter/cactus.png new file mode 100644 index 0000000000..54f83949ee Binary files /dev/null and b/public/images/emoji/twitter/cactus.png differ diff --git a/public/images/emoji/twitter/cake.png b/public/images/emoji/twitter/cake.png new file mode 100644 index 0000000000..d81f9abb5a Binary files /dev/null and b/public/images/emoji/twitter/cake.png differ diff --git a/public/images/emoji/twitter/calendar.png b/public/images/emoji/twitter/calendar.png new file mode 100644 index 0000000000..185e25a564 Binary files /dev/null and b/public/images/emoji/twitter/calendar.png differ diff --git a/public/images/emoji/twitter/calling.png b/public/images/emoji/twitter/calling.png new file mode 100644 index 0000000000..41d26d1b3f Binary files /dev/null and b/public/images/emoji/twitter/calling.png differ diff --git a/public/images/emoji/twitter/camel.png b/public/images/emoji/twitter/camel.png new file mode 100644 index 0000000000..6417a70074 Binary files /dev/null and b/public/images/emoji/twitter/camel.png differ diff --git a/public/images/emoji/twitter/camera.png b/public/images/emoji/twitter/camera.png new file mode 100644 index 0000000000..4cf79f3090 Binary files /dev/null and b/public/images/emoji/twitter/camera.png differ diff --git a/public/images/emoji/twitter/cancer.png b/public/images/emoji/twitter/cancer.png new file mode 100644 index 0000000000..3d1420a398 Binary files /dev/null and b/public/images/emoji/twitter/cancer.png differ diff --git a/public/images/emoji/twitter/candy.png b/public/images/emoji/twitter/candy.png new file mode 100644 index 0000000000..2d1d3378d0 Binary files /dev/null and b/public/images/emoji/twitter/candy.png differ diff --git a/public/images/emoji/twitter/capital_abcd.png b/public/images/emoji/twitter/capital_abcd.png new file mode 100644 index 0000000000..d5e843d70c Binary files /dev/null and b/public/images/emoji/twitter/capital_abcd.png differ diff --git a/public/images/emoji/twitter/capricorn.png b/public/images/emoji/twitter/capricorn.png new file mode 100644 index 0000000000..c3458d236f Binary files /dev/null and b/public/images/emoji/twitter/capricorn.png differ diff --git a/public/images/emoji/twitter/car.png b/public/images/emoji/twitter/car.png new file mode 100644 index 0000000000..93610d2c43 Binary files /dev/null and b/public/images/emoji/twitter/car.png differ diff --git a/public/images/emoji/twitter/card_index.png b/public/images/emoji/twitter/card_index.png new file mode 100644 index 0000000000..6621717911 Binary files /dev/null and b/public/images/emoji/twitter/card_index.png differ diff --git a/public/images/emoji/twitter/carousel_horse.png b/public/images/emoji/twitter/carousel_horse.png new file mode 100644 index 0000000000..df4c93d9b8 Binary files /dev/null and b/public/images/emoji/twitter/carousel_horse.png differ diff --git a/public/images/emoji/twitter/cat.png b/public/images/emoji/twitter/cat.png new file mode 100644 index 0000000000..131059b491 Binary files /dev/null and b/public/images/emoji/twitter/cat.png differ diff --git a/public/images/emoji/twitter/cat2.png b/public/images/emoji/twitter/cat2.png new file mode 100644 index 0000000000..9d2b681d7e Binary files /dev/null and b/public/images/emoji/twitter/cat2.png differ diff --git a/public/images/emoji/twitter/cd.png b/public/images/emoji/twitter/cd.png new file mode 100644 index 0000000000..29f3a72e78 Binary files /dev/null and b/public/images/emoji/twitter/cd.png differ diff --git a/public/images/emoji/twitter/chart.png b/public/images/emoji/twitter/chart.png new file mode 100644 index 0000000000..d45ccad87d Binary files /dev/null and b/public/images/emoji/twitter/chart.png differ diff --git a/public/images/emoji/twitter/chart_with_downwards_trend.png b/public/images/emoji/twitter/chart_with_downwards_trend.png new file mode 100644 index 0000000000..c15b13dcfb Binary files /dev/null and b/public/images/emoji/twitter/chart_with_downwards_trend.png differ diff --git a/public/images/emoji/twitter/chart_with_upwards_trend.png b/public/images/emoji/twitter/chart_with_upwards_trend.png new file mode 100644 index 0000000000..27eec28511 Binary files /dev/null and b/public/images/emoji/twitter/chart_with_upwards_trend.png differ diff --git a/public/images/emoji/twitter/checkered_flag.png b/public/images/emoji/twitter/checkered_flag.png new file mode 100644 index 0000000000..e9f90e3ffd Binary files /dev/null and b/public/images/emoji/twitter/checkered_flag.png differ diff --git a/public/images/emoji/twitter/cherries.png b/public/images/emoji/twitter/cherries.png new file mode 100644 index 0000000000..7ef46a065d Binary files /dev/null and b/public/images/emoji/twitter/cherries.png differ diff --git a/public/images/emoji/twitter/cherry_blossom.png b/public/images/emoji/twitter/cherry_blossom.png new file mode 100644 index 0000000000..423f93d75c Binary files /dev/null and b/public/images/emoji/twitter/cherry_blossom.png differ diff --git a/public/images/emoji/twitter/chestnut.png b/public/images/emoji/twitter/chestnut.png new file mode 100644 index 0000000000..bc6f3189d4 Binary files /dev/null and b/public/images/emoji/twitter/chestnut.png differ diff --git a/public/images/emoji/twitter/chicken.png b/public/images/emoji/twitter/chicken.png new file mode 100644 index 0000000000..8a11e60698 Binary files /dev/null and b/public/images/emoji/twitter/chicken.png differ diff --git a/public/images/emoji/twitter/children_crossing.png b/public/images/emoji/twitter/children_crossing.png new file mode 100644 index 0000000000..585f42852e Binary files /dev/null and b/public/images/emoji/twitter/children_crossing.png differ diff --git a/public/images/emoji/twitter/chocolate_bar.png b/public/images/emoji/twitter/chocolate_bar.png new file mode 100644 index 0000000000..1339a7f370 Binary files /dev/null and b/public/images/emoji/twitter/chocolate_bar.png differ diff --git a/public/images/emoji/twitter/christmas_tree.png b/public/images/emoji/twitter/christmas_tree.png new file mode 100644 index 0000000000..c8329d1eb0 Binary files /dev/null and b/public/images/emoji/twitter/christmas_tree.png differ diff --git a/public/images/emoji/twitter/church.png b/public/images/emoji/twitter/church.png new file mode 100644 index 0000000000..89cbc84b25 Binary files /dev/null and b/public/images/emoji/twitter/church.png differ diff --git a/public/images/emoji/twitter/cinema.png b/public/images/emoji/twitter/cinema.png new file mode 100644 index 0000000000..186f11e007 Binary files /dev/null and b/public/images/emoji/twitter/cinema.png differ diff --git a/public/images/emoji/twitter/circus_tent.png b/public/images/emoji/twitter/circus_tent.png new file mode 100644 index 0000000000..362b8be730 Binary files /dev/null and b/public/images/emoji/twitter/circus_tent.png differ diff --git a/public/images/emoji/twitter/city_sunrise.png b/public/images/emoji/twitter/city_sunrise.png new file mode 100644 index 0000000000..5bf1a5a85d Binary files /dev/null and b/public/images/emoji/twitter/city_sunrise.png differ diff --git a/public/images/emoji/twitter/city_sunset.png b/public/images/emoji/twitter/city_sunset.png new file mode 100644 index 0000000000..087f9b4eec Binary files /dev/null and b/public/images/emoji/twitter/city_sunset.png differ diff --git a/public/images/emoji/twitter/cl.png b/public/images/emoji/twitter/cl.png new file mode 100644 index 0000000000..c80ff2c21b Binary files /dev/null and b/public/images/emoji/twitter/cl.png differ diff --git a/public/images/emoji/twitter/clap.png b/public/images/emoji/twitter/clap.png new file mode 100644 index 0000000000..5683995f1b Binary files /dev/null and b/public/images/emoji/twitter/clap.png differ diff --git a/public/images/emoji/twitter/clapper.png b/public/images/emoji/twitter/clapper.png new file mode 100644 index 0000000000..68b61925a6 Binary files /dev/null and b/public/images/emoji/twitter/clapper.png differ diff --git a/public/images/emoji/twitter/clipboard.png b/public/images/emoji/twitter/clipboard.png new file mode 100644 index 0000000000..b04e37a5f2 Binary files /dev/null and b/public/images/emoji/twitter/clipboard.png differ diff --git a/public/images/emoji/twitter/clock1.png b/public/images/emoji/twitter/clock1.png new file mode 100644 index 0000000000..766f09a00f Binary files /dev/null and b/public/images/emoji/twitter/clock1.png differ diff --git a/public/images/emoji/twitter/clock10.png b/public/images/emoji/twitter/clock10.png new file mode 100644 index 0000000000..6bb7462931 Binary files /dev/null and b/public/images/emoji/twitter/clock10.png differ diff --git a/public/images/emoji/twitter/clock1030.png b/public/images/emoji/twitter/clock1030.png new file mode 100644 index 0000000000..5894b04e67 Binary files /dev/null and b/public/images/emoji/twitter/clock1030.png differ diff --git a/public/images/emoji/twitter/clock11.png b/public/images/emoji/twitter/clock11.png new file mode 100644 index 0000000000..eb17f8ceb7 Binary files /dev/null and b/public/images/emoji/twitter/clock11.png differ diff --git a/public/images/emoji/twitter/clock1130.png b/public/images/emoji/twitter/clock1130.png new file mode 100644 index 0000000000..946959ae5a Binary files /dev/null and b/public/images/emoji/twitter/clock1130.png differ diff --git a/public/images/emoji/twitter/clock12.png b/public/images/emoji/twitter/clock12.png new file mode 100644 index 0000000000..3eedd9f589 Binary files /dev/null and b/public/images/emoji/twitter/clock12.png differ diff --git a/public/images/emoji/twitter/clock1230.png b/public/images/emoji/twitter/clock1230.png new file mode 100644 index 0000000000..8fa3fc2d4c Binary files /dev/null and b/public/images/emoji/twitter/clock1230.png differ diff --git a/public/images/emoji/twitter/clock130.png b/public/images/emoji/twitter/clock130.png new file mode 100644 index 0000000000..fadb2d700a Binary files /dev/null and b/public/images/emoji/twitter/clock130.png differ diff --git a/public/images/emoji/twitter/clock2.png b/public/images/emoji/twitter/clock2.png new file mode 100644 index 0000000000..28b580392b Binary files /dev/null and b/public/images/emoji/twitter/clock2.png differ diff --git a/public/images/emoji/twitter/clock230.png b/public/images/emoji/twitter/clock230.png new file mode 100644 index 0000000000..2ea515b453 Binary files /dev/null and b/public/images/emoji/twitter/clock230.png differ diff --git a/public/images/emoji/twitter/clock3.png b/public/images/emoji/twitter/clock3.png new file mode 100644 index 0000000000..b6838e5d9a Binary files /dev/null and b/public/images/emoji/twitter/clock3.png differ diff --git a/public/images/emoji/twitter/clock330.png b/public/images/emoji/twitter/clock330.png new file mode 100644 index 0000000000..5dd7454268 Binary files /dev/null and b/public/images/emoji/twitter/clock330.png differ diff --git a/public/images/emoji/twitter/clock4.png b/public/images/emoji/twitter/clock4.png new file mode 100644 index 0000000000..40acf8c04d Binary files /dev/null and b/public/images/emoji/twitter/clock4.png differ diff --git a/public/images/emoji/twitter/clock430.png b/public/images/emoji/twitter/clock430.png new file mode 100644 index 0000000000..db6765d4bc Binary files /dev/null and b/public/images/emoji/twitter/clock430.png differ diff --git a/public/images/emoji/twitter/clock5.png b/public/images/emoji/twitter/clock5.png new file mode 100644 index 0000000000..43b9718de0 Binary files /dev/null and b/public/images/emoji/twitter/clock5.png differ diff --git a/public/images/emoji/twitter/clock530.png b/public/images/emoji/twitter/clock530.png new file mode 100644 index 0000000000..f7a5b76d3a Binary files /dev/null and b/public/images/emoji/twitter/clock530.png differ diff --git a/public/images/emoji/twitter/clock6.png b/public/images/emoji/twitter/clock6.png new file mode 100644 index 0000000000..c3c1fa85ba Binary files /dev/null and b/public/images/emoji/twitter/clock6.png differ diff --git a/public/images/emoji/twitter/clock630.png b/public/images/emoji/twitter/clock630.png new file mode 100644 index 0000000000..48c5d0a3c8 Binary files /dev/null and b/public/images/emoji/twitter/clock630.png differ diff --git a/public/images/emoji/twitter/clock7.png b/public/images/emoji/twitter/clock7.png new file mode 100644 index 0000000000..5f04fcda12 Binary files /dev/null and b/public/images/emoji/twitter/clock7.png differ diff --git a/public/images/emoji/twitter/clock730.png b/public/images/emoji/twitter/clock730.png new file mode 100644 index 0000000000..dad3fd39c8 Binary files /dev/null and b/public/images/emoji/twitter/clock730.png differ diff --git a/public/images/emoji/twitter/clock8.png b/public/images/emoji/twitter/clock8.png new file mode 100644 index 0000000000..446da51fc9 Binary files /dev/null and b/public/images/emoji/twitter/clock8.png differ diff --git a/public/images/emoji/twitter/clock830.png b/public/images/emoji/twitter/clock830.png new file mode 100644 index 0000000000..5ae1273f3c Binary files /dev/null and b/public/images/emoji/twitter/clock830.png differ diff --git a/public/images/emoji/twitter/clock9.png b/public/images/emoji/twitter/clock9.png new file mode 100644 index 0000000000..c5ff5261ec Binary files /dev/null and b/public/images/emoji/twitter/clock9.png differ diff --git a/public/images/emoji/twitter/clock930.png b/public/images/emoji/twitter/clock930.png new file mode 100644 index 0000000000..2c726282a2 Binary files /dev/null and b/public/images/emoji/twitter/clock930.png differ diff --git a/public/images/emoji/twitter/closed_book.png b/public/images/emoji/twitter/closed_book.png new file mode 100644 index 0000000000..8436c5ff83 Binary files /dev/null and b/public/images/emoji/twitter/closed_book.png differ diff --git a/public/images/emoji/twitter/closed_lock_with_key.png b/public/images/emoji/twitter/closed_lock_with_key.png new file mode 100644 index 0000000000..5bd299e00e Binary files /dev/null and b/public/images/emoji/twitter/closed_lock_with_key.png differ diff --git a/public/images/emoji/twitter/closed_umbrella.png b/public/images/emoji/twitter/closed_umbrella.png new file mode 100644 index 0000000000..6baf7e2199 Binary files /dev/null and b/public/images/emoji/twitter/closed_umbrella.png differ diff --git a/public/images/emoji/twitter/cloud.png b/public/images/emoji/twitter/cloud.png new file mode 100644 index 0000000000..8cd5827eb4 Binary files /dev/null and b/public/images/emoji/twitter/cloud.png differ diff --git a/public/images/emoji/twitter/clubs.png b/public/images/emoji/twitter/clubs.png new file mode 100644 index 0000000000..a6ca677262 Binary files /dev/null and b/public/images/emoji/twitter/clubs.png differ diff --git a/public/images/emoji/twitter/cn.png b/public/images/emoji/twitter/cn.png new file mode 100644 index 0000000000..de68ae2a23 Binary files /dev/null and b/public/images/emoji/twitter/cn.png differ diff --git a/public/images/emoji/twitter/cocktail.png b/public/images/emoji/twitter/cocktail.png new file mode 100644 index 0000000000..6cd28f2e6f Binary files /dev/null and b/public/images/emoji/twitter/cocktail.png differ diff --git a/public/images/emoji/twitter/coffee.png b/public/images/emoji/twitter/coffee.png new file mode 100644 index 0000000000..831a18d2dc Binary files /dev/null and b/public/images/emoji/twitter/coffee.png differ diff --git a/public/images/emoji/twitter/cold_sweat.png b/public/images/emoji/twitter/cold_sweat.png new file mode 100644 index 0000000000..64bba6550a Binary files /dev/null and b/public/images/emoji/twitter/cold_sweat.png differ diff --git a/public/images/emoji/twitter/collision.png b/public/images/emoji/twitter/collision.png new file mode 100644 index 0000000000..a7f1e2a5eb Binary files /dev/null and b/public/images/emoji/twitter/collision.png differ diff --git a/public/images/emoji/twitter/computer.png b/public/images/emoji/twitter/computer.png new file mode 100644 index 0000000000..5f1241a227 Binary files /dev/null and b/public/images/emoji/twitter/computer.png differ diff --git a/public/images/emoji/twitter/confetti_ball.png b/public/images/emoji/twitter/confetti_ball.png new file mode 100644 index 0000000000..cde7370c2c Binary files /dev/null and b/public/images/emoji/twitter/confetti_ball.png differ diff --git a/public/images/emoji/twitter/confounded.png b/public/images/emoji/twitter/confounded.png new file mode 100644 index 0000000000..391887eb46 Binary files /dev/null and b/public/images/emoji/twitter/confounded.png differ diff --git a/public/images/emoji/twitter/confused.png b/public/images/emoji/twitter/confused.png new file mode 100644 index 0000000000..b5c5b0752c Binary files /dev/null and b/public/images/emoji/twitter/confused.png differ diff --git a/public/images/emoji/twitter/congratulations.png b/public/images/emoji/twitter/congratulations.png new file mode 100644 index 0000000000..43941cf960 Binary files /dev/null and b/public/images/emoji/twitter/congratulations.png differ diff --git a/public/images/emoji/twitter/construction.png b/public/images/emoji/twitter/construction.png new file mode 100644 index 0000000000..9ad7eb0803 Binary files /dev/null and b/public/images/emoji/twitter/construction.png differ diff --git a/public/images/emoji/twitter/construction_worker.png b/public/images/emoji/twitter/construction_worker.png new file mode 100644 index 0000000000..9275fc2556 Binary files /dev/null and b/public/images/emoji/twitter/construction_worker.png differ diff --git a/public/images/emoji/twitter/convenience_store.png b/public/images/emoji/twitter/convenience_store.png new file mode 100644 index 0000000000..5384f54983 Binary files /dev/null and b/public/images/emoji/twitter/convenience_store.png differ diff --git a/public/images/emoji/twitter/cookie.png b/public/images/emoji/twitter/cookie.png new file mode 100644 index 0000000000..f558874b02 Binary files /dev/null and b/public/images/emoji/twitter/cookie.png differ diff --git a/public/images/emoji/twitter/cool.png b/public/images/emoji/twitter/cool.png new file mode 100644 index 0000000000..768286f26f Binary files /dev/null and b/public/images/emoji/twitter/cool.png differ diff --git a/public/images/emoji/twitter/cop.png b/public/images/emoji/twitter/cop.png new file mode 100644 index 0000000000..0ee11b9040 Binary files /dev/null and b/public/images/emoji/twitter/cop.png differ diff --git a/public/images/emoji/twitter/copyright.png b/public/images/emoji/twitter/copyright.png new file mode 100644 index 0000000000..48699b6e46 Binary files /dev/null and b/public/images/emoji/twitter/copyright.png differ diff --git a/public/images/emoji/twitter/corn.png b/public/images/emoji/twitter/corn.png new file mode 100644 index 0000000000..941a8085a8 Binary files /dev/null and b/public/images/emoji/twitter/corn.png differ diff --git a/public/images/emoji/twitter/couple.png b/public/images/emoji/twitter/couple.png new file mode 100644 index 0000000000..3a7c20a76c Binary files /dev/null and b/public/images/emoji/twitter/couple.png differ diff --git a/public/images/emoji/twitter/couple_with_heart.png b/public/images/emoji/twitter/couple_with_heart.png new file mode 100644 index 0000000000..d8a2a1f947 Binary files /dev/null and b/public/images/emoji/twitter/couple_with_heart.png differ diff --git a/public/images/emoji/twitter/couplekiss.png b/public/images/emoji/twitter/couplekiss.png new file mode 100644 index 0000000000..2f1bb00cd5 Binary files /dev/null and b/public/images/emoji/twitter/couplekiss.png differ diff --git a/public/images/emoji/twitter/cow.png b/public/images/emoji/twitter/cow.png new file mode 100644 index 0000000000..498220c09f Binary files /dev/null and b/public/images/emoji/twitter/cow.png differ diff --git a/public/images/emoji/twitter/cow2.png b/public/images/emoji/twitter/cow2.png new file mode 100644 index 0000000000..7f3c3d65fb Binary files /dev/null and b/public/images/emoji/twitter/cow2.png differ diff --git a/public/images/emoji/twitter/credit_card.png b/public/images/emoji/twitter/credit_card.png new file mode 100644 index 0000000000..80e559e311 Binary files /dev/null and b/public/images/emoji/twitter/credit_card.png differ diff --git a/public/images/emoji/twitter/crescent_moon.png b/public/images/emoji/twitter/crescent_moon.png new file mode 100644 index 0000000000..b8eab76ece Binary files /dev/null and b/public/images/emoji/twitter/crescent_moon.png differ diff --git a/public/images/emoji/twitter/crocodile.png b/public/images/emoji/twitter/crocodile.png new file mode 100644 index 0000000000..37aa7ddd01 Binary files /dev/null and b/public/images/emoji/twitter/crocodile.png differ diff --git a/public/images/emoji/twitter/crossed_flags.png b/public/images/emoji/twitter/crossed_flags.png new file mode 100644 index 0000000000..c96898fa68 Binary files /dev/null and b/public/images/emoji/twitter/crossed_flags.png differ diff --git a/public/images/emoji/twitter/crown.png b/public/images/emoji/twitter/crown.png new file mode 100644 index 0000000000..6fc5359bfe Binary files /dev/null and b/public/images/emoji/twitter/crown.png differ diff --git a/public/images/emoji/twitter/cry.png b/public/images/emoji/twitter/cry.png new file mode 100644 index 0000000000..3cedd10024 Binary files /dev/null and b/public/images/emoji/twitter/cry.png differ diff --git a/public/images/emoji/twitter/crying_cat_face.png b/public/images/emoji/twitter/crying_cat_face.png new file mode 100644 index 0000000000..112d9b08d9 Binary files /dev/null and b/public/images/emoji/twitter/crying_cat_face.png differ diff --git a/public/images/emoji/twitter/crystal_ball.png b/public/images/emoji/twitter/crystal_ball.png new file mode 100644 index 0000000000..bc73165e37 Binary files /dev/null and b/public/images/emoji/twitter/crystal_ball.png differ diff --git a/public/images/emoji/twitter/cupid.png b/public/images/emoji/twitter/cupid.png new file mode 100644 index 0000000000..635adec976 Binary files /dev/null and b/public/images/emoji/twitter/cupid.png differ diff --git a/public/images/emoji/twitter/curly_loop.png b/public/images/emoji/twitter/curly_loop.png new file mode 100644 index 0000000000..dfd006b6ba Binary files /dev/null and b/public/images/emoji/twitter/curly_loop.png differ diff --git a/public/images/emoji/twitter/currency_exchange.png b/public/images/emoji/twitter/currency_exchange.png new file mode 100644 index 0000000000..a6a295083f Binary files /dev/null and b/public/images/emoji/twitter/currency_exchange.png differ diff --git a/public/images/emoji/twitter/curry.png b/public/images/emoji/twitter/curry.png new file mode 100644 index 0000000000..6708a387c1 Binary files /dev/null and b/public/images/emoji/twitter/curry.png differ diff --git a/public/images/emoji/twitter/custard.png b/public/images/emoji/twitter/custard.png new file mode 100644 index 0000000000..3b740c002a Binary files /dev/null and b/public/images/emoji/twitter/custard.png differ diff --git a/public/images/emoji/twitter/customs.png b/public/images/emoji/twitter/customs.png new file mode 100644 index 0000000000..7f51eac1fa Binary files /dev/null and b/public/images/emoji/twitter/customs.png differ diff --git a/public/images/emoji/twitter/cyclone.png b/public/images/emoji/twitter/cyclone.png new file mode 100644 index 0000000000..81298ee38e Binary files /dev/null and b/public/images/emoji/twitter/cyclone.png differ diff --git a/public/images/emoji/twitter/dancer.png b/public/images/emoji/twitter/dancer.png new file mode 100644 index 0000000000..383a7b7bf8 Binary files /dev/null and b/public/images/emoji/twitter/dancer.png differ diff --git a/public/images/emoji/twitter/dancers.png b/public/images/emoji/twitter/dancers.png new file mode 100644 index 0000000000..52768b4abe Binary files /dev/null and b/public/images/emoji/twitter/dancers.png differ diff --git a/public/images/emoji/twitter/dango.png b/public/images/emoji/twitter/dango.png new file mode 100644 index 0000000000..8567a28537 Binary files /dev/null and b/public/images/emoji/twitter/dango.png differ diff --git a/public/images/emoji/twitter/dart.png b/public/images/emoji/twitter/dart.png new file mode 100644 index 0000000000..812f335421 Binary files /dev/null and b/public/images/emoji/twitter/dart.png differ diff --git a/public/images/emoji/twitter/dash.png b/public/images/emoji/twitter/dash.png new file mode 100644 index 0000000000..bbcd065b11 Binary files /dev/null and b/public/images/emoji/twitter/dash.png differ diff --git a/public/images/emoji/twitter/date.png b/public/images/emoji/twitter/date.png new file mode 100644 index 0000000000..1f65765e49 Binary files /dev/null and b/public/images/emoji/twitter/date.png differ diff --git a/public/images/emoji/twitter/de.png b/public/images/emoji/twitter/de.png new file mode 100644 index 0000000000..6c98990888 Binary files /dev/null and b/public/images/emoji/twitter/de.png differ diff --git a/public/images/emoji/twitter/deciduous_tree.png b/public/images/emoji/twitter/deciduous_tree.png new file mode 100644 index 0000000000..34a0a9516f Binary files /dev/null and b/public/images/emoji/twitter/deciduous_tree.png differ diff --git a/public/images/emoji/twitter/department_store.png b/public/images/emoji/twitter/department_store.png new file mode 100644 index 0000000000..473ac9921e Binary files /dev/null and b/public/images/emoji/twitter/department_store.png differ diff --git a/public/images/emoji/twitter/diamond_shape_with_a_dot_inside.png b/public/images/emoji/twitter/diamond_shape_with_a_dot_inside.png new file mode 100644 index 0000000000..ad16b8b899 Binary files /dev/null and b/public/images/emoji/twitter/diamond_shape_with_a_dot_inside.png differ diff --git a/public/images/emoji/twitter/diamonds.png b/public/images/emoji/twitter/diamonds.png new file mode 100644 index 0000000000..b9f3ca876a Binary files /dev/null and b/public/images/emoji/twitter/diamonds.png differ diff --git a/public/images/emoji/twitter/disappointed.png b/public/images/emoji/twitter/disappointed.png new file mode 100644 index 0000000000..22bf80de00 Binary files /dev/null and b/public/images/emoji/twitter/disappointed.png differ diff --git a/public/images/emoji/twitter/disappointed_relieved.png b/public/images/emoji/twitter/disappointed_relieved.png new file mode 100644 index 0000000000..d73d8a31f7 Binary files /dev/null and b/public/images/emoji/twitter/disappointed_relieved.png differ diff --git a/public/images/emoji/twitter/dizzy.png b/public/images/emoji/twitter/dizzy.png new file mode 100644 index 0000000000..f6b9920ab9 Binary files /dev/null and b/public/images/emoji/twitter/dizzy.png differ diff --git a/public/images/emoji/twitter/dizzy_face.png b/public/images/emoji/twitter/dizzy_face.png new file mode 100644 index 0000000000..45d3443efc Binary files /dev/null and b/public/images/emoji/twitter/dizzy_face.png differ diff --git a/public/images/emoji/twitter/do_not_litter.png b/public/images/emoji/twitter/do_not_litter.png new file mode 100644 index 0000000000..235ceccc5c Binary files /dev/null and b/public/images/emoji/twitter/do_not_litter.png differ diff --git a/public/images/emoji/twitter/dog.png b/public/images/emoji/twitter/dog.png new file mode 100644 index 0000000000..cb5afae151 Binary files /dev/null and b/public/images/emoji/twitter/dog.png differ diff --git a/public/images/emoji/twitter/dog2.png b/public/images/emoji/twitter/dog2.png new file mode 100644 index 0000000000..544b06c57e Binary files /dev/null and b/public/images/emoji/twitter/dog2.png differ diff --git a/public/images/emoji/twitter/dollar.png b/public/images/emoji/twitter/dollar.png new file mode 100644 index 0000000000..a6fc28564c Binary files /dev/null and b/public/images/emoji/twitter/dollar.png differ diff --git a/public/images/emoji/twitter/dolls.png b/public/images/emoji/twitter/dolls.png new file mode 100644 index 0000000000..2091a47e4b Binary files /dev/null and b/public/images/emoji/twitter/dolls.png differ diff --git a/public/images/emoji/twitter/dolphin.png b/public/images/emoji/twitter/dolphin.png new file mode 100644 index 0000000000..38c764d878 Binary files /dev/null and b/public/images/emoji/twitter/dolphin.png differ diff --git a/public/images/emoji/twitter/door.png b/public/images/emoji/twitter/door.png new file mode 100644 index 0000000000..b812516a8e Binary files /dev/null and b/public/images/emoji/twitter/door.png differ diff --git a/public/images/emoji/twitter/doughnut.png b/public/images/emoji/twitter/doughnut.png new file mode 100644 index 0000000000..77fbe57701 Binary files /dev/null and b/public/images/emoji/twitter/doughnut.png differ diff --git a/public/images/emoji/twitter/dragon.png b/public/images/emoji/twitter/dragon.png new file mode 100644 index 0000000000..328dba4a0d Binary files /dev/null and b/public/images/emoji/twitter/dragon.png differ diff --git a/public/images/emoji/twitter/dragon_face.png b/public/images/emoji/twitter/dragon_face.png new file mode 100644 index 0000000000..a082fc5984 Binary files /dev/null and b/public/images/emoji/twitter/dragon_face.png differ diff --git a/public/images/emoji/twitter/dress.png b/public/images/emoji/twitter/dress.png new file mode 100644 index 0000000000..2cd3306393 Binary files /dev/null and b/public/images/emoji/twitter/dress.png differ diff --git a/public/images/emoji/twitter/dromedary_camel.png b/public/images/emoji/twitter/dromedary_camel.png new file mode 100644 index 0000000000..59b26340bf Binary files /dev/null and b/public/images/emoji/twitter/dromedary_camel.png differ diff --git a/public/images/emoji/twitter/droplet.png b/public/images/emoji/twitter/droplet.png new file mode 100644 index 0000000000..c880318b29 Binary files /dev/null and b/public/images/emoji/twitter/droplet.png differ diff --git a/public/images/emoji/twitter/dvd.png b/public/images/emoji/twitter/dvd.png new file mode 100644 index 0000000000..b2fef5b99e Binary files /dev/null and b/public/images/emoji/twitter/dvd.png differ diff --git a/public/images/emoji/twitter/e-mail.png b/public/images/emoji/twitter/e-mail.png new file mode 100644 index 0000000000..7b759b705d Binary files /dev/null and b/public/images/emoji/twitter/e-mail.png differ diff --git a/public/images/emoji/twitter/ear.png b/public/images/emoji/twitter/ear.png new file mode 100644 index 0000000000..327d2ca3cb Binary files /dev/null and b/public/images/emoji/twitter/ear.png differ diff --git a/public/images/emoji/twitter/ear_of_rice.png b/public/images/emoji/twitter/ear_of_rice.png new file mode 100644 index 0000000000..db1c7b998f Binary files /dev/null and b/public/images/emoji/twitter/ear_of_rice.png differ diff --git a/public/images/emoji/twitter/earth_africa.png b/public/images/emoji/twitter/earth_africa.png new file mode 100644 index 0000000000..8d0222397c Binary files /dev/null and b/public/images/emoji/twitter/earth_africa.png differ diff --git a/public/images/emoji/twitter/earth_americas.png b/public/images/emoji/twitter/earth_americas.png new file mode 100644 index 0000000000..e3adea7945 Binary files /dev/null and b/public/images/emoji/twitter/earth_americas.png differ diff --git a/public/images/emoji/twitter/earth_asia.png b/public/images/emoji/twitter/earth_asia.png new file mode 100644 index 0000000000..1ef76cd9a3 Binary files /dev/null and b/public/images/emoji/twitter/earth_asia.png differ diff --git a/public/images/emoji/twitter/egg.png b/public/images/emoji/twitter/egg.png new file mode 100644 index 0000000000..05d55c3970 Binary files /dev/null and b/public/images/emoji/twitter/egg.png differ diff --git a/public/images/emoji/twitter/eggplant.png b/public/images/emoji/twitter/eggplant.png new file mode 100644 index 0000000000..f730d0cc59 Binary files /dev/null and b/public/images/emoji/twitter/eggplant.png differ diff --git a/public/images/emoji/twitter/eight.png b/public/images/emoji/twitter/eight.png new file mode 100644 index 0000000000..f8fce1bd81 Binary files /dev/null and b/public/images/emoji/twitter/eight.png differ diff --git a/public/images/emoji/twitter/eight_pointed_black_star.png b/public/images/emoji/twitter/eight_pointed_black_star.png new file mode 100644 index 0000000000..e627291651 Binary files /dev/null and b/public/images/emoji/twitter/eight_pointed_black_star.png differ diff --git a/public/images/emoji/twitter/eight_spoked_asterisk.png b/public/images/emoji/twitter/eight_spoked_asterisk.png new file mode 100644 index 0000000000..e946641cd6 Binary files /dev/null and b/public/images/emoji/twitter/eight_spoked_asterisk.png differ diff --git a/public/images/emoji/twitter/electric_plug.png b/public/images/emoji/twitter/electric_plug.png new file mode 100644 index 0000000000..b5cae49a8f Binary files /dev/null and b/public/images/emoji/twitter/electric_plug.png differ diff --git a/public/images/emoji/twitter/elephant.png b/public/images/emoji/twitter/elephant.png new file mode 100644 index 0000000000..f09a72ff36 Binary files /dev/null and b/public/images/emoji/twitter/elephant.png differ diff --git a/public/images/emoji/twitter/email.png b/public/images/emoji/twitter/email.png new file mode 100644 index 0000000000..48f5f907fa Binary files /dev/null and b/public/images/emoji/twitter/email.png differ diff --git a/public/images/emoji/twitter/end.png b/public/images/emoji/twitter/end.png new file mode 100644 index 0000000000..6f31c3698b Binary files /dev/null and b/public/images/emoji/twitter/end.png differ diff --git a/public/images/emoji/twitter/envelope.png b/public/images/emoji/twitter/envelope.png new file mode 100644 index 0000000000..48f5f907fa Binary files /dev/null and b/public/images/emoji/twitter/envelope.png differ diff --git a/public/images/emoji/twitter/envelope_with_arrow.png b/public/images/emoji/twitter/envelope_with_arrow.png new file mode 100644 index 0000000000..437f2bacfe Binary files /dev/null and b/public/images/emoji/twitter/envelope_with_arrow.png differ diff --git a/public/images/emoji/twitter/es.png b/public/images/emoji/twitter/es.png new file mode 100644 index 0000000000..f1c9ca864a Binary files /dev/null and b/public/images/emoji/twitter/es.png differ diff --git a/public/images/emoji/twitter/euro.png b/public/images/emoji/twitter/euro.png new file mode 100644 index 0000000000..34b3fdca17 Binary files /dev/null and b/public/images/emoji/twitter/euro.png differ diff --git a/public/images/emoji/twitter/european_castle.png b/public/images/emoji/twitter/european_castle.png new file mode 100644 index 0000000000..3746920b65 Binary files /dev/null and b/public/images/emoji/twitter/european_castle.png differ diff --git a/public/images/emoji/twitter/european_post_office.png b/public/images/emoji/twitter/european_post_office.png new file mode 100644 index 0000000000..fc809d143e Binary files /dev/null and b/public/images/emoji/twitter/european_post_office.png differ diff --git a/public/images/emoji/twitter/evergreen_tree.png b/public/images/emoji/twitter/evergreen_tree.png new file mode 100644 index 0000000000..a5dc92c249 Binary files /dev/null and b/public/images/emoji/twitter/evergreen_tree.png differ diff --git a/public/images/emoji/twitter/exclamation.png b/public/images/emoji/twitter/exclamation.png new file mode 100644 index 0000000000..9fdca664aa Binary files /dev/null and b/public/images/emoji/twitter/exclamation.png differ diff --git a/public/images/emoji/twitter/expressionless.png b/public/images/emoji/twitter/expressionless.png new file mode 100644 index 0000000000..b18262c97a Binary files /dev/null and b/public/images/emoji/twitter/expressionless.png differ diff --git a/public/images/emoji/twitter/eyeglasses.png b/public/images/emoji/twitter/eyeglasses.png new file mode 100644 index 0000000000..56843572e4 Binary files /dev/null and b/public/images/emoji/twitter/eyeglasses.png differ diff --git a/public/images/emoji/twitter/eyes.png b/public/images/emoji/twitter/eyes.png new file mode 100644 index 0000000000..543cd83089 Binary files /dev/null and b/public/images/emoji/twitter/eyes.png differ diff --git a/public/images/emoji/twitter/facepunch.png b/public/images/emoji/twitter/facepunch.png new file mode 100644 index 0000000000..b41129276f Binary files /dev/null and b/public/images/emoji/twitter/facepunch.png differ diff --git a/public/images/emoji/twitter/factory.png b/public/images/emoji/twitter/factory.png new file mode 100644 index 0000000000..ae843aa1fd Binary files /dev/null and b/public/images/emoji/twitter/factory.png differ diff --git a/public/images/emoji/twitter/fallen_leaf.png b/public/images/emoji/twitter/fallen_leaf.png new file mode 100644 index 0000000000..12fabf89e6 Binary files /dev/null and b/public/images/emoji/twitter/fallen_leaf.png differ diff --git a/public/images/emoji/twitter/family.png b/public/images/emoji/twitter/family.png new file mode 100644 index 0000000000..c51b9c5349 Binary files /dev/null and b/public/images/emoji/twitter/family.png differ diff --git a/public/images/emoji/twitter/fast_forward.png b/public/images/emoji/twitter/fast_forward.png new file mode 100644 index 0000000000..5e5bf418ce Binary files /dev/null and b/public/images/emoji/twitter/fast_forward.png differ diff --git a/public/images/emoji/twitter/fax.png b/public/images/emoji/twitter/fax.png new file mode 100644 index 0000000000..cc9c1e8461 Binary files /dev/null and b/public/images/emoji/twitter/fax.png differ diff --git a/public/images/emoji/twitter/fearful.png b/public/images/emoji/twitter/fearful.png new file mode 100644 index 0000000000..87fbeafded Binary files /dev/null and b/public/images/emoji/twitter/fearful.png differ diff --git a/public/images/emoji/twitter/feet.png b/public/images/emoji/twitter/feet.png new file mode 100644 index 0000000000..94deaa1e55 Binary files /dev/null and b/public/images/emoji/twitter/feet.png differ diff --git a/public/images/emoji/twitter/ferris_wheel.png b/public/images/emoji/twitter/ferris_wheel.png new file mode 100644 index 0000000000..f1fdeeda81 Binary files /dev/null and b/public/images/emoji/twitter/ferris_wheel.png differ diff --git a/public/images/emoji/twitter/file_folder.png b/public/images/emoji/twitter/file_folder.png new file mode 100644 index 0000000000..d35190876c Binary files /dev/null and b/public/images/emoji/twitter/file_folder.png differ diff --git a/public/images/emoji/twitter/fire.png b/public/images/emoji/twitter/fire.png new file mode 100644 index 0000000000..6b794c4fcf Binary files /dev/null and b/public/images/emoji/twitter/fire.png differ diff --git a/public/images/emoji/twitter/fire_engine.png b/public/images/emoji/twitter/fire_engine.png new file mode 100644 index 0000000000..83018e1b7a Binary files /dev/null and b/public/images/emoji/twitter/fire_engine.png differ diff --git a/public/images/emoji/twitter/fireworks.png b/public/images/emoji/twitter/fireworks.png new file mode 100644 index 0000000000..fbfe37a540 Binary files /dev/null and b/public/images/emoji/twitter/fireworks.png differ diff --git a/public/images/emoji/twitter/first_quarter_moon.png b/public/images/emoji/twitter/first_quarter_moon.png new file mode 100644 index 0000000000..72aedfe005 Binary files /dev/null and b/public/images/emoji/twitter/first_quarter_moon.png differ diff --git a/public/images/emoji/twitter/first_quarter_moon_with_face.png b/public/images/emoji/twitter/first_quarter_moon_with_face.png new file mode 100644 index 0000000000..3324846ee2 Binary files /dev/null and b/public/images/emoji/twitter/first_quarter_moon_with_face.png differ diff --git a/public/images/emoji/twitter/fish.png b/public/images/emoji/twitter/fish.png new file mode 100644 index 0000000000..839e210761 Binary files /dev/null and b/public/images/emoji/twitter/fish.png differ diff --git a/public/images/emoji/twitter/fish_cake.png b/public/images/emoji/twitter/fish_cake.png new file mode 100644 index 0000000000..b1a00ee452 Binary files /dev/null and b/public/images/emoji/twitter/fish_cake.png differ diff --git a/public/images/emoji/twitter/fishing_pole_and_fish.png b/public/images/emoji/twitter/fishing_pole_and_fish.png new file mode 100644 index 0000000000..eee0257faf Binary files /dev/null and b/public/images/emoji/twitter/fishing_pole_and_fish.png differ diff --git a/public/images/emoji/twitter/fist.png b/public/images/emoji/twitter/fist.png new file mode 100644 index 0000000000..2374784cae Binary files /dev/null and b/public/images/emoji/twitter/fist.png differ diff --git a/public/images/emoji/twitter/five.png b/public/images/emoji/twitter/five.png new file mode 100644 index 0000000000..77034136a3 Binary files /dev/null and b/public/images/emoji/twitter/five.png differ diff --git a/public/images/emoji/twitter/flags.png b/public/images/emoji/twitter/flags.png new file mode 100644 index 0000000000..f3a2f3b0e3 Binary files /dev/null and b/public/images/emoji/twitter/flags.png differ diff --git a/public/images/emoji/twitter/flashlight.png b/public/images/emoji/twitter/flashlight.png new file mode 100644 index 0000000000..de62c7f5f5 Binary files /dev/null and b/public/images/emoji/twitter/flashlight.png differ diff --git a/public/images/emoji/twitter/flipper.png b/public/images/emoji/twitter/flipper.png new file mode 100644 index 0000000000..38c764d878 Binary files /dev/null and b/public/images/emoji/twitter/flipper.png differ diff --git a/public/images/emoji/twitter/floppy_disk.png b/public/images/emoji/twitter/floppy_disk.png new file mode 100644 index 0000000000..8c93824ee8 Binary files /dev/null and b/public/images/emoji/twitter/floppy_disk.png differ diff --git a/public/images/emoji/twitter/flower_playing_cards.png b/public/images/emoji/twitter/flower_playing_cards.png new file mode 100644 index 0000000000..ce00831432 Binary files /dev/null and b/public/images/emoji/twitter/flower_playing_cards.png differ diff --git a/public/images/emoji/twitter/flushed.png b/public/images/emoji/twitter/flushed.png new file mode 100644 index 0000000000..3957403f5d Binary files /dev/null and b/public/images/emoji/twitter/flushed.png differ diff --git a/public/images/emoji/twitter/foggy.png b/public/images/emoji/twitter/foggy.png new file mode 100644 index 0000000000..dedacf36c0 Binary files /dev/null and b/public/images/emoji/twitter/foggy.png differ diff --git a/public/images/emoji/twitter/football.png b/public/images/emoji/twitter/football.png new file mode 100644 index 0000000000..4e87648f1a Binary files /dev/null and b/public/images/emoji/twitter/football.png differ diff --git a/public/images/emoji/twitter/footprints.png b/public/images/emoji/twitter/footprints.png new file mode 100644 index 0000000000..e5db6271ed Binary files /dev/null and b/public/images/emoji/twitter/footprints.png differ diff --git a/public/images/emoji/twitter/fork_and_knife.png b/public/images/emoji/twitter/fork_and_knife.png new file mode 100644 index 0000000000..c13653732d Binary files /dev/null and b/public/images/emoji/twitter/fork_and_knife.png differ diff --git a/public/images/emoji/twitter/fountain.png b/public/images/emoji/twitter/fountain.png new file mode 100644 index 0000000000..01fab36e54 Binary files /dev/null and b/public/images/emoji/twitter/fountain.png differ diff --git a/public/images/emoji/twitter/four.png b/public/images/emoji/twitter/four.png new file mode 100644 index 0000000000..a6debcffc1 Binary files /dev/null and b/public/images/emoji/twitter/four.png differ diff --git a/public/images/emoji/twitter/four_leaf_clover.png b/public/images/emoji/twitter/four_leaf_clover.png new file mode 100644 index 0000000000..0339897b72 Binary files /dev/null and b/public/images/emoji/twitter/four_leaf_clover.png differ diff --git a/public/images/emoji/twitter/fr.png b/public/images/emoji/twitter/fr.png new file mode 100644 index 0000000000..5db0326127 Binary files /dev/null and b/public/images/emoji/twitter/fr.png differ diff --git a/public/images/emoji/twitter/free.png b/public/images/emoji/twitter/free.png new file mode 100644 index 0000000000..28803e044f Binary files /dev/null and b/public/images/emoji/twitter/free.png differ diff --git a/public/images/emoji/twitter/fried_shrimp.png b/public/images/emoji/twitter/fried_shrimp.png new file mode 100644 index 0000000000..16726fb6ca Binary files /dev/null and b/public/images/emoji/twitter/fried_shrimp.png differ diff --git a/public/images/emoji/twitter/fries.png b/public/images/emoji/twitter/fries.png new file mode 100644 index 0000000000..976e30d901 Binary files /dev/null and b/public/images/emoji/twitter/fries.png differ diff --git a/public/images/emoji/twitter/frog.png b/public/images/emoji/twitter/frog.png new file mode 100644 index 0000000000..13678d96e2 Binary files /dev/null and b/public/images/emoji/twitter/frog.png differ diff --git a/public/images/emoji/twitter/frowning.png b/public/images/emoji/twitter/frowning.png new file mode 100644 index 0000000000..6a6d43d319 Binary files /dev/null and b/public/images/emoji/twitter/frowning.png differ diff --git a/public/images/emoji/twitter/fuelpump.png b/public/images/emoji/twitter/fuelpump.png new file mode 100644 index 0000000000..29e2970484 Binary files /dev/null and b/public/images/emoji/twitter/fuelpump.png differ diff --git a/public/images/emoji/twitter/full_moon.png b/public/images/emoji/twitter/full_moon.png new file mode 100644 index 0000000000..b97fc9bcde Binary files /dev/null and b/public/images/emoji/twitter/full_moon.png differ diff --git a/public/images/emoji/twitter/full_moon_with_face.png b/public/images/emoji/twitter/full_moon_with_face.png new file mode 100644 index 0000000000..ea8b052d1b Binary files /dev/null and b/public/images/emoji/twitter/full_moon_with_face.png differ diff --git a/public/images/emoji/twitter/game_die.png b/public/images/emoji/twitter/game_die.png new file mode 100644 index 0000000000..2745754d14 Binary files /dev/null and b/public/images/emoji/twitter/game_die.png differ diff --git a/public/images/emoji/twitter/gb.png b/public/images/emoji/twitter/gb.png new file mode 100644 index 0000000000..f31e458c1d Binary files /dev/null and b/public/images/emoji/twitter/gb.png differ diff --git a/public/images/emoji/twitter/gem.png b/public/images/emoji/twitter/gem.png new file mode 100644 index 0000000000..e455ec978b Binary files /dev/null and b/public/images/emoji/twitter/gem.png differ diff --git a/public/images/emoji/twitter/gemini.png b/public/images/emoji/twitter/gemini.png new file mode 100644 index 0000000000..bb1fde5cd7 Binary files /dev/null and b/public/images/emoji/twitter/gemini.png differ diff --git a/public/images/emoji/twitter/ghost.png b/public/images/emoji/twitter/ghost.png new file mode 100644 index 0000000000..43f96c27b8 Binary files /dev/null and b/public/images/emoji/twitter/ghost.png differ diff --git a/public/images/emoji/twitter/gift.png b/public/images/emoji/twitter/gift.png new file mode 100644 index 0000000000..054ceb609d Binary files /dev/null and b/public/images/emoji/twitter/gift.png differ diff --git a/public/images/emoji/twitter/gift_heart.png b/public/images/emoji/twitter/gift_heart.png new file mode 100644 index 0000000000..9370396f84 Binary files /dev/null and b/public/images/emoji/twitter/gift_heart.png differ diff --git a/public/images/emoji/twitter/girl.png b/public/images/emoji/twitter/girl.png new file mode 100644 index 0000000000..1414cc64c9 Binary files /dev/null and b/public/images/emoji/twitter/girl.png differ diff --git a/public/images/emoji/twitter/globe_with_meridians.png b/public/images/emoji/twitter/globe_with_meridians.png new file mode 100644 index 0000000000..7875096c3c Binary files /dev/null and b/public/images/emoji/twitter/globe_with_meridians.png differ diff --git a/public/images/emoji/twitter/goat.png b/public/images/emoji/twitter/goat.png new file mode 100644 index 0000000000..a6f5479a8d Binary files /dev/null and b/public/images/emoji/twitter/goat.png differ diff --git a/public/images/emoji/twitter/golf.png b/public/images/emoji/twitter/golf.png new file mode 100644 index 0000000000..cf27469ca3 Binary files /dev/null and b/public/images/emoji/twitter/golf.png differ diff --git a/public/images/emoji/twitter/grapes.png b/public/images/emoji/twitter/grapes.png new file mode 100644 index 0000000000..694dd705b0 Binary files /dev/null and b/public/images/emoji/twitter/grapes.png differ diff --git a/public/images/emoji/twitter/green_apple.png b/public/images/emoji/twitter/green_apple.png new file mode 100644 index 0000000000..37a3a771d2 Binary files /dev/null and b/public/images/emoji/twitter/green_apple.png differ diff --git a/public/images/emoji/twitter/green_book.png b/public/images/emoji/twitter/green_book.png new file mode 100644 index 0000000000..3b642510e1 Binary files /dev/null and b/public/images/emoji/twitter/green_book.png differ diff --git a/public/images/emoji/twitter/green_heart.png b/public/images/emoji/twitter/green_heart.png new file mode 100644 index 0000000000..e00b740cbe Binary files /dev/null and b/public/images/emoji/twitter/green_heart.png differ diff --git a/public/images/emoji/twitter/grey_exclamation.png b/public/images/emoji/twitter/grey_exclamation.png new file mode 100644 index 0000000000..938a03f1f5 Binary files /dev/null and b/public/images/emoji/twitter/grey_exclamation.png differ diff --git a/public/images/emoji/twitter/grey_question.png b/public/images/emoji/twitter/grey_question.png new file mode 100644 index 0000000000..f315475e0e Binary files /dev/null and b/public/images/emoji/twitter/grey_question.png differ diff --git a/public/images/emoji/twitter/grimacing.png b/public/images/emoji/twitter/grimacing.png new file mode 100644 index 0000000000..00244300ed Binary files /dev/null and b/public/images/emoji/twitter/grimacing.png differ diff --git a/public/images/emoji/twitter/grin.png b/public/images/emoji/twitter/grin.png new file mode 100644 index 0000000000..dba5a759b6 Binary files /dev/null and b/public/images/emoji/twitter/grin.png differ diff --git a/public/images/emoji/twitter/grinning.png b/public/images/emoji/twitter/grinning.png new file mode 100644 index 0000000000..34ac223f39 Binary files /dev/null and b/public/images/emoji/twitter/grinning.png differ diff --git a/public/images/emoji/twitter/guardsman.png b/public/images/emoji/twitter/guardsman.png new file mode 100644 index 0000000000..213b4f2d3d Binary files /dev/null and b/public/images/emoji/twitter/guardsman.png differ diff --git a/public/images/emoji/twitter/guitar.png b/public/images/emoji/twitter/guitar.png new file mode 100644 index 0000000000..94459caa66 Binary files /dev/null and b/public/images/emoji/twitter/guitar.png differ diff --git a/public/images/emoji/twitter/gun.png b/public/images/emoji/twitter/gun.png new file mode 100644 index 0000000000..243beb1e2a Binary files /dev/null and b/public/images/emoji/twitter/gun.png differ diff --git a/public/images/emoji/twitter/haircut.png b/public/images/emoji/twitter/haircut.png new file mode 100644 index 0000000000..042cdf54da Binary files /dev/null and b/public/images/emoji/twitter/haircut.png differ diff --git a/public/images/emoji/twitter/hamburger.png b/public/images/emoji/twitter/hamburger.png new file mode 100644 index 0000000000..d720c57208 Binary files /dev/null and b/public/images/emoji/twitter/hamburger.png differ diff --git a/public/images/emoji/twitter/hammer.png b/public/images/emoji/twitter/hammer.png new file mode 100644 index 0000000000..99f9f88ad1 Binary files /dev/null and b/public/images/emoji/twitter/hammer.png differ diff --git a/public/images/emoji/twitter/hamster.png b/public/images/emoji/twitter/hamster.png new file mode 100644 index 0000000000..ac23f41155 Binary files /dev/null and b/public/images/emoji/twitter/hamster.png differ diff --git a/public/images/emoji/twitter/hand.png b/public/images/emoji/twitter/hand.png new file mode 100644 index 0000000000..f7edfd6c3d Binary files /dev/null and b/public/images/emoji/twitter/hand.png differ diff --git a/public/images/emoji/twitter/handbag.png b/public/images/emoji/twitter/handbag.png new file mode 100644 index 0000000000..be89378c79 Binary files /dev/null and b/public/images/emoji/twitter/handbag.png differ diff --git a/public/images/emoji/twitter/hankey.png b/public/images/emoji/twitter/hankey.png new file mode 100644 index 0000000000..d44f203646 Binary files /dev/null and b/public/images/emoji/twitter/hankey.png differ diff --git a/public/images/emoji/twitter/hash.png b/public/images/emoji/twitter/hash.png new file mode 100644 index 0000000000..496e3c8f9a Binary files /dev/null and b/public/images/emoji/twitter/hash.png differ diff --git a/public/images/emoji/twitter/hatched_chick.png b/public/images/emoji/twitter/hatched_chick.png new file mode 100644 index 0000000000..2593d560f6 Binary files /dev/null and b/public/images/emoji/twitter/hatched_chick.png differ diff --git a/public/images/emoji/twitter/hatching_chick.png b/public/images/emoji/twitter/hatching_chick.png new file mode 100644 index 0000000000..8e5cb2b609 Binary files /dev/null and b/public/images/emoji/twitter/hatching_chick.png differ diff --git a/public/images/emoji/twitter/headphones.png b/public/images/emoji/twitter/headphones.png new file mode 100644 index 0000000000..58c3aabcdc Binary files /dev/null and b/public/images/emoji/twitter/headphones.png differ diff --git a/public/images/emoji/twitter/hear_no_evil.png b/public/images/emoji/twitter/hear_no_evil.png new file mode 100644 index 0000000000..dc5c4b2a34 Binary files /dev/null and b/public/images/emoji/twitter/hear_no_evil.png differ diff --git a/public/images/emoji/twitter/heart.png b/public/images/emoji/twitter/heart.png new file mode 100644 index 0000000000..add7a5de00 Binary files /dev/null and b/public/images/emoji/twitter/heart.png differ diff --git a/public/images/emoji/twitter/heart_decoration.png b/public/images/emoji/twitter/heart_decoration.png new file mode 100644 index 0000000000..ca45712fa8 Binary files /dev/null and b/public/images/emoji/twitter/heart_decoration.png differ diff --git a/public/images/emoji/twitter/heart_eyes.png b/public/images/emoji/twitter/heart_eyes.png new file mode 100644 index 0000000000..ecfc26db64 Binary files /dev/null and b/public/images/emoji/twitter/heart_eyes.png differ diff --git a/public/images/emoji/twitter/heart_eyes_cat.png b/public/images/emoji/twitter/heart_eyes_cat.png new file mode 100644 index 0000000000..2de123d16b Binary files /dev/null and b/public/images/emoji/twitter/heart_eyes_cat.png differ diff --git a/public/images/emoji/twitter/heartbeat.png b/public/images/emoji/twitter/heartbeat.png new file mode 100644 index 0000000000..95416c6f11 Binary files /dev/null and b/public/images/emoji/twitter/heartbeat.png differ diff --git a/public/images/emoji/twitter/heartpulse.png b/public/images/emoji/twitter/heartpulse.png new file mode 100644 index 0000000000..e6256db2d1 Binary files /dev/null and b/public/images/emoji/twitter/heartpulse.png differ diff --git a/public/images/emoji/twitter/hearts.png b/public/images/emoji/twitter/hearts.png new file mode 100644 index 0000000000..3e56fe0cbf Binary files /dev/null and b/public/images/emoji/twitter/hearts.png differ diff --git a/public/images/emoji/twitter/heavy_check_mark.png b/public/images/emoji/twitter/heavy_check_mark.png new file mode 100644 index 0000000000..41a80f3f6e Binary files /dev/null and b/public/images/emoji/twitter/heavy_check_mark.png differ diff --git a/public/images/emoji/twitter/heavy_division_sign.png b/public/images/emoji/twitter/heavy_division_sign.png new file mode 100644 index 0000000000..5a780b11a9 Binary files /dev/null and b/public/images/emoji/twitter/heavy_division_sign.png differ diff --git a/public/images/emoji/twitter/heavy_dollar_sign.png b/public/images/emoji/twitter/heavy_dollar_sign.png new file mode 100644 index 0000000000..af0ea8d1f2 Binary files /dev/null and b/public/images/emoji/twitter/heavy_dollar_sign.png differ diff --git a/public/images/emoji/twitter/heavy_exclamation_mark.png b/public/images/emoji/twitter/heavy_exclamation_mark.png new file mode 100644 index 0000000000..9fdca664aa Binary files /dev/null and b/public/images/emoji/twitter/heavy_exclamation_mark.png differ diff --git a/public/images/emoji/twitter/heavy_minus_sign.png b/public/images/emoji/twitter/heavy_minus_sign.png new file mode 100644 index 0000000000..91e1e27e7a Binary files /dev/null and b/public/images/emoji/twitter/heavy_minus_sign.png differ diff --git a/public/images/emoji/twitter/heavy_multiplication_x.png b/public/images/emoji/twitter/heavy_multiplication_x.png new file mode 100644 index 0000000000..f497148921 Binary files /dev/null and b/public/images/emoji/twitter/heavy_multiplication_x.png differ diff --git a/public/images/emoji/twitter/heavy_plus_sign.png b/public/images/emoji/twitter/heavy_plus_sign.png new file mode 100644 index 0000000000..245b120410 Binary files /dev/null and b/public/images/emoji/twitter/heavy_plus_sign.png differ diff --git a/public/images/emoji/twitter/helicopter.png b/public/images/emoji/twitter/helicopter.png new file mode 100644 index 0000000000..222450010d Binary files /dev/null and b/public/images/emoji/twitter/helicopter.png differ diff --git a/public/images/emoji/twitter/herb.png b/public/images/emoji/twitter/herb.png new file mode 100644 index 0000000000..dfb2cf0423 Binary files /dev/null and b/public/images/emoji/twitter/herb.png differ diff --git a/public/images/emoji/twitter/hibiscus.png b/public/images/emoji/twitter/hibiscus.png new file mode 100644 index 0000000000..eef0068139 Binary files /dev/null and b/public/images/emoji/twitter/hibiscus.png differ diff --git a/public/images/emoji/twitter/high_brightness.png b/public/images/emoji/twitter/high_brightness.png new file mode 100644 index 0000000000..ffd3dee5eb Binary files /dev/null and b/public/images/emoji/twitter/high_brightness.png differ diff --git a/public/images/emoji/twitter/high_heel.png b/public/images/emoji/twitter/high_heel.png new file mode 100644 index 0000000000..ef4e937073 Binary files /dev/null and b/public/images/emoji/twitter/high_heel.png differ diff --git a/public/images/emoji/twitter/hocho.png b/public/images/emoji/twitter/hocho.png new file mode 100644 index 0000000000..1aae17f5b5 Binary files /dev/null and b/public/images/emoji/twitter/hocho.png differ diff --git a/public/images/emoji/twitter/honey_pot.png b/public/images/emoji/twitter/honey_pot.png new file mode 100644 index 0000000000..10cd190a2d Binary files /dev/null and b/public/images/emoji/twitter/honey_pot.png differ diff --git a/public/images/emoji/twitter/honeybee.png b/public/images/emoji/twitter/honeybee.png new file mode 100644 index 0000000000..fa15755f91 Binary files /dev/null and b/public/images/emoji/twitter/honeybee.png differ diff --git a/public/images/emoji/twitter/horse.png b/public/images/emoji/twitter/horse.png new file mode 100644 index 0000000000..f7d143166c Binary files /dev/null and b/public/images/emoji/twitter/horse.png differ diff --git a/public/images/emoji/twitter/horse_racing.png b/public/images/emoji/twitter/horse_racing.png new file mode 100644 index 0000000000..026f9db33f Binary files /dev/null and b/public/images/emoji/twitter/horse_racing.png differ diff --git a/public/images/emoji/twitter/hospital.png b/public/images/emoji/twitter/hospital.png new file mode 100644 index 0000000000..6fce5dd0b2 Binary files /dev/null and b/public/images/emoji/twitter/hospital.png differ diff --git a/public/images/emoji/twitter/hotel.png b/public/images/emoji/twitter/hotel.png new file mode 100644 index 0000000000..63f5c5b1f3 Binary files /dev/null and b/public/images/emoji/twitter/hotel.png differ diff --git a/public/images/emoji/twitter/hotsprings.png b/public/images/emoji/twitter/hotsprings.png new file mode 100644 index 0000000000..e88aa44b31 Binary files /dev/null and b/public/images/emoji/twitter/hotsprings.png differ diff --git a/public/images/emoji/twitter/hourglass.png b/public/images/emoji/twitter/hourglass.png new file mode 100644 index 0000000000..55d822d0a1 Binary files /dev/null and b/public/images/emoji/twitter/hourglass.png differ diff --git a/public/images/emoji/twitter/hourglass_flowing_sand.png b/public/images/emoji/twitter/hourglass_flowing_sand.png new file mode 100644 index 0000000000..91bc5ab832 Binary files /dev/null and b/public/images/emoji/twitter/hourglass_flowing_sand.png differ diff --git a/public/images/emoji/twitter/house.png b/public/images/emoji/twitter/house.png new file mode 100644 index 0000000000..ca181c9939 Binary files /dev/null and b/public/images/emoji/twitter/house.png differ diff --git a/public/images/emoji/twitter/house_with_garden.png b/public/images/emoji/twitter/house_with_garden.png new file mode 100644 index 0000000000..40e09fb8d0 Binary files /dev/null and b/public/images/emoji/twitter/house_with_garden.png differ diff --git a/public/images/emoji/twitter/hushed.png b/public/images/emoji/twitter/hushed.png new file mode 100644 index 0000000000..7df7660877 Binary files /dev/null and b/public/images/emoji/twitter/hushed.png differ diff --git a/public/images/emoji/twitter/ice_cream.png b/public/images/emoji/twitter/ice_cream.png new file mode 100644 index 0000000000..93123a28a5 Binary files /dev/null and b/public/images/emoji/twitter/ice_cream.png differ diff --git a/public/images/emoji/twitter/icecream.png b/public/images/emoji/twitter/icecream.png new file mode 100644 index 0000000000..9554000f00 Binary files /dev/null and b/public/images/emoji/twitter/icecream.png differ diff --git a/public/images/emoji/twitter/id.png b/public/images/emoji/twitter/id.png new file mode 100644 index 0000000000..b16bb031fa Binary files /dev/null and b/public/images/emoji/twitter/id.png differ diff --git a/public/images/emoji/twitter/ideograph_advantage.png b/public/images/emoji/twitter/ideograph_advantage.png new file mode 100644 index 0000000000..b29464fc21 Binary files /dev/null and b/public/images/emoji/twitter/ideograph_advantage.png differ diff --git a/public/images/emoji/twitter/imp.png b/public/images/emoji/twitter/imp.png new file mode 100644 index 0000000000..682809ce1a Binary files /dev/null and b/public/images/emoji/twitter/imp.png differ diff --git a/public/images/emoji/twitter/inbox_tray.png b/public/images/emoji/twitter/inbox_tray.png new file mode 100644 index 0000000000..a316547a94 Binary files /dev/null and b/public/images/emoji/twitter/inbox_tray.png differ diff --git a/public/images/emoji/twitter/incoming_envelope.png b/public/images/emoji/twitter/incoming_envelope.png new file mode 100644 index 0000000000..b54d6d40f3 Binary files /dev/null and b/public/images/emoji/twitter/incoming_envelope.png differ diff --git a/public/images/emoji/twitter/information_desk_person.png b/public/images/emoji/twitter/information_desk_person.png new file mode 100644 index 0000000000..d5a8e98892 Binary files /dev/null and b/public/images/emoji/twitter/information_desk_person.png differ diff --git a/public/images/emoji/twitter/information_source.png b/public/images/emoji/twitter/information_source.png new file mode 100644 index 0000000000..18df7aa44e Binary files /dev/null and b/public/images/emoji/twitter/information_source.png differ diff --git a/public/images/emoji/twitter/innocent.png b/public/images/emoji/twitter/innocent.png new file mode 100644 index 0000000000..76d9bc4265 Binary files /dev/null and b/public/images/emoji/twitter/innocent.png differ diff --git a/public/images/emoji/twitter/interrobang.png b/public/images/emoji/twitter/interrobang.png new file mode 100644 index 0000000000..99a7e68bc6 Binary files /dev/null and b/public/images/emoji/twitter/interrobang.png differ diff --git a/public/images/emoji/twitter/iphone.png b/public/images/emoji/twitter/iphone.png new file mode 100644 index 0000000000..9e04b16d9c Binary files /dev/null and b/public/images/emoji/twitter/iphone.png differ diff --git a/public/images/emoji/twitter/it.png b/public/images/emoji/twitter/it.png new file mode 100644 index 0000000000..fc876eb18f Binary files /dev/null and b/public/images/emoji/twitter/it.png differ diff --git a/public/images/emoji/twitter/izakaya_lantern.png b/public/images/emoji/twitter/izakaya_lantern.png new file mode 100644 index 0000000000..b0db1cbed6 Binary files /dev/null and b/public/images/emoji/twitter/izakaya_lantern.png differ diff --git a/public/images/emoji/twitter/jack_o_lantern.png b/public/images/emoji/twitter/jack_o_lantern.png new file mode 100644 index 0000000000..2c23eaa9ce Binary files /dev/null and b/public/images/emoji/twitter/jack_o_lantern.png differ diff --git a/public/images/emoji/twitter/japan.png b/public/images/emoji/twitter/japan.png new file mode 100644 index 0000000000..4a0373975f Binary files /dev/null and b/public/images/emoji/twitter/japan.png differ diff --git a/public/images/emoji/twitter/japanese_castle.png b/public/images/emoji/twitter/japanese_castle.png new file mode 100644 index 0000000000..597bd22440 Binary files /dev/null and b/public/images/emoji/twitter/japanese_castle.png differ diff --git a/public/images/emoji/twitter/japanese_goblin.png b/public/images/emoji/twitter/japanese_goblin.png new file mode 100644 index 0000000000..c5204750bc Binary files /dev/null and b/public/images/emoji/twitter/japanese_goblin.png differ diff --git a/public/images/emoji/twitter/japanese_ogre.png b/public/images/emoji/twitter/japanese_ogre.png new file mode 100644 index 0000000000..01fae2768a Binary files /dev/null and b/public/images/emoji/twitter/japanese_ogre.png differ diff --git a/public/images/emoji/twitter/jeans.png b/public/images/emoji/twitter/jeans.png new file mode 100644 index 0000000000..2d60a872d9 Binary files /dev/null and b/public/images/emoji/twitter/jeans.png differ diff --git a/public/images/emoji/twitter/joy.png b/public/images/emoji/twitter/joy.png new file mode 100644 index 0000000000..b2a2037a0e Binary files /dev/null and b/public/images/emoji/twitter/joy.png differ diff --git a/public/images/emoji/twitter/joy_cat.png b/public/images/emoji/twitter/joy_cat.png new file mode 100644 index 0000000000..dbd4f34b75 Binary files /dev/null and b/public/images/emoji/twitter/joy_cat.png differ diff --git a/public/images/emoji/twitter/jp.png b/public/images/emoji/twitter/jp.png new file mode 100644 index 0000000000..64967f96ca Binary files /dev/null and b/public/images/emoji/twitter/jp.png differ diff --git a/public/images/emoji/twitter/key.png b/public/images/emoji/twitter/key.png new file mode 100644 index 0000000000..e7be37b6d3 Binary files /dev/null and b/public/images/emoji/twitter/key.png differ diff --git a/public/images/emoji/twitter/keycap_ten.png b/public/images/emoji/twitter/keycap_ten.png new file mode 100644 index 0000000000..60ef2a92dc Binary files /dev/null and b/public/images/emoji/twitter/keycap_ten.png differ diff --git a/public/images/emoji/twitter/kimono.png b/public/images/emoji/twitter/kimono.png new file mode 100644 index 0000000000..fbb9d5d518 Binary files /dev/null and b/public/images/emoji/twitter/kimono.png differ diff --git a/public/images/emoji/twitter/kiss.png b/public/images/emoji/twitter/kiss.png new file mode 100644 index 0000000000..aff0652a00 Binary files /dev/null and b/public/images/emoji/twitter/kiss.png differ diff --git a/public/images/emoji/twitter/kissing.png b/public/images/emoji/twitter/kissing.png new file mode 100644 index 0000000000..12d18f7c0b Binary files /dev/null and b/public/images/emoji/twitter/kissing.png differ diff --git a/public/images/emoji/twitter/kissing_cat.png b/public/images/emoji/twitter/kissing_cat.png new file mode 100644 index 0000000000..4ecc4e3fe4 Binary files /dev/null and b/public/images/emoji/twitter/kissing_cat.png differ diff --git a/public/images/emoji/twitter/kissing_closed_eyes.png b/public/images/emoji/twitter/kissing_closed_eyes.png new file mode 100644 index 0000000000..8ebaff085a Binary files /dev/null and b/public/images/emoji/twitter/kissing_closed_eyes.png differ diff --git a/public/images/emoji/twitter/kissing_heart.png b/public/images/emoji/twitter/kissing_heart.png new file mode 100644 index 0000000000..ed2db332fd Binary files /dev/null and b/public/images/emoji/twitter/kissing_heart.png differ diff --git a/public/images/emoji/twitter/kissing_smiling_eyes.png b/public/images/emoji/twitter/kissing_smiling_eyes.png new file mode 100644 index 0000000000..871680f68a Binary files /dev/null and b/public/images/emoji/twitter/kissing_smiling_eyes.png differ diff --git a/public/images/emoji/twitter/knife.png b/public/images/emoji/twitter/knife.png new file mode 100644 index 0000000000..1aae17f5b5 Binary files /dev/null and b/public/images/emoji/twitter/knife.png differ diff --git a/public/images/emoji/twitter/koala.png b/public/images/emoji/twitter/koala.png new file mode 100644 index 0000000000..7dc6ffc0d8 Binary files /dev/null and b/public/images/emoji/twitter/koala.png differ diff --git a/public/images/emoji/twitter/koko.png b/public/images/emoji/twitter/koko.png new file mode 100644 index 0000000000..cdfe2da910 Binary files /dev/null and b/public/images/emoji/twitter/koko.png differ diff --git a/public/images/emoji/twitter/kr.png b/public/images/emoji/twitter/kr.png new file mode 100644 index 0000000000..95d9d1f8ae Binary files /dev/null and b/public/images/emoji/twitter/kr.png differ diff --git a/public/images/emoji/twitter/lantern.png b/public/images/emoji/twitter/lantern.png new file mode 100644 index 0000000000..b0db1cbed6 Binary files /dev/null and b/public/images/emoji/twitter/lantern.png differ diff --git a/public/images/emoji/twitter/large_blue_circle.png b/public/images/emoji/twitter/large_blue_circle.png new file mode 100644 index 0000000000..c88d962c76 Binary files /dev/null and b/public/images/emoji/twitter/large_blue_circle.png differ diff --git a/public/images/emoji/twitter/large_blue_diamond.png b/public/images/emoji/twitter/large_blue_diamond.png new file mode 100644 index 0000000000..6132b0ae5e Binary files /dev/null and b/public/images/emoji/twitter/large_blue_diamond.png differ diff --git a/public/images/emoji/twitter/large_orange_diamond.png b/public/images/emoji/twitter/large_orange_diamond.png new file mode 100644 index 0000000000..fe248ded8b Binary files /dev/null and b/public/images/emoji/twitter/large_orange_diamond.png differ diff --git a/public/images/emoji/twitter/last_quarter_moon.png b/public/images/emoji/twitter/last_quarter_moon.png new file mode 100644 index 0000000000..e8df8a06e4 Binary files /dev/null and b/public/images/emoji/twitter/last_quarter_moon.png differ diff --git a/public/images/emoji/twitter/last_quarter_moon_with_face.png b/public/images/emoji/twitter/last_quarter_moon_with_face.png new file mode 100644 index 0000000000..8fd4b780d9 Binary files /dev/null and b/public/images/emoji/twitter/last_quarter_moon_with_face.png differ diff --git a/public/images/emoji/twitter/laughing.png b/public/images/emoji/twitter/laughing.png new file mode 100644 index 0000000000..fa350e829e Binary files /dev/null and b/public/images/emoji/twitter/laughing.png differ diff --git a/public/images/emoji/twitter/leaves.png b/public/images/emoji/twitter/leaves.png new file mode 100644 index 0000000000..b5d4212569 Binary files /dev/null and b/public/images/emoji/twitter/leaves.png differ diff --git a/public/images/emoji/twitter/ledger.png b/public/images/emoji/twitter/ledger.png new file mode 100644 index 0000000000..065fd009a2 Binary files /dev/null and b/public/images/emoji/twitter/ledger.png differ diff --git a/public/images/emoji/twitter/left_luggage.png b/public/images/emoji/twitter/left_luggage.png new file mode 100644 index 0000000000..abb4056828 Binary files /dev/null and b/public/images/emoji/twitter/left_luggage.png differ diff --git a/public/images/emoji/twitter/left_right_arrow.png b/public/images/emoji/twitter/left_right_arrow.png new file mode 100644 index 0000000000..189d061eb2 Binary files /dev/null and b/public/images/emoji/twitter/left_right_arrow.png differ diff --git a/public/images/emoji/twitter/leftwards_arrow_with_hook.png b/public/images/emoji/twitter/leftwards_arrow_with_hook.png new file mode 100644 index 0000000000..959519d64f Binary files /dev/null and b/public/images/emoji/twitter/leftwards_arrow_with_hook.png differ diff --git a/public/images/emoji/twitter/lemon.png b/public/images/emoji/twitter/lemon.png new file mode 100644 index 0000000000..e36b801191 Binary files /dev/null and b/public/images/emoji/twitter/lemon.png differ diff --git a/public/images/emoji/twitter/leo.png b/public/images/emoji/twitter/leo.png new file mode 100644 index 0000000000..d2d00b2f6c Binary files /dev/null and b/public/images/emoji/twitter/leo.png differ diff --git a/public/images/emoji/twitter/leopard.png b/public/images/emoji/twitter/leopard.png new file mode 100644 index 0000000000..61f54b7035 Binary files /dev/null and b/public/images/emoji/twitter/leopard.png differ diff --git a/public/images/emoji/twitter/libra.png b/public/images/emoji/twitter/libra.png new file mode 100644 index 0000000000..8033638220 Binary files /dev/null and b/public/images/emoji/twitter/libra.png differ diff --git a/public/images/emoji/twitter/light_rail.png b/public/images/emoji/twitter/light_rail.png new file mode 100644 index 0000000000..9644a1448e Binary files /dev/null and b/public/images/emoji/twitter/light_rail.png differ diff --git a/public/images/emoji/twitter/link.png b/public/images/emoji/twitter/link.png new file mode 100644 index 0000000000..aec60045be Binary files /dev/null and b/public/images/emoji/twitter/link.png differ diff --git a/public/images/emoji/twitter/lips.png b/public/images/emoji/twitter/lips.png new file mode 100644 index 0000000000..bf071d3454 Binary files /dev/null and b/public/images/emoji/twitter/lips.png differ diff --git a/public/images/emoji/twitter/lipstick.png b/public/images/emoji/twitter/lipstick.png new file mode 100644 index 0000000000..cd66dc73d4 Binary files /dev/null and b/public/images/emoji/twitter/lipstick.png differ diff --git a/public/images/emoji/twitter/lock.png b/public/images/emoji/twitter/lock.png new file mode 100644 index 0000000000..0ca7847e76 Binary files /dev/null and b/public/images/emoji/twitter/lock.png differ diff --git a/public/images/emoji/twitter/lock_with_ink_pen.png b/public/images/emoji/twitter/lock_with_ink_pen.png new file mode 100644 index 0000000000..4545d84e6b Binary files /dev/null and b/public/images/emoji/twitter/lock_with_ink_pen.png differ diff --git a/public/images/emoji/twitter/lollipop.png b/public/images/emoji/twitter/lollipop.png new file mode 100644 index 0000000000..8ee842079c Binary files /dev/null and b/public/images/emoji/twitter/lollipop.png differ diff --git a/public/images/emoji/twitter/loop.png b/public/images/emoji/twitter/loop.png new file mode 100644 index 0000000000..84531b512e Binary files /dev/null and b/public/images/emoji/twitter/loop.png differ diff --git a/public/images/emoji/twitter/loud_sound.png b/public/images/emoji/twitter/loud_sound.png new file mode 100644 index 0000000000..5223a4fdf0 Binary files /dev/null and b/public/images/emoji/twitter/loud_sound.png differ diff --git a/public/images/emoji/twitter/loudspeaker.png b/public/images/emoji/twitter/loudspeaker.png new file mode 100644 index 0000000000..07a48a075b Binary files /dev/null and b/public/images/emoji/twitter/loudspeaker.png differ diff --git a/public/images/emoji/twitter/love_hotel.png b/public/images/emoji/twitter/love_hotel.png new file mode 100644 index 0000000000..361bd8b90a Binary files /dev/null and b/public/images/emoji/twitter/love_hotel.png differ diff --git a/public/images/emoji/twitter/love_letter.png b/public/images/emoji/twitter/love_letter.png new file mode 100644 index 0000000000..f21cb7ce09 Binary files /dev/null and b/public/images/emoji/twitter/love_letter.png differ diff --git a/public/images/emoji/twitter/low_brightness.png b/public/images/emoji/twitter/low_brightness.png new file mode 100644 index 0000000000..3793de1799 Binary files /dev/null and b/public/images/emoji/twitter/low_brightness.png differ diff --git a/public/images/emoji/twitter/m.png b/public/images/emoji/twitter/m.png new file mode 100644 index 0000000000..194552e9a1 Binary files /dev/null and b/public/images/emoji/twitter/m.png differ diff --git a/public/images/emoji/twitter/mag.png b/public/images/emoji/twitter/mag.png new file mode 100644 index 0000000000..7b0029e578 Binary files /dev/null and b/public/images/emoji/twitter/mag.png differ diff --git a/public/images/emoji/twitter/mag_right.png b/public/images/emoji/twitter/mag_right.png new file mode 100644 index 0000000000..768ea43482 Binary files /dev/null and b/public/images/emoji/twitter/mag_right.png differ diff --git a/public/images/emoji/twitter/mahjong.png b/public/images/emoji/twitter/mahjong.png new file mode 100644 index 0000000000..0bd4418bb3 Binary files /dev/null and b/public/images/emoji/twitter/mahjong.png differ diff --git a/public/images/emoji/twitter/mailbox.png b/public/images/emoji/twitter/mailbox.png new file mode 100644 index 0000000000..a4971adecb Binary files /dev/null and b/public/images/emoji/twitter/mailbox.png differ diff --git a/public/images/emoji/twitter/mailbox_closed.png b/public/images/emoji/twitter/mailbox_closed.png new file mode 100644 index 0000000000..4cd28cf556 Binary files /dev/null and b/public/images/emoji/twitter/mailbox_closed.png differ diff --git a/public/images/emoji/twitter/mailbox_with_mail.png b/public/images/emoji/twitter/mailbox_with_mail.png new file mode 100644 index 0000000000..e2d7887c7f Binary files /dev/null and b/public/images/emoji/twitter/mailbox_with_mail.png differ diff --git a/public/images/emoji/twitter/mailbox_with_no_mail.png b/public/images/emoji/twitter/mailbox_with_no_mail.png new file mode 100644 index 0000000000..9fa2c61b61 Binary files /dev/null and b/public/images/emoji/twitter/mailbox_with_no_mail.png differ diff --git a/public/images/emoji/twitter/man.png b/public/images/emoji/twitter/man.png new file mode 100644 index 0000000000..e10fc1521c Binary files /dev/null and b/public/images/emoji/twitter/man.png differ diff --git a/public/images/emoji/twitter/man_with_gua_pi_mao.png b/public/images/emoji/twitter/man_with_gua_pi_mao.png new file mode 100644 index 0000000000..fc3a90d2de Binary files /dev/null and b/public/images/emoji/twitter/man_with_gua_pi_mao.png differ diff --git a/public/images/emoji/twitter/man_with_turban.png b/public/images/emoji/twitter/man_with_turban.png new file mode 100644 index 0000000000..22d5ac6b44 Binary files /dev/null and b/public/images/emoji/twitter/man_with_turban.png differ diff --git a/public/images/emoji/twitter/mans_shoe.png b/public/images/emoji/twitter/mans_shoe.png new file mode 100644 index 0000000000..71d4be2c33 Binary files /dev/null and b/public/images/emoji/twitter/mans_shoe.png differ diff --git a/public/images/emoji/twitter/maple_leaf.png b/public/images/emoji/twitter/maple_leaf.png new file mode 100644 index 0000000000..97faddd01c Binary files /dev/null and b/public/images/emoji/twitter/maple_leaf.png differ diff --git a/public/images/emoji/twitter/mask.png b/public/images/emoji/twitter/mask.png new file mode 100644 index 0000000000..f10360e5f6 Binary files /dev/null and b/public/images/emoji/twitter/mask.png differ diff --git a/public/images/emoji/twitter/massage.png b/public/images/emoji/twitter/massage.png new file mode 100644 index 0000000000..8d72618c54 Binary files /dev/null and b/public/images/emoji/twitter/massage.png differ diff --git a/public/images/emoji/twitter/meat_on_bone.png b/public/images/emoji/twitter/meat_on_bone.png new file mode 100644 index 0000000000..33a16690a5 Binary files /dev/null and b/public/images/emoji/twitter/meat_on_bone.png differ diff --git a/public/images/emoji/twitter/mega.png b/public/images/emoji/twitter/mega.png new file mode 100644 index 0000000000..d77e10cc9f Binary files /dev/null and b/public/images/emoji/twitter/mega.png differ diff --git a/public/images/emoji/twitter/melon.png b/public/images/emoji/twitter/melon.png new file mode 100644 index 0000000000..d5243e0c9e Binary files /dev/null and b/public/images/emoji/twitter/melon.png differ diff --git a/public/images/emoji/twitter/memo.png b/public/images/emoji/twitter/memo.png new file mode 100644 index 0000000000..9bbc624eb0 Binary files /dev/null and b/public/images/emoji/twitter/memo.png differ diff --git a/public/images/emoji/twitter/mens.png b/public/images/emoji/twitter/mens.png new file mode 100644 index 0000000000..3b84cf01cb Binary files /dev/null and b/public/images/emoji/twitter/mens.png differ diff --git a/public/images/emoji/twitter/metro.png b/public/images/emoji/twitter/metro.png new file mode 100644 index 0000000000..319b6428d2 Binary files /dev/null and b/public/images/emoji/twitter/metro.png differ diff --git a/public/images/emoji/twitter/microphone.png b/public/images/emoji/twitter/microphone.png new file mode 100644 index 0000000000..3e909125b1 Binary files /dev/null and b/public/images/emoji/twitter/microphone.png differ diff --git a/public/images/emoji/twitter/microscope.png b/public/images/emoji/twitter/microscope.png new file mode 100644 index 0000000000..d06f145d9f Binary files /dev/null and b/public/images/emoji/twitter/microscope.png differ diff --git a/public/images/emoji/twitter/milky_way.png b/public/images/emoji/twitter/milky_way.png new file mode 100644 index 0000000000..d8bc86ba7c Binary files /dev/null and b/public/images/emoji/twitter/milky_way.png differ diff --git a/public/images/emoji/twitter/minibus.png b/public/images/emoji/twitter/minibus.png new file mode 100644 index 0000000000..592058cd58 Binary files /dev/null and b/public/images/emoji/twitter/minibus.png differ diff --git a/public/images/emoji/twitter/minidisc.png b/public/images/emoji/twitter/minidisc.png new file mode 100644 index 0000000000..d7e1bea69a Binary files /dev/null and b/public/images/emoji/twitter/minidisc.png differ diff --git a/public/images/emoji/twitter/mobile_phone_off.png b/public/images/emoji/twitter/mobile_phone_off.png new file mode 100644 index 0000000000..ae6eddf731 Binary files /dev/null and b/public/images/emoji/twitter/mobile_phone_off.png differ diff --git a/public/images/emoji/twitter/money_with_wings.png b/public/images/emoji/twitter/money_with_wings.png new file mode 100644 index 0000000000..cef4d0d53d Binary files /dev/null and b/public/images/emoji/twitter/money_with_wings.png differ diff --git a/public/images/emoji/twitter/moneybag.png b/public/images/emoji/twitter/moneybag.png new file mode 100644 index 0000000000..21fd16a1a3 Binary files /dev/null and b/public/images/emoji/twitter/moneybag.png differ diff --git a/public/images/emoji/twitter/monkey.png b/public/images/emoji/twitter/monkey.png new file mode 100644 index 0000000000..47aa819783 Binary files /dev/null and b/public/images/emoji/twitter/monkey.png differ diff --git a/public/images/emoji/twitter/monkey_face.png b/public/images/emoji/twitter/monkey_face.png new file mode 100644 index 0000000000..bd19d05d9c Binary files /dev/null and b/public/images/emoji/twitter/monkey_face.png differ diff --git a/public/images/emoji/twitter/monorail.png b/public/images/emoji/twitter/monorail.png new file mode 100644 index 0000000000..1887dd9e7d Binary files /dev/null and b/public/images/emoji/twitter/monorail.png differ diff --git a/public/images/emoji/twitter/moon.png b/public/images/emoji/twitter/moon.png new file mode 100644 index 0000000000..319561d710 Binary files /dev/null and b/public/images/emoji/twitter/moon.png differ diff --git a/public/images/emoji/twitter/mortar_board.png b/public/images/emoji/twitter/mortar_board.png new file mode 100644 index 0000000000..f50bb04119 Binary files /dev/null and b/public/images/emoji/twitter/mortar_board.png differ diff --git a/public/images/emoji/twitter/mount_fuji.png b/public/images/emoji/twitter/mount_fuji.png new file mode 100644 index 0000000000..1a5fe70d2c Binary files /dev/null and b/public/images/emoji/twitter/mount_fuji.png differ diff --git a/public/images/emoji/twitter/mountain_bicyclist.png b/public/images/emoji/twitter/mountain_bicyclist.png new file mode 100644 index 0000000000..d9bc79c759 Binary files /dev/null and b/public/images/emoji/twitter/mountain_bicyclist.png differ diff --git a/public/images/emoji/twitter/mountain_cableway.png b/public/images/emoji/twitter/mountain_cableway.png new file mode 100644 index 0000000000..0d5fb274c7 Binary files /dev/null and b/public/images/emoji/twitter/mountain_cableway.png differ diff --git a/public/images/emoji/twitter/mountain_railway.png b/public/images/emoji/twitter/mountain_railway.png new file mode 100644 index 0000000000..1b3d6b8593 Binary files /dev/null and b/public/images/emoji/twitter/mountain_railway.png differ diff --git a/public/images/emoji/twitter/mouse.png b/public/images/emoji/twitter/mouse.png new file mode 100644 index 0000000000..0c10a90bd1 Binary files /dev/null and b/public/images/emoji/twitter/mouse.png differ diff --git a/public/images/emoji/twitter/mouse2.png b/public/images/emoji/twitter/mouse2.png new file mode 100644 index 0000000000..0f91255e3f Binary files /dev/null and b/public/images/emoji/twitter/mouse2.png differ diff --git a/public/images/emoji/twitter/movie_camera.png b/public/images/emoji/twitter/movie_camera.png new file mode 100644 index 0000000000..084e37772e Binary files /dev/null and b/public/images/emoji/twitter/movie_camera.png differ diff --git a/public/images/emoji/twitter/moyai.png b/public/images/emoji/twitter/moyai.png new file mode 100644 index 0000000000..4e711f2152 Binary files /dev/null and b/public/images/emoji/twitter/moyai.png differ diff --git a/public/images/emoji/twitter/muscle.png b/public/images/emoji/twitter/muscle.png new file mode 100644 index 0000000000..34fa6c5538 Binary files /dev/null and b/public/images/emoji/twitter/muscle.png differ diff --git a/public/images/emoji/twitter/mushroom.png b/public/images/emoji/twitter/mushroom.png new file mode 100644 index 0000000000..c5ac4e5338 Binary files /dev/null and b/public/images/emoji/twitter/mushroom.png differ diff --git a/public/images/emoji/twitter/musical_keyboard.png b/public/images/emoji/twitter/musical_keyboard.png new file mode 100644 index 0000000000..2c579cef7b Binary files /dev/null and b/public/images/emoji/twitter/musical_keyboard.png differ diff --git a/public/images/emoji/twitter/musical_note.png b/public/images/emoji/twitter/musical_note.png new file mode 100644 index 0000000000..16ebcca7d6 Binary files /dev/null and b/public/images/emoji/twitter/musical_note.png differ diff --git a/public/images/emoji/twitter/musical_score.png b/public/images/emoji/twitter/musical_score.png new file mode 100644 index 0000000000..a381d646a2 Binary files /dev/null and b/public/images/emoji/twitter/musical_score.png differ diff --git a/public/images/emoji/twitter/mute.png b/public/images/emoji/twitter/mute.png new file mode 100644 index 0000000000..e32805db1a Binary files /dev/null and b/public/images/emoji/twitter/mute.png differ diff --git a/public/images/emoji/twitter/nail_care.png b/public/images/emoji/twitter/nail_care.png new file mode 100644 index 0000000000..26218548ab Binary files /dev/null and b/public/images/emoji/twitter/nail_care.png differ diff --git a/public/images/emoji/twitter/name_badge.png b/public/images/emoji/twitter/name_badge.png new file mode 100644 index 0000000000..d2f06bdfc9 Binary files /dev/null and b/public/images/emoji/twitter/name_badge.png differ diff --git a/public/images/emoji/twitter/necktie.png b/public/images/emoji/twitter/necktie.png new file mode 100644 index 0000000000..c4a1381dd3 Binary files /dev/null and b/public/images/emoji/twitter/necktie.png differ diff --git a/public/images/emoji/twitter/negative_squared_cross_mark.png b/public/images/emoji/twitter/negative_squared_cross_mark.png new file mode 100644 index 0000000000..7906549a9d Binary files /dev/null and b/public/images/emoji/twitter/negative_squared_cross_mark.png differ diff --git a/public/images/emoji/twitter/neutral_face.png b/public/images/emoji/twitter/neutral_face.png new file mode 100644 index 0000000000..2cfa8c444f Binary files /dev/null and b/public/images/emoji/twitter/neutral_face.png differ diff --git a/public/images/emoji/twitter/new.png b/public/images/emoji/twitter/new.png new file mode 100644 index 0000000000..276537a398 Binary files /dev/null and b/public/images/emoji/twitter/new.png differ diff --git a/public/images/emoji/twitter/new_moon.png b/public/images/emoji/twitter/new_moon.png new file mode 100644 index 0000000000..6d497dc114 Binary files /dev/null and b/public/images/emoji/twitter/new_moon.png differ diff --git a/public/images/emoji/twitter/new_moon_with_face.png b/public/images/emoji/twitter/new_moon_with_face.png new file mode 100644 index 0000000000..ebceb6b1cc Binary files /dev/null and b/public/images/emoji/twitter/new_moon_with_face.png differ diff --git a/public/images/emoji/twitter/newspaper.png b/public/images/emoji/twitter/newspaper.png new file mode 100644 index 0000000000..41c5f765e4 Binary files /dev/null and b/public/images/emoji/twitter/newspaper.png differ diff --git a/public/images/emoji/twitter/ng.png b/public/images/emoji/twitter/ng.png new file mode 100644 index 0000000000..f8e409a867 Binary files /dev/null and b/public/images/emoji/twitter/ng.png differ diff --git a/public/images/emoji/twitter/night_with_stars.png b/public/images/emoji/twitter/night_with_stars.png new file mode 100644 index 0000000000..0635fe3d28 Binary files /dev/null and b/public/images/emoji/twitter/night_with_stars.png differ diff --git a/public/images/emoji/twitter/nine.png b/public/images/emoji/twitter/nine.png new file mode 100644 index 0000000000..ba757742cb Binary files /dev/null and b/public/images/emoji/twitter/nine.png differ diff --git a/public/images/emoji/twitter/no_bell.png b/public/images/emoji/twitter/no_bell.png new file mode 100644 index 0000000000..fdb2c84b64 Binary files /dev/null and b/public/images/emoji/twitter/no_bell.png differ diff --git a/public/images/emoji/twitter/no_bicycles.png b/public/images/emoji/twitter/no_bicycles.png new file mode 100644 index 0000000000..84f5ef0d2c Binary files /dev/null and b/public/images/emoji/twitter/no_bicycles.png differ diff --git a/public/images/emoji/twitter/no_entry.png b/public/images/emoji/twitter/no_entry.png new file mode 100644 index 0000000000..2f119b691a Binary files /dev/null and b/public/images/emoji/twitter/no_entry.png differ diff --git a/public/images/emoji/twitter/no_entry_sign.png b/public/images/emoji/twitter/no_entry_sign.png new file mode 100644 index 0000000000..89d8c80b54 Binary files /dev/null and b/public/images/emoji/twitter/no_entry_sign.png differ diff --git a/public/images/emoji/twitter/no_good.png b/public/images/emoji/twitter/no_good.png new file mode 100644 index 0000000000..9869f61e33 Binary files /dev/null and b/public/images/emoji/twitter/no_good.png differ diff --git a/public/images/emoji/twitter/no_mobile_phones.png b/public/images/emoji/twitter/no_mobile_phones.png new file mode 100644 index 0000000000..d9f18020d0 Binary files /dev/null and b/public/images/emoji/twitter/no_mobile_phones.png differ diff --git a/public/images/emoji/twitter/no_mouth.png b/public/images/emoji/twitter/no_mouth.png new file mode 100644 index 0000000000..1ce9b90a8b Binary files /dev/null and b/public/images/emoji/twitter/no_mouth.png differ diff --git a/public/images/emoji/twitter/no_pedestrians.png b/public/images/emoji/twitter/no_pedestrians.png new file mode 100644 index 0000000000..a5b334b508 Binary files /dev/null and b/public/images/emoji/twitter/no_pedestrians.png differ diff --git a/public/images/emoji/twitter/no_smoking.png b/public/images/emoji/twitter/no_smoking.png new file mode 100644 index 0000000000..44deedbac9 Binary files /dev/null and b/public/images/emoji/twitter/no_smoking.png differ diff --git a/public/images/emoji/twitter/non-potable_water.png b/public/images/emoji/twitter/non-potable_water.png new file mode 100644 index 0000000000..7073d006ee Binary files /dev/null and b/public/images/emoji/twitter/non-potable_water.png differ diff --git a/public/images/emoji/twitter/nose.png b/public/images/emoji/twitter/nose.png new file mode 100644 index 0000000000..0ebca56495 Binary files /dev/null and b/public/images/emoji/twitter/nose.png differ diff --git a/public/images/emoji/twitter/notebook.png b/public/images/emoji/twitter/notebook.png new file mode 100644 index 0000000000..c9a88cbb4e Binary files /dev/null and b/public/images/emoji/twitter/notebook.png differ diff --git a/public/images/emoji/twitter/notebook_with_decorative_cover.png b/public/images/emoji/twitter/notebook_with_decorative_cover.png new file mode 100644 index 0000000000..d856c5bbf3 Binary files /dev/null and b/public/images/emoji/twitter/notebook_with_decorative_cover.png differ diff --git a/public/images/emoji/twitter/notes.png b/public/images/emoji/twitter/notes.png new file mode 100644 index 0000000000..a3cbb555c2 Binary files /dev/null and b/public/images/emoji/twitter/notes.png differ diff --git a/public/images/emoji/twitter/nut_and_bolt.png b/public/images/emoji/twitter/nut_and_bolt.png new file mode 100644 index 0000000000..18e6093344 Binary files /dev/null and b/public/images/emoji/twitter/nut_and_bolt.png differ diff --git a/public/images/emoji/twitter/o.png b/public/images/emoji/twitter/o.png new file mode 100644 index 0000000000..fc938e887e Binary files /dev/null and b/public/images/emoji/twitter/o.png differ diff --git a/public/images/emoji/twitter/o2.png b/public/images/emoji/twitter/o2.png new file mode 100644 index 0000000000..9d601e39da Binary files /dev/null and b/public/images/emoji/twitter/o2.png differ diff --git a/public/images/emoji/twitter/ocean.png b/public/images/emoji/twitter/ocean.png new file mode 100644 index 0000000000..8da365de18 Binary files /dev/null and b/public/images/emoji/twitter/ocean.png differ diff --git a/public/images/emoji/twitter/octopus.png b/public/images/emoji/twitter/octopus.png new file mode 100644 index 0000000000..2ef3384087 Binary files /dev/null and b/public/images/emoji/twitter/octopus.png differ diff --git a/public/images/emoji/twitter/oden.png b/public/images/emoji/twitter/oden.png new file mode 100644 index 0000000000..2859686c06 Binary files /dev/null and b/public/images/emoji/twitter/oden.png differ diff --git a/public/images/emoji/twitter/office.png b/public/images/emoji/twitter/office.png new file mode 100644 index 0000000000..b991af54dd Binary files /dev/null and b/public/images/emoji/twitter/office.png differ diff --git a/public/images/emoji/twitter/ok.png b/public/images/emoji/twitter/ok.png new file mode 100644 index 0000000000..0820262bed Binary files /dev/null and b/public/images/emoji/twitter/ok.png differ diff --git a/public/images/emoji/twitter/ok_hand.png b/public/images/emoji/twitter/ok_hand.png new file mode 100644 index 0000000000..fb8ea8e73a Binary files /dev/null and b/public/images/emoji/twitter/ok_hand.png differ diff --git a/public/images/emoji/twitter/ok_woman.png b/public/images/emoji/twitter/ok_woman.png new file mode 100644 index 0000000000..d072795cd9 Binary files /dev/null and b/public/images/emoji/twitter/ok_woman.png differ diff --git a/public/images/emoji/twitter/older_man.png b/public/images/emoji/twitter/older_man.png new file mode 100644 index 0000000000..9ace4c880e Binary files /dev/null and b/public/images/emoji/twitter/older_man.png differ diff --git a/public/images/emoji/twitter/older_woman.png b/public/images/emoji/twitter/older_woman.png new file mode 100644 index 0000000000..2b2c9f47d3 Binary files /dev/null and b/public/images/emoji/twitter/older_woman.png differ diff --git a/public/images/emoji/twitter/on.png b/public/images/emoji/twitter/on.png new file mode 100644 index 0000000000..1e1645555d Binary files /dev/null and b/public/images/emoji/twitter/on.png differ diff --git a/public/images/emoji/twitter/oncoming_automobile.png b/public/images/emoji/twitter/oncoming_automobile.png new file mode 100644 index 0000000000..32c5364955 Binary files /dev/null and b/public/images/emoji/twitter/oncoming_automobile.png differ diff --git a/public/images/emoji/twitter/oncoming_bus.png b/public/images/emoji/twitter/oncoming_bus.png new file mode 100644 index 0000000000..6a83467842 Binary files /dev/null and b/public/images/emoji/twitter/oncoming_bus.png differ diff --git a/public/images/emoji/twitter/oncoming_police_car.png b/public/images/emoji/twitter/oncoming_police_car.png new file mode 100644 index 0000000000..291162ca57 Binary files /dev/null and b/public/images/emoji/twitter/oncoming_police_car.png differ diff --git a/public/images/emoji/twitter/oncoming_taxi.png b/public/images/emoji/twitter/oncoming_taxi.png new file mode 100644 index 0000000000..acb9814692 Binary files /dev/null and b/public/images/emoji/twitter/oncoming_taxi.png differ diff --git a/public/images/emoji/twitter/one.png b/public/images/emoji/twitter/one.png new file mode 100644 index 0000000000..1155e53a51 Binary files /dev/null and b/public/images/emoji/twitter/one.png differ diff --git a/public/images/emoji/twitter/open_book.png b/public/images/emoji/twitter/open_book.png new file mode 100644 index 0000000000..da48082842 Binary files /dev/null and b/public/images/emoji/twitter/open_book.png differ diff --git a/public/images/emoji/twitter/open_file_folder.png b/public/images/emoji/twitter/open_file_folder.png new file mode 100644 index 0000000000..cc2d2f3eba Binary files /dev/null and b/public/images/emoji/twitter/open_file_folder.png differ diff --git a/public/images/emoji/twitter/open_hands.png b/public/images/emoji/twitter/open_hands.png new file mode 100644 index 0000000000..54ce8e260a Binary files /dev/null and b/public/images/emoji/twitter/open_hands.png differ diff --git a/public/images/emoji/twitter/open_mouth.png b/public/images/emoji/twitter/open_mouth.png new file mode 100644 index 0000000000..f079cebcbc Binary files /dev/null and b/public/images/emoji/twitter/open_mouth.png differ diff --git a/public/images/emoji/twitter/ophiuchus.png b/public/images/emoji/twitter/ophiuchus.png new file mode 100644 index 0000000000..718308fff6 Binary files /dev/null and b/public/images/emoji/twitter/ophiuchus.png differ diff --git a/public/images/emoji/twitter/orange_book.png b/public/images/emoji/twitter/orange_book.png new file mode 100644 index 0000000000..25dc2e153b Binary files /dev/null and b/public/images/emoji/twitter/orange_book.png differ diff --git a/public/images/emoji/twitter/outbox_tray.png b/public/images/emoji/twitter/outbox_tray.png new file mode 100644 index 0000000000..1243a9c787 Binary files /dev/null and b/public/images/emoji/twitter/outbox_tray.png differ diff --git a/public/images/emoji/twitter/ox.png b/public/images/emoji/twitter/ox.png new file mode 100644 index 0000000000..0404a21d6d Binary files /dev/null and b/public/images/emoji/twitter/ox.png differ diff --git a/public/images/emoji/twitter/package.png b/public/images/emoji/twitter/package.png new file mode 100644 index 0000000000..f707f1242b Binary files /dev/null and b/public/images/emoji/twitter/package.png differ diff --git a/public/images/emoji/twitter/page_facing_up.png b/public/images/emoji/twitter/page_facing_up.png new file mode 100644 index 0000000000..4927daf5bd Binary files /dev/null and b/public/images/emoji/twitter/page_facing_up.png differ diff --git a/public/images/emoji/twitter/page_with_curl.png b/public/images/emoji/twitter/page_with_curl.png new file mode 100644 index 0000000000..10e3219b9d Binary files /dev/null and b/public/images/emoji/twitter/page_with_curl.png differ diff --git a/public/images/emoji/twitter/pager.png b/public/images/emoji/twitter/pager.png new file mode 100644 index 0000000000..f1ae64ce33 Binary files /dev/null and b/public/images/emoji/twitter/pager.png differ diff --git a/public/images/emoji/twitter/palm_tree.png b/public/images/emoji/twitter/palm_tree.png new file mode 100644 index 0000000000..918ed0c4e7 Binary files /dev/null and b/public/images/emoji/twitter/palm_tree.png differ diff --git a/public/images/emoji/twitter/panda_face.png b/public/images/emoji/twitter/panda_face.png new file mode 100644 index 0000000000..59cdd2a7ae Binary files /dev/null and b/public/images/emoji/twitter/panda_face.png differ diff --git a/public/images/emoji/twitter/paperclip.png b/public/images/emoji/twitter/paperclip.png new file mode 100644 index 0000000000..0a92e4219d Binary files /dev/null and b/public/images/emoji/twitter/paperclip.png differ diff --git a/public/images/emoji/twitter/parking.png b/public/images/emoji/twitter/parking.png new file mode 100644 index 0000000000..efbfbebd9f Binary files /dev/null and b/public/images/emoji/twitter/parking.png differ diff --git a/public/images/emoji/twitter/part_alternation_mark.png b/public/images/emoji/twitter/part_alternation_mark.png new file mode 100644 index 0000000000..a73ddf7767 Binary files /dev/null and b/public/images/emoji/twitter/part_alternation_mark.png differ diff --git a/public/images/emoji/twitter/partly_sunny.png b/public/images/emoji/twitter/partly_sunny.png new file mode 100644 index 0000000000..0d42818d19 Binary files /dev/null and b/public/images/emoji/twitter/partly_sunny.png differ diff --git a/public/images/emoji/twitter/passport_control.png b/public/images/emoji/twitter/passport_control.png new file mode 100644 index 0000000000..328551726e Binary files /dev/null and b/public/images/emoji/twitter/passport_control.png differ diff --git a/public/images/emoji/twitter/paw_prints.png b/public/images/emoji/twitter/paw_prints.png new file mode 100644 index 0000000000..94deaa1e55 Binary files /dev/null and b/public/images/emoji/twitter/paw_prints.png differ diff --git a/public/images/emoji/twitter/peach.png b/public/images/emoji/twitter/peach.png new file mode 100644 index 0000000000..92fa321311 Binary files /dev/null and b/public/images/emoji/twitter/peach.png differ diff --git a/public/images/emoji/twitter/pear.png b/public/images/emoji/twitter/pear.png new file mode 100644 index 0000000000..4a6ad8fc45 Binary files /dev/null and b/public/images/emoji/twitter/pear.png differ diff --git a/public/images/emoji/twitter/pencil.png b/public/images/emoji/twitter/pencil.png new file mode 100644 index 0000000000..9bbc624eb0 Binary files /dev/null and b/public/images/emoji/twitter/pencil.png differ diff --git a/public/images/emoji/twitter/pencil2.png b/public/images/emoji/twitter/pencil2.png new file mode 100644 index 0000000000..a469366006 Binary files /dev/null and b/public/images/emoji/twitter/pencil2.png differ diff --git a/public/images/emoji/twitter/penguin.png b/public/images/emoji/twitter/penguin.png new file mode 100644 index 0000000000..931349517c Binary files /dev/null and b/public/images/emoji/twitter/penguin.png differ diff --git a/public/images/emoji/twitter/pensive.png b/public/images/emoji/twitter/pensive.png new file mode 100644 index 0000000000..b43a3de295 Binary files /dev/null and b/public/images/emoji/twitter/pensive.png differ diff --git a/public/images/emoji/twitter/performing_arts.png b/public/images/emoji/twitter/performing_arts.png new file mode 100644 index 0000000000..08412f8606 Binary files /dev/null and b/public/images/emoji/twitter/performing_arts.png differ diff --git a/public/images/emoji/twitter/persevere.png b/public/images/emoji/twitter/persevere.png new file mode 100644 index 0000000000..2f44a83177 Binary files /dev/null and b/public/images/emoji/twitter/persevere.png differ diff --git a/public/images/emoji/twitter/person_frowning.png b/public/images/emoji/twitter/person_frowning.png new file mode 100644 index 0000000000..9cc0a968bb Binary files /dev/null and b/public/images/emoji/twitter/person_frowning.png differ diff --git a/public/images/emoji/twitter/person_with_blond_hair.png b/public/images/emoji/twitter/person_with_blond_hair.png new file mode 100644 index 0000000000..15dbacbd70 Binary files /dev/null and b/public/images/emoji/twitter/person_with_blond_hair.png differ diff --git a/public/images/emoji/twitter/person_with_pouting_face.png b/public/images/emoji/twitter/person_with_pouting_face.png new file mode 100644 index 0000000000..6f8b40e3a7 Binary files /dev/null and b/public/images/emoji/twitter/person_with_pouting_face.png differ diff --git a/public/images/emoji/twitter/phone.png b/public/images/emoji/twitter/phone.png new file mode 100644 index 0000000000..3d636ba8c5 Binary files /dev/null and b/public/images/emoji/twitter/phone.png differ diff --git a/public/images/emoji/twitter/pig.png b/public/images/emoji/twitter/pig.png new file mode 100644 index 0000000000..c57ac4fdb9 Binary files /dev/null and b/public/images/emoji/twitter/pig.png differ diff --git a/public/images/emoji/twitter/pig2.png b/public/images/emoji/twitter/pig2.png new file mode 100644 index 0000000000..2492c5628f Binary files /dev/null and b/public/images/emoji/twitter/pig2.png differ diff --git a/public/images/emoji/twitter/pig_nose.png b/public/images/emoji/twitter/pig_nose.png new file mode 100644 index 0000000000..a78d4e9ee7 Binary files /dev/null and b/public/images/emoji/twitter/pig_nose.png differ diff --git a/public/images/emoji/twitter/pill.png b/public/images/emoji/twitter/pill.png new file mode 100644 index 0000000000..a9def2fd03 Binary files /dev/null and b/public/images/emoji/twitter/pill.png differ diff --git a/public/images/emoji/twitter/pineapple.png b/public/images/emoji/twitter/pineapple.png new file mode 100644 index 0000000000..4e6cf2615e Binary files /dev/null and b/public/images/emoji/twitter/pineapple.png differ diff --git a/public/images/emoji/twitter/pisces.png b/public/images/emoji/twitter/pisces.png new file mode 100644 index 0000000000..274daa24af Binary files /dev/null and b/public/images/emoji/twitter/pisces.png differ diff --git a/public/images/emoji/twitter/pizza.png b/public/images/emoji/twitter/pizza.png new file mode 100644 index 0000000000..eaa5f87938 Binary files /dev/null and b/public/images/emoji/twitter/pizza.png differ diff --git a/public/images/emoji/twitter/point_down.png b/public/images/emoji/twitter/point_down.png new file mode 100644 index 0000000000..275970ffce Binary files /dev/null and b/public/images/emoji/twitter/point_down.png differ diff --git a/public/images/emoji/twitter/point_left.png b/public/images/emoji/twitter/point_left.png new file mode 100644 index 0000000000..fe1c9e2a9b Binary files /dev/null and b/public/images/emoji/twitter/point_left.png differ diff --git a/public/images/emoji/twitter/point_right.png b/public/images/emoji/twitter/point_right.png new file mode 100644 index 0000000000..54b368ac63 Binary files /dev/null and b/public/images/emoji/twitter/point_right.png differ diff --git a/public/images/emoji/twitter/point_up.png b/public/images/emoji/twitter/point_up.png new file mode 100644 index 0000000000..8f13a38f16 Binary files /dev/null and b/public/images/emoji/twitter/point_up.png differ diff --git a/public/images/emoji/twitter/point_up_2.png b/public/images/emoji/twitter/point_up_2.png new file mode 100644 index 0000000000..374c6fb799 Binary files /dev/null and b/public/images/emoji/twitter/point_up_2.png differ diff --git a/public/images/emoji/twitter/police_car.png b/public/images/emoji/twitter/police_car.png new file mode 100644 index 0000000000..7829c80129 Binary files /dev/null and b/public/images/emoji/twitter/police_car.png differ diff --git a/public/images/emoji/twitter/poodle.png b/public/images/emoji/twitter/poodle.png new file mode 100644 index 0000000000..6e77dc3f09 Binary files /dev/null and b/public/images/emoji/twitter/poodle.png differ diff --git a/public/images/emoji/twitter/poop.png b/public/images/emoji/twitter/poop.png new file mode 100644 index 0000000000..604d2a2d0a Binary files /dev/null and b/public/images/emoji/twitter/poop.png differ diff --git a/public/images/emoji/twitter/post_office.png b/public/images/emoji/twitter/post_office.png new file mode 100644 index 0000000000..aeecd2f65f Binary files /dev/null and b/public/images/emoji/twitter/post_office.png differ diff --git a/public/images/emoji/twitter/postal_horn.png b/public/images/emoji/twitter/postal_horn.png new file mode 100644 index 0000000000..01008d8283 Binary files /dev/null and b/public/images/emoji/twitter/postal_horn.png differ diff --git a/public/images/emoji/twitter/postbox.png b/public/images/emoji/twitter/postbox.png new file mode 100644 index 0000000000..fbff07974f Binary files /dev/null and b/public/images/emoji/twitter/postbox.png differ diff --git a/public/images/emoji/twitter/potable_water.png b/public/images/emoji/twitter/potable_water.png new file mode 100644 index 0000000000..5d8b61f82d Binary files /dev/null and b/public/images/emoji/twitter/potable_water.png differ diff --git a/public/images/emoji/twitter/pouch.png b/public/images/emoji/twitter/pouch.png new file mode 100644 index 0000000000..926368a804 Binary files /dev/null and b/public/images/emoji/twitter/pouch.png differ diff --git a/public/images/emoji/twitter/poultry_leg.png b/public/images/emoji/twitter/poultry_leg.png new file mode 100644 index 0000000000..e4140ec393 Binary files /dev/null and b/public/images/emoji/twitter/poultry_leg.png differ diff --git a/public/images/emoji/twitter/pound.png b/public/images/emoji/twitter/pound.png new file mode 100644 index 0000000000..a8156dc4f2 Binary files /dev/null and b/public/images/emoji/twitter/pound.png differ diff --git a/public/images/emoji/twitter/pouting_cat.png b/public/images/emoji/twitter/pouting_cat.png new file mode 100644 index 0000000000..77a4195fdc Binary files /dev/null and b/public/images/emoji/twitter/pouting_cat.png differ diff --git a/public/images/emoji/twitter/pray.png b/public/images/emoji/twitter/pray.png new file mode 100644 index 0000000000..e866b14496 Binary files /dev/null and b/public/images/emoji/twitter/pray.png differ diff --git a/public/images/emoji/twitter/princess.png b/public/images/emoji/twitter/princess.png new file mode 100644 index 0000000000..3830c501e5 Binary files /dev/null and b/public/images/emoji/twitter/princess.png differ diff --git a/public/images/emoji/twitter/punch.png b/public/images/emoji/twitter/punch.png new file mode 100644 index 0000000000..b41129276f Binary files /dev/null and b/public/images/emoji/twitter/punch.png differ diff --git a/public/images/emoji/twitter/purple_heart.png b/public/images/emoji/twitter/purple_heart.png new file mode 100644 index 0000000000..f63e727394 Binary files /dev/null and b/public/images/emoji/twitter/purple_heart.png differ diff --git a/public/images/emoji/twitter/purse.png b/public/images/emoji/twitter/purse.png new file mode 100644 index 0000000000..76717fbc71 Binary files /dev/null and b/public/images/emoji/twitter/purse.png differ diff --git a/public/images/emoji/twitter/pushpin.png b/public/images/emoji/twitter/pushpin.png new file mode 100644 index 0000000000..5e6faac1df Binary files /dev/null and b/public/images/emoji/twitter/pushpin.png differ diff --git a/public/images/emoji/twitter/put_litter_in_its_place.png b/public/images/emoji/twitter/put_litter_in_its_place.png new file mode 100644 index 0000000000..fdf4bb6db9 Binary files /dev/null and b/public/images/emoji/twitter/put_litter_in_its_place.png differ diff --git a/public/images/emoji/twitter/question.png b/public/images/emoji/twitter/question.png new file mode 100644 index 0000000000..fc0680b3cd Binary files /dev/null and b/public/images/emoji/twitter/question.png differ diff --git a/public/images/emoji/twitter/rabbit.png b/public/images/emoji/twitter/rabbit.png new file mode 100644 index 0000000000..a75b275827 Binary files /dev/null and b/public/images/emoji/twitter/rabbit.png differ diff --git a/public/images/emoji/twitter/rabbit2.png b/public/images/emoji/twitter/rabbit2.png new file mode 100644 index 0000000000..07d14a83f4 Binary files /dev/null and b/public/images/emoji/twitter/rabbit2.png differ diff --git a/public/images/emoji/twitter/racehorse.png b/public/images/emoji/twitter/racehorse.png new file mode 100644 index 0000000000..9cff0b935d Binary files /dev/null and b/public/images/emoji/twitter/racehorse.png differ diff --git a/public/images/emoji/twitter/radio.png b/public/images/emoji/twitter/radio.png new file mode 100644 index 0000000000..1b54f54e03 Binary files /dev/null and b/public/images/emoji/twitter/radio.png differ diff --git a/public/images/emoji/twitter/radio_button.png b/public/images/emoji/twitter/radio_button.png new file mode 100644 index 0000000000..54d091f5c0 Binary files /dev/null and b/public/images/emoji/twitter/radio_button.png differ diff --git a/public/images/emoji/twitter/rage.png b/public/images/emoji/twitter/rage.png new file mode 100644 index 0000000000..c1ab5d1fd9 Binary files /dev/null and b/public/images/emoji/twitter/rage.png differ diff --git a/public/images/emoji/twitter/railway_car.png b/public/images/emoji/twitter/railway_car.png new file mode 100644 index 0000000000..e4dbd4f882 Binary files /dev/null and b/public/images/emoji/twitter/railway_car.png differ diff --git a/public/images/emoji/twitter/rainbow.png b/public/images/emoji/twitter/rainbow.png new file mode 100644 index 0000000000..d0aaec8aa2 Binary files /dev/null and b/public/images/emoji/twitter/rainbow.png differ diff --git a/public/images/emoji/twitter/raised_hand.png b/public/images/emoji/twitter/raised_hand.png new file mode 100644 index 0000000000..f7edfd6c3d Binary files /dev/null and b/public/images/emoji/twitter/raised_hand.png differ diff --git a/public/images/emoji/twitter/raised_hands.png b/public/images/emoji/twitter/raised_hands.png new file mode 100644 index 0000000000..bb0bcd8ad9 Binary files /dev/null and b/public/images/emoji/twitter/raised_hands.png differ diff --git a/public/images/emoji/twitter/raising_hand.png b/public/images/emoji/twitter/raising_hand.png new file mode 100644 index 0000000000..3d23df8810 Binary files /dev/null and b/public/images/emoji/twitter/raising_hand.png differ diff --git a/public/images/emoji/twitter/ram.png b/public/images/emoji/twitter/ram.png new file mode 100644 index 0000000000..9259b3c35a Binary files /dev/null and b/public/images/emoji/twitter/ram.png differ diff --git a/public/images/emoji/twitter/ramen.png b/public/images/emoji/twitter/ramen.png new file mode 100644 index 0000000000..577b4a5c04 Binary files /dev/null and b/public/images/emoji/twitter/ramen.png differ diff --git a/public/images/emoji/twitter/rat.png b/public/images/emoji/twitter/rat.png new file mode 100644 index 0000000000..5c577cf5cb Binary files /dev/null and b/public/images/emoji/twitter/rat.png differ diff --git a/public/images/emoji/twitter/recycle.png b/public/images/emoji/twitter/recycle.png new file mode 100644 index 0000000000..568b5459a7 Binary files /dev/null and b/public/images/emoji/twitter/recycle.png differ diff --git a/public/images/emoji/twitter/red_car.png b/public/images/emoji/twitter/red_car.png new file mode 100644 index 0000000000..93610d2c43 Binary files /dev/null and b/public/images/emoji/twitter/red_car.png differ diff --git a/public/images/emoji/twitter/red_circle.png b/public/images/emoji/twitter/red_circle.png new file mode 100644 index 0000000000..90103e1e70 Binary files /dev/null and b/public/images/emoji/twitter/red_circle.png differ diff --git a/public/images/emoji/twitter/registered.png b/public/images/emoji/twitter/registered.png new file mode 100644 index 0000000000..8761856480 Binary files /dev/null and b/public/images/emoji/twitter/registered.png differ diff --git a/public/images/emoji/twitter/relaxed.png b/public/images/emoji/twitter/relaxed.png new file mode 100644 index 0000000000..5206089b60 Binary files /dev/null and b/public/images/emoji/twitter/relaxed.png differ diff --git a/public/images/emoji/twitter/relieved.png b/public/images/emoji/twitter/relieved.png new file mode 100644 index 0000000000..d3743ebc2c Binary files /dev/null and b/public/images/emoji/twitter/relieved.png differ diff --git a/public/images/emoji/twitter/repeat.png b/public/images/emoji/twitter/repeat.png new file mode 100644 index 0000000000..89fcc1f232 Binary files /dev/null and b/public/images/emoji/twitter/repeat.png differ diff --git a/public/images/emoji/twitter/repeat_one.png b/public/images/emoji/twitter/repeat_one.png new file mode 100644 index 0000000000..d8cba319ca Binary files /dev/null and b/public/images/emoji/twitter/repeat_one.png differ diff --git a/public/images/emoji/twitter/restroom.png b/public/images/emoji/twitter/restroom.png new file mode 100644 index 0000000000..df75a32071 Binary files /dev/null and b/public/images/emoji/twitter/restroom.png differ diff --git a/public/images/emoji/twitter/revolving_hearts.png b/public/images/emoji/twitter/revolving_hearts.png new file mode 100644 index 0000000000..fcfb0c3504 Binary files /dev/null and b/public/images/emoji/twitter/revolving_hearts.png differ diff --git a/public/images/emoji/twitter/rewind.png b/public/images/emoji/twitter/rewind.png new file mode 100644 index 0000000000..09aba5b223 Binary files /dev/null and b/public/images/emoji/twitter/rewind.png differ diff --git a/public/images/emoji/twitter/ribbon.png b/public/images/emoji/twitter/ribbon.png new file mode 100644 index 0000000000..530137dbc2 Binary files /dev/null and b/public/images/emoji/twitter/ribbon.png differ diff --git a/public/images/emoji/twitter/rice.png b/public/images/emoji/twitter/rice.png new file mode 100644 index 0000000000..d4824bfa78 Binary files /dev/null and b/public/images/emoji/twitter/rice.png differ diff --git a/public/images/emoji/twitter/rice_ball.png b/public/images/emoji/twitter/rice_ball.png new file mode 100644 index 0000000000..b5ad453ebd Binary files /dev/null and b/public/images/emoji/twitter/rice_ball.png differ diff --git a/public/images/emoji/twitter/rice_cracker.png b/public/images/emoji/twitter/rice_cracker.png new file mode 100644 index 0000000000..0be2e5c481 Binary files /dev/null and b/public/images/emoji/twitter/rice_cracker.png differ diff --git a/public/images/emoji/twitter/rice_scene.png b/public/images/emoji/twitter/rice_scene.png new file mode 100644 index 0000000000..56f23076d4 Binary files /dev/null and b/public/images/emoji/twitter/rice_scene.png differ diff --git a/public/images/emoji/twitter/ring.png b/public/images/emoji/twitter/ring.png new file mode 100644 index 0000000000..b46e092e54 Binary files /dev/null and b/public/images/emoji/twitter/ring.png differ diff --git a/public/images/emoji/twitter/rocket.png b/public/images/emoji/twitter/rocket.png new file mode 100644 index 0000000000..041fe59866 Binary files /dev/null and b/public/images/emoji/twitter/rocket.png differ diff --git a/public/images/emoji/twitter/roller_coaster.png b/public/images/emoji/twitter/roller_coaster.png new file mode 100644 index 0000000000..3f95bab1cf Binary files /dev/null and b/public/images/emoji/twitter/roller_coaster.png differ diff --git a/public/images/emoji/twitter/rooster.png b/public/images/emoji/twitter/rooster.png new file mode 100644 index 0000000000..ea5c8564a8 Binary files /dev/null and b/public/images/emoji/twitter/rooster.png differ diff --git a/public/images/emoji/twitter/rose.png b/public/images/emoji/twitter/rose.png new file mode 100644 index 0000000000..aba7e66516 Binary files /dev/null and b/public/images/emoji/twitter/rose.png differ diff --git a/public/images/emoji/twitter/rotating_light.png b/public/images/emoji/twitter/rotating_light.png new file mode 100644 index 0000000000..950497413a Binary files /dev/null and b/public/images/emoji/twitter/rotating_light.png differ diff --git a/public/images/emoji/twitter/round_pushpin.png b/public/images/emoji/twitter/round_pushpin.png new file mode 100644 index 0000000000..7846f1669b Binary files /dev/null and b/public/images/emoji/twitter/round_pushpin.png differ diff --git a/public/images/emoji/twitter/rowboat.png b/public/images/emoji/twitter/rowboat.png new file mode 100644 index 0000000000..05004b4eef Binary files /dev/null and b/public/images/emoji/twitter/rowboat.png differ diff --git a/public/images/emoji/twitter/ru.png b/public/images/emoji/twitter/ru.png new file mode 100644 index 0000000000..021d32f938 Binary files /dev/null and b/public/images/emoji/twitter/ru.png differ diff --git a/public/images/emoji/twitter/rugby_football.png b/public/images/emoji/twitter/rugby_football.png new file mode 100644 index 0000000000..74aaa25375 Binary files /dev/null and b/public/images/emoji/twitter/rugby_football.png differ diff --git a/public/images/emoji/twitter/runner.png b/public/images/emoji/twitter/runner.png new file mode 100644 index 0000000000..73a678354b Binary files /dev/null and b/public/images/emoji/twitter/runner.png differ diff --git a/public/images/emoji/twitter/running.png b/public/images/emoji/twitter/running.png new file mode 100644 index 0000000000..73a678354b Binary files /dev/null and b/public/images/emoji/twitter/running.png differ diff --git a/public/images/emoji/twitter/running_shirt_with_sash.png b/public/images/emoji/twitter/running_shirt_with_sash.png new file mode 100644 index 0000000000..1e41d23f89 Binary files /dev/null and b/public/images/emoji/twitter/running_shirt_with_sash.png differ diff --git a/public/images/emoji/twitter/sa.png b/public/images/emoji/twitter/sa.png new file mode 100644 index 0000000000..6f981401b1 Binary files /dev/null and b/public/images/emoji/twitter/sa.png differ diff --git a/public/images/emoji/twitter/sagittarius.png b/public/images/emoji/twitter/sagittarius.png new file mode 100644 index 0000000000..68d964ca99 Binary files /dev/null and b/public/images/emoji/twitter/sagittarius.png differ diff --git a/public/images/emoji/twitter/sailboat.png b/public/images/emoji/twitter/sailboat.png new file mode 100644 index 0000000000..7b4a289ed2 Binary files /dev/null and b/public/images/emoji/twitter/sailboat.png differ diff --git a/public/images/emoji/twitter/sake.png b/public/images/emoji/twitter/sake.png new file mode 100644 index 0000000000..c705943afe Binary files /dev/null and b/public/images/emoji/twitter/sake.png differ diff --git a/public/images/emoji/twitter/sandal.png b/public/images/emoji/twitter/sandal.png new file mode 100644 index 0000000000..73bbff56af Binary files /dev/null and b/public/images/emoji/twitter/sandal.png differ diff --git a/public/images/emoji/twitter/santa.png b/public/images/emoji/twitter/santa.png new file mode 100644 index 0000000000..8d38564629 Binary files /dev/null and b/public/images/emoji/twitter/santa.png differ diff --git a/public/images/emoji/twitter/satellite.png b/public/images/emoji/twitter/satellite.png new file mode 100644 index 0000000000..a83aa3e259 Binary files /dev/null and b/public/images/emoji/twitter/satellite.png differ diff --git a/public/images/emoji/twitter/satisfied.png b/public/images/emoji/twitter/satisfied.png new file mode 100644 index 0000000000..fa350e829e Binary files /dev/null and b/public/images/emoji/twitter/satisfied.png differ diff --git a/public/images/emoji/twitter/saxophone.png b/public/images/emoji/twitter/saxophone.png new file mode 100644 index 0000000000..4cc798d0f1 Binary files /dev/null and b/public/images/emoji/twitter/saxophone.png differ diff --git a/public/images/emoji/twitter/school.png b/public/images/emoji/twitter/school.png new file mode 100644 index 0000000000..78b6ad844b Binary files /dev/null and b/public/images/emoji/twitter/school.png differ diff --git a/public/images/emoji/twitter/school_satchel.png b/public/images/emoji/twitter/school_satchel.png new file mode 100644 index 0000000000..5f8295fb23 Binary files /dev/null and b/public/images/emoji/twitter/school_satchel.png differ diff --git a/public/images/emoji/twitter/scissors.png b/public/images/emoji/twitter/scissors.png new file mode 100644 index 0000000000..12d809df3f Binary files /dev/null and b/public/images/emoji/twitter/scissors.png differ diff --git a/public/images/emoji/twitter/scorpius.png b/public/images/emoji/twitter/scorpius.png new file mode 100644 index 0000000000..17b213184f Binary files /dev/null and b/public/images/emoji/twitter/scorpius.png differ diff --git a/public/images/emoji/twitter/scream.png b/public/images/emoji/twitter/scream.png new file mode 100644 index 0000000000..0167a57b72 Binary files /dev/null and b/public/images/emoji/twitter/scream.png differ diff --git a/public/images/emoji/twitter/scream_cat.png b/public/images/emoji/twitter/scream_cat.png new file mode 100644 index 0000000000..6f009c49df Binary files /dev/null and b/public/images/emoji/twitter/scream_cat.png differ diff --git a/public/images/emoji/twitter/scroll.png b/public/images/emoji/twitter/scroll.png new file mode 100644 index 0000000000..c973d3cb98 Binary files /dev/null and b/public/images/emoji/twitter/scroll.png differ diff --git a/public/images/emoji/twitter/seat.png b/public/images/emoji/twitter/seat.png new file mode 100644 index 0000000000..4f42f6212e Binary files /dev/null and b/public/images/emoji/twitter/seat.png differ diff --git a/public/images/emoji/twitter/secret.png b/public/images/emoji/twitter/secret.png new file mode 100644 index 0000000000..d64ecfabd2 Binary files /dev/null and b/public/images/emoji/twitter/secret.png differ diff --git a/public/images/emoji/twitter/see_no_evil.png b/public/images/emoji/twitter/see_no_evil.png new file mode 100644 index 0000000000..e3ee674da8 Binary files /dev/null and b/public/images/emoji/twitter/see_no_evil.png differ diff --git a/public/images/emoji/twitter/seedling.png b/public/images/emoji/twitter/seedling.png new file mode 100644 index 0000000000..e2afd3fc73 Binary files /dev/null and b/public/images/emoji/twitter/seedling.png differ diff --git a/public/images/emoji/twitter/seven.png b/public/images/emoji/twitter/seven.png new file mode 100644 index 0000000000..9d846a9878 Binary files /dev/null and b/public/images/emoji/twitter/seven.png differ diff --git a/public/images/emoji/twitter/shaved_ice.png b/public/images/emoji/twitter/shaved_ice.png new file mode 100644 index 0000000000..40e7e673ae Binary files /dev/null and b/public/images/emoji/twitter/shaved_ice.png differ diff --git a/public/images/emoji/twitter/sheep.png b/public/images/emoji/twitter/sheep.png new file mode 100644 index 0000000000..9a02e6b6f4 Binary files /dev/null and b/public/images/emoji/twitter/sheep.png differ diff --git a/public/images/emoji/twitter/shell.png b/public/images/emoji/twitter/shell.png new file mode 100644 index 0000000000..e9efae186e Binary files /dev/null and b/public/images/emoji/twitter/shell.png differ diff --git a/public/images/emoji/twitter/ship.png b/public/images/emoji/twitter/ship.png new file mode 100644 index 0000000000..bad0411e1f Binary files /dev/null and b/public/images/emoji/twitter/ship.png differ diff --git a/public/images/emoji/twitter/shirt.png b/public/images/emoji/twitter/shirt.png new file mode 100644 index 0000000000..bf78d21c06 Binary files /dev/null and b/public/images/emoji/twitter/shirt.png differ diff --git a/public/images/emoji/twitter/shit.png b/public/images/emoji/twitter/shit.png new file mode 100644 index 0000000000..604d2a2d0a Binary files /dev/null and b/public/images/emoji/twitter/shit.png differ diff --git a/public/images/emoji/twitter/shoe.png b/public/images/emoji/twitter/shoe.png new file mode 100644 index 0000000000..71d4be2c33 Binary files /dev/null and b/public/images/emoji/twitter/shoe.png differ diff --git a/public/images/emoji/twitter/shower.png b/public/images/emoji/twitter/shower.png new file mode 100644 index 0000000000..9a448a4905 Binary files /dev/null and b/public/images/emoji/twitter/shower.png differ diff --git a/public/images/emoji/twitter/signal_strength.png b/public/images/emoji/twitter/signal_strength.png new file mode 100644 index 0000000000..7ba93b1657 Binary files /dev/null and b/public/images/emoji/twitter/signal_strength.png differ diff --git a/public/images/emoji/twitter/six.png b/public/images/emoji/twitter/six.png new file mode 100644 index 0000000000..a79b45b739 Binary files /dev/null and b/public/images/emoji/twitter/six.png differ diff --git a/public/images/emoji/twitter/six_pointed_star.png b/public/images/emoji/twitter/six_pointed_star.png new file mode 100644 index 0000000000..148a7090cd Binary files /dev/null and b/public/images/emoji/twitter/six_pointed_star.png differ diff --git a/public/images/emoji/twitter/ski.png b/public/images/emoji/twitter/ski.png new file mode 100644 index 0000000000..84d05c7dee Binary files /dev/null and b/public/images/emoji/twitter/ski.png differ diff --git a/public/images/emoji/twitter/skull.png b/public/images/emoji/twitter/skull.png new file mode 100644 index 0000000000..9573b3f73a Binary files /dev/null and b/public/images/emoji/twitter/skull.png differ diff --git a/public/images/emoji/twitter/sleeping.png b/public/images/emoji/twitter/sleeping.png new file mode 100644 index 0000000000..f8aca5e443 Binary files /dev/null and b/public/images/emoji/twitter/sleeping.png differ diff --git a/public/images/emoji/twitter/sleepy.png b/public/images/emoji/twitter/sleepy.png new file mode 100644 index 0000000000..adbe0bc6a7 Binary files /dev/null and b/public/images/emoji/twitter/sleepy.png differ diff --git a/public/images/emoji/twitter/slot_machine.png b/public/images/emoji/twitter/slot_machine.png new file mode 100644 index 0000000000..cfdc70a0fd Binary files /dev/null and b/public/images/emoji/twitter/slot_machine.png differ diff --git a/public/images/emoji/twitter/small_blue_diamond.png b/public/images/emoji/twitter/small_blue_diamond.png new file mode 100644 index 0000000000..f7bddb0dff Binary files /dev/null and b/public/images/emoji/twitter/small_blue_diamond.png differ diff --git a/public/images/emoji/twitter/small_orange_diamond.png b/public/images/emoji/twitter/small_orange_diamond.png new file mode 100644 index 0000000000..c0770290fd Binary files /dev/null and b/public/images/emoji/twitter/small_orange_diamond.png differ diff --git a/public/images/emoji/twitter/small_red_triangle.png b/public/images/emoji/twitter/small_red_triangle.png new file mode 100644 index 0000000000..cbfc5377da Binary files /dev/null and b/public/images/emoji/twitter/small_red_triangle.png differ diff --git a/public/images/emoji/twitter/small_red_triangle_down.png b/public/images/emoji/twitter/small_red_triangle_down.png new file mode 100644 index 0000000000..3aa3db5f65 Binary files /dev/null and b/public/images/emoji/twitter/small_red_triangle_down.png differ diff --git a/public/images/emoji/twitter/smile.png b/public/images/emoji/twitter/smile.png new file mode 100644 index 0000000000..642c90ccbc Binary files /dev/null and b/public/images/emoji/twitter/smile.png differ diff --git a/public/images/emoji/twitter/smile_cat.png b/public/images/emoji/twitter/smile_cat.png new file mode 100644 index 0000000000..0461f8cc01 Binary files /dev/null and b/public/images/emoji/twitter/smile_cat.png differ diff --git a/public/images/emoji/twitter/smiley.png b/public/images/emoji/twitter/smiley.png new file mode 100644 index 0000000000..add5433807 Binary files /dev/null and b/public/images/emoji/twitter/smiley.png differ diff --git a/public/images/emoji/twitter/smiley_cat.png b/public/images/emoji/twitter/smiley_cat.png new file mode 100644 index 0000000000..d8d42a8ca2 Binary files /dev/null and b/public/images/emoji/twitter/smiley_cat.png differ diff --git a/public/images/emoji/twitter/smiling_imp.png b/public/images/emoji/twitter/smiling_imp.png new file mode 100644 index 0000000000..66a8653d01 Binary files /dev/null and b/public/images/emoji/twitter/smiling_imp.png differ diff --git a/public/images/emoji/twitter/smirk.png b/public/images/emoji/twitter/smirk.png new file mode 100644 index 0000000000..70240ea5ba Binary files /dev/null and b/public/images/emoji/twitter/smirk.png differ diff --git a/public/images/emoji/twitter/smirk_cat.png b/public/images/emoji/twitter/smirk_cat.png new file mode 100644 index 0000000000..24f9337a52 Binary files /dev/null and b/public/images/emoji/twitter/smirk_cat.png differ diff --git a/public/images/emoji/twitter/smoking.png b/public/images/emoji/twitter/smoking.png new file mode 100644 index 0000000000..2dd81c268f Binary files /dev/null and b/public/images/emoji/twitter/smoking.png differ diff --git a/public/images/emoji/twitter/snail.png b/public/images/emoji/twitter/snail.png new file mode 100644 index 0000000000..95994f978a Binary files /dev/null and b/public/images/emoji/twitter/snail.png differ diff --git a/public/images/emoji/twitter/snake.png b/public/images/emoji/twitter/snake.png new file mode 100644 index 0000000000..511d1ed9ed Binary files /dev/null and b/public/images/emoji/twitter/snake.png differ diff --git a/public/images/emoji/twitter/snowboarder.png b/public/images/emoji/twitter/snowboarder.png new file mode 100644 index 0000000000..25d831f3ed Binary files /dev/null and b/public/images/emoji/twitter/snowboarder.png differ diff --git a/public/images/emoji/twitter/snowflake.png b/public/images/emoji/twitter/snowflake.png new file mode 100644 index 0000000000..f028132c84 Binary files /dev/null and b/public/images/emoji/twitter/snowflake.png differ diff --git a/public/images/emoji/twitter/snowman.png b/public/images/emoji/twitter/snowman.png new file mode 100644 index 0000000000..21be238667 Binary files /dev/null and b/public/images/emoji/twitter/snowman.png differ diff --git a/public/images/emoji/twitter/sob.png b/public/images/emoji/twitter/sob.png new file mode 100644 index 0000000000..a6d73884f2 Binary files /dev/null and b/public/images/emoji/twitter/sob.png differ diff --git a/public/images/emoji/twitter/soccer.png b/public/images/emoji/twitter/soccer.png new file mode 100644 index 0000000000..c306eced70 Binary files /dev/null and b/public/images/emoji/twitter/soccer.png differ diff --git a/public/images/emoji/twitter/soon.png b/public/images/emoji/twitter/soon.png new file mode 100644 index 0000000000..eac4c59d95 Binary files /dev/null and b/public/images/emoji/twitter/soon.png differ diff --git a/public/images/emoji/twitter/sos.png b/public/images/emoji/twitter/sos.png new file mode 100644 index 0000000000..086755edd4 Binary files /dev/null and b/public/images/emoji/twitter/sos.png differ diff --git a/public/images/emoji/twitter/sound.png b/public/images/emoji/twitter/sound.png new file mode 100644 index 0000000000..e0d7df94b8 Binary files /dev/null and b/public/images/emoji/twitter/sound.png differ diff --git a/public/images/emoji/twitter/space_invader.png b/public/images/emoji/twitter/space_invader.png new file mode 100644 index 0000000000..c714fc2815 Binary files /dev/null and b/public/images/emoji/twitter/space_invader.png differ diff --git a/public/images/emoji/twitter/spades.png b/public/images/emoji/twitter/spades.png new file mode 100644 index 0000000000..b8276037eb Binary files /dev/null and b/public/images/emoji/twitter/spades.png differ diff --git a/public/images/emoji/twitter/spaghetti.png b/public/images/emoji/twitter/spaghetti.png new file mode 100644 index 0000000000..d07ed9ec44 Binary files /dev/null and b/public/images/emoji/twitter/spaghetti.png differ diff --git a/public/images/emoji/twitter/sparkle.png b/public/images/emoji/twitter/sparkle.png new file mode 100644 index 0000000000..d3e0207641 Binary files /dev/null and b/public/images/emoji/twitter/sparkle.png differ diff --git a/public/images/emoji/twitter/sparkler.png b/public/images/emoji/twitter/sparkler.png new file mode 100644 index 0000000000..9c43022b9d Binary files /dev/null and b/public/images/emoji/twitter/sparkler.png differ diff --git a/public/images/emoji/twitter/sparkles.png b/public/images/emoji/twitter/sparkles.png new file mode 100644 index 0000000000..514855cf81 Binary files /dev/null and b/public/images/emoji/twitter/sparkles.png differ diff --git a/public/images/emoji/twitter/sparkling_heart.png b/public/images/emoji/twitter/sparkling_heart.png new file mode 100644 index 0000000000..7221d81d06 Binary files /dev/null and b/public/images/emoji/twitter/sparkling_heart.png differ diff --git a/public/images/emoji/twitter/speak_no_evil.png b/public/images/emoji/twitter/speak_no_evil.png new file mode 100644 index 0000000000..c2061ce899 Binary files /dev/null and b/public/images/emoji/twitter/speak_no_evil.png differ diff --git a/public/images/emoji/twitter/speaker.png b/public/images/emoji/twitter/speaker.png new file mode 100644 index 0000000000..ed651cc72e Binary files /dev/null and b/public/images/emoji/twitter/speaker.png differ diff --git a/public/images/emoji/twitter/speech_balloon.png b/public/images/emoji/twitter/speech_balloon.png new file mode 100644 index 0000000000..a084cdc80b Binary files /dev/null and b/public/images/emoji/twitter/speech_balloon.png differ diff --git a/public/images/emoji/twitter/speedboat.png b/public/images/emoji/twitter/speedboat.png new file mode 100644 index 0000000000..de945ce10a Binary files /dev/null and b/public/images/emoji/twitter/speedboat.png differ diff --git a/public/images/emoji/twitter/star.png b/public/images/emoji/twitter/star.png new file mode 100644 index 0000000000..6e54055529 Binary files /dev/null and b/public/images/emoji/twitter/star.png differ diff --git a/public/images/emoji/twitter/star2.png b/public/images/emoji/twitter/star2.png new file mode 100644 index 0000000000..b3b7f87db7 Binary files /dev/null and b/public/images/emoji/twitter/star2.png differ diff --git a/public/images/emoji/twitter/stars.png b/public/images/emoji/twitter/stars.png new file mode 100644 index 0000000000..61a2907213 Binary files /dev/null and b/public/images/emoji/twitter/stars.png differ diff --git a/public/images/emoji/twitter/station.png b/public/images/emoji/twitter/station.png new file mode 100644 index 0000000000..5921653cc1 Binary files /dev/null and b/public/images/emoji/twitter/station.png differ diff --git a/public/images/emoji/twitter/statue_of_liberty.png b/public/images/emoji/twitter/statue_of_liberty.png new file mode 100644 index 0000000000..e3006b714c Binary files /dev/null and b/public/images/emoji/twitter/statue_of_liberty.png differ diff --git a/public/images/emoji/twitter/steam_locomotive.png b/public/images/emoji/twitter/steam_locomotive.png new file mode 100644 index 0000000000..16f69944f2 Binary files /dev/null and b/public/images/emoji/twitter/steam_locomotive.png differ diff --git a/public/images/emoji/twitter/stew.png b/public/images/emoji/twitter/stew.png new file mode 100644 index 0000000000..96ab289ffa Binary files /dev/null and b/public/images/emoji/twitter/stew.png differ diff --git a/public/images/emoji/twitter/straight_ruler.png b/public/images/emoji/twitter/straight_ruler.png new file mode 100644 index 0000000000..ff4dceac95 Binary files /dev/null and b/public/images/emoji/twitter/straight_ruler.png differ diff --git a/public/images/emoji/twitter/strawberry.png b/public/images/emoji/twitter/strawberry.png new file mode 100644 index 0000000000..8e01de58bb Binary files /dev/null and b/public/images/emoji/twitter/strawberry.png differ diff --git a/public/images/emoji/twitter/stuck_out_tongue.png b/public/images/emoji/twitter/stuck_out_tongue.png new file mode 100644 index 0000000000..f036ed35fa Binary files /dev/null and b/public/images/emoji/twitter/stuck_out_tongue.png differ diff --git a/public/images/emoji/twitter/stuck_out_tongue_closed_eyes.png b/public/images/emoji/twitter/stuck_out_tongue_closed_eyes.png new file mode 100644 index 0000000000..05b69618ae Binary files /dev/null and b/public/images/emoji/twitter/stuck_out_tongue_closed_eyes.png differ diff --git a/public/images/emoji/twitter/stuck_out_tongue_winking_eye.png b/public/images/emoji/twitter/stuck_out_tongue_winking_eye.png new file mode 100644 index 0000000000..00f9976ca9 Binary files /dev/null and b/public/images/emoji/twitter/stuck_out_tongue_winking_eye.png differ diff --git a/public/images/emoji/twitter/sun_with_face.png b/public/images/emoji/twitter/sun_with_face.png new file mode 100644 index 0000000000..78297f9fdb Binary files /dev/null and b/public/images/emoji/twitter/sun_with_face.png differ diff --git a/public/images/emoji/twitter/sunflower.png b/public/images/emoji/twitter/sunflower.png new file mode 100644 index 0000000000..cbe183f49b Binary files /dev/null and b/public/images/emoji/twitter/sunflower.png differ diff --git a/public/images/emoji/twitter/sunglasses.png b/public/images/emoji/twitter/sunglasses.png new file mode 100644 index 0000000000..d11e074d1d Binary files /dev/null and b/public/images/emoji/twitter/sunglasses.png differ diff --git a/public/images/emoji/twitter/sunny.png b/public/images/emoji/twitter/sunny.png new file mode 100644 index 0000000000..ff67294e5f Binary files /dev/null and b/public/images/emoji/twitter/sunny.png differ diff --git a/public/images/emoji/twitter/sunrise.png b/public/images/emoji/twitter/sunrise.png new file mode 100644 index 0000000000..0e77ad0cd1 Binary files /dev/null and b/public/images/emoji/twitter/sunrise.png differ diff --git a/public/images/emoji/twitter/sunrise_over_mountains.png b/public/images/emoji/twitter/sunrise_over_mountains.png new file mode 100644 index 0000000000..c95776b527 Binary files /dev/null and b/public/images/emoji/twitter/sunrise_over_mountains.png differ diff --git a/public/images/emoji/twitter/surfer.png b/public/images/emoji/twitter/surfer.png new file mode 100644 index 0000000000..84e76fa439 Binary files /dev/null and b/public/images/emoji/twitter/surfer.png differ diff --git a/public/images/emoji/twitter/sushi.png b/public/images/emoji/twitter/sushi.png new file mode 100644 index 0000000000..53c053f021 Binary files /dev/null and b/public/images/emoji/twitter/sushi.png differ diff --git a/public/images/emoji/twitter/suspension_railway.png b/public/images/emoji/twitter/suspension_railway.png new file mode 100644 index 0000000000..f357d583a9 Binary files /dev/null and b/public/images/emoji/twitter/suspension_railway.png differ diff --git a/public/images/emoji/twitter/sweat.png b/public/images/emoji/twitter/sweat.png new file mode 100644 index 0000000000..30f8642b86 Binary files /dev/null and b/public/images/emoji/twitter/sweat.png differ diff --git a/public/images/emoji/twitter/sweat_drops.png b/public/images/emoji/twitter/sweat_drops.png new file mode 100644 index 0000000000..6b14c111ef Binary files /dev/null and b/public/images/emoji/twitter/sweat_drops.png differ diff --git a/public/images/emoji/twitter/sweat_smile.png b/public/images/emoji/twitter/sweat_smile.png new file mode 100644 index 0000000000..2401acf351 Binary files /dev/null and b/public/images/emoji/twitter/sweat_smile.png differ diff --git a/public/images/emoji/twitter/sweet_potato.png b/public/images/emoji/twitter/sweet_potato.png new file mode 100644 index 0000000000..014490b42f Binary files /dev/null and b/public/images/emoji/twitter/sweet_potato.png differ diff --git a/public/images/emoji/twitter/swimmer.png b/public/images/emoji/twitter/swimmer.png new file mode 100644 index 0000000000..d15b6cd901 Binary files /dev/null and b/public/images/emoji/twitter/swimmer.png differ diff --git a/public/images/emoji/twitter/symbols.png b/public/images/emoji/twitter/symbols.png new file mode 100644 index 0000000000..21ccc8953b Binary files /dev/null and b/public/images/emoji/twitter/symbols.png differ diff --git a/public/images/emoji/twitter/syringe.png b/public/images/emoji/twitter/syringe.png new file mode 100644 index 0000000000..6f5b363f4b Binary files /dev/null and b/public/images/emoji/twitter/syringe.png differ diff --git a/public/images/emoji/twitter/tada.png b/public/images/emoji/twitter/tada.png new file mode 100644 index 0000000000..26b49592cf Binary files /dev/null and b/public/images/emoji/twitter/tada.png differ diff --git a/public/images/emoji/twitter/tanabata_tree.png b/public/images/emoji/twitter/tanabata_tree.png new file mode 100644 index 0000000000..9e583b9e02 Binary files /dev/null and b/public/images/emoji/twitter/tanabata_tree.png differ diff --git a/public/images/emoji/twitter/tangerine.png b/public/images/emoji/twitter/tangerine.png new file mode 100644 index 0000000000..9d7c349b38 Binary files /dev/null and b/public/images/emoji/twitter/tangerine.png differ diff --git a/public/images/emoji/twitter/taurus.png b/public/images/emoji/twitter/taurus.png new file mode 100644 index 0000000000..413be66bc1 Binary files /dev/null and b/public/images/emoji/twitter/taurus.png differ diff --git a/public/images/emoji/twitter/taxi.png b/public/images/emoji/twitter/taxi.png new file mode 100644 index 0000000000..112a3ad7f5 Binary files /dev/null and b/public/images/emoji/twitter/taxi.png differ diff --git a/public/images/emoji/twitter/tea.png b/public/images/emoji/twitter/tea.png new file mode 100644 index 0000000000..93b986f9e2 Binary files /dev/null and b/public/images/emoji/twitter/tea.png differ diff --git a/public/images/emoji/twitter/telephone.png b/public/images/emoji/twitter/telephone.png new file mode 100644 index 0000000000..3d636ba8c5 Binary files /dev/null and b/public/images/emoji/twitter/telephone.png differ diff --git a/public/images/emoji/twitter/telephone_receiver.png b/public/images/emoji/twitter/telephone_receiver.png new file mode 100644 index 0000000000..2c8bd6a58c Binary files /dev/null and b/public/images/emoji/twitter/telephone_receiver.png differ diff --git a/public/images/emoji/twitter/telescope.png b/public/images/emoji/twitter/telescope.png new file mode 100644 index 0000000000..dc6ab4a6e7 Binary files /dev/null and b/public/images/emoji/twitter/telescope.png differ diff --git a/public/images/emoji/twitter/tennis.png b/public/images/emoji/twitter/tennis.png new file mode 100644 index 0000000000..53749f61f8 Binary files /dev/null and b/public/images/emoji/twitter/tennis.png differ diff --git a/public/images/emoji/twitter/tent.png b/public/images/emoji/twitter/tent.png new file mode 100644 index 0000000000..10f8770763 Binary files /dev/null and b/public/images/emoji/twitter/tent.png differ diff --git a/public/images/emoji/twitter/thought_balloon.png b/public/images/emoji/twitter/thought_balloon.png new file mode 100644 index 0000000000..65b0129676 Binary files /dev/null and b/public/images/emoji/twitter/thought_balloon.png differ diff --git a/public/images/emoji/twitter/three.png b/public/images/emoji/twitter/three.png new file mode 100644 index 0000000000..044578cdab Binary files /dev/null and b/public/images/emoji/twitter/three.png differ diff --git a/public/images/emoji/twitter/thumbsdown.png b/public/images/emoji/twitter/thumbsdown.png new file mode 100644 index 0000000000..6a639124ee Binary files /dev/null and b/public/images/emoji/twitter/thumbsdown.png differ diff --git a/public/images/emoji/twitter/thumbsup.png b/public/images/emoji/twitter/thumbsup.png new file mode 100644 index 0000000000..f4bafe3cc1 Binary files /dev/null and b/public/images/emoji/twitter/thumbsup.png differ diff --git a/public/images/emoji/twitter/ticket.png b/public/images/emoji/twitter/ticket.png new file mode 100644 index 0000000000..e87e3128ef Binary files /dev/null and b/public/images/emoji/twitter/ticket.png differ diff --git a/public/images/emoji/twitter/tiger.png b/public/images/emoji/twitter/tiger.png new file mode 100644 index 0000000000..871536275f Binary files /dev/null and b/public/images/emoji/twitter/tiger.png differ diff --git a/public/images/emoji/twitter/tiger2.png b/public/images/emoji/twitter/tiger2.png new file mode 100644 index 0000000000..0b7f0ebb38 Binary files /dev/null and b/public/images/emoji/twitter/tiger2.png differ diff --git a/public/images/emoji/twitter/tired_face.png b/public/images/emoji/twitter/tired_face.png new file mode 100644 index 0000000000..b87d4dbeb6 Binary files /dev/null and b/public/images/emoji/twitter/tired_face.png differ diff --git a/public/images/emoji/twitter/tm.png b/public/images/emoji/twitter/tm.png new file mode 100644 index 0000000000..f70fd4797e Binary files /dev/null and b/public/images/emoji/twitter/tm.png differ diff --git a/public/images/emoji/twitter/toilet.png b/public/images/emoji/twitter/toilet.png new file mode 100644 index 0000000000..7b7468b1be Binary files /dev/null and b/public/images/emoji/twitter/toilet.png differ diff --git a/public/images/emoji/twitter/tokyo_tower.png b/public/images/emoji/twitter/tokyo_tower.png new file mode 100644 index 0000000000..88c8d71c74 Binary files /dev/null and b/public/images/emoji/twitter/tokyo_tower.png differ diff --git a/public/images/emoji/twitter/tomato.png b/public/images/emoji/twitter/tomato.png new file mode 100644 index 0000000000..95d9acba96 Binary files /dev/null and b/public/images/emoji/twitter/tomato.png differ diff --git a/public/images/emoji/twitter/tongue.png b/public/images/emoji/twitter/tongue.png new file mode 100644 index 0000000000..2b730f9339 Binary files /dev/null and b/public/images/emoji/twitter/tongue.png differ diff --git a/public/images/emoji/twitter/top.png b/public/images/emoji/twitter/top.png new file mode 100644 index 0000000000..eb23f5ef3c Binary files /dev/null and b/public/images/emoji/twitter/top.png differ diff --git a/public/images/emoji/twitter/tophat.png b/public/images/emoji/twitter/tophat.png new file mode 100644 index 0000000000..2adb554c60 Binary files /dev/null and b/public/images/emoji/twitter/tophat.png differ diff --git a/public/images/emoji/twitter/tractor.png b/public/images/emoji/twitter/tractor.png new file mode 100644 index 0000000000..1f984972a8 Binary files /dev/null and b/public/images/emoji/twitter/tractor.png differ diff --git a/public/images/emoji/twitter/traffic_light.png b/public/images/emoji/twitter/traffic_light.png new file mode 100644 index 0000000000..48b5d29a42 Binary files /dev/null and b/public/images/emoji/twitter/traffic_light.png differ diff --git a/public/images/emoji/twitter/train.png b/public/images/emoji/twitter/train.png new file mode 100644 index 0000000000..013893b76f Binary files /dev/null and b/public/images/emoji/twitter/train.png differ diff --git a/public/images/emoji/twitter/train2.png b/public/images/emoji/twitter/train2.png new file mode 100644 index 0000000000..c7120d915e Binary files /dev/null and b/public/images/emoji/twitter/train2.png differ diff --git a/public/images/emoji/twitter/tram.png b/public/images/emoji/twitter/tram.png new file mode 100644 index 0000000000..2297b1de0c Binary files /dev/null and b/public/images/emoji/twitter/tram.png differ diff --git a/public/images/emoji/twitter/triangular_flag_on_post.png b/public/images/emoji/twitter/triangular_flag_on_post.png new file mode 100644 index 0000000000..2243d6db66 Binary files /dev/null and b/public/images/emoji/twitter/triangular_flag_on_post.png differ diff --git a/public/images/emoji/twitter/triangular_ruler.png b/public/images/emoji/twitter/triangular_ruler.png new file mode 100644 index 0000000000..2c8045da48 Binary files /dev/null and b/public/images/emoji/twitter/triangular_ruler.png differ diff --git a/public/images/emoji/twitter/trident.png b/public/images/emoji/twitter/trident.png new file mode 100644 index 0000000000..5eee6c02f5 Binary files /dev/null and b/public/images/emoji/twitter/trident.png differ diff --git a/public/images/emoji/twitter/triumph.png b/public/images/emoji/twitter/triumph.png new file mode 100644 index 0000000000..65d90497a0 Binary files /dev/null and b/public/images/emoji/twitter/triumph.png differ diff --git a/public/images/emoji/twitter/trolleybus.png b/public/images/emoji/twitter/trolleybus.png new file mode 100644 index 0000000000..fe8548106d Binary files /dev/null and b/public/images/emoji/twitter/trolleybus.png differ diff --git a/public/images/emoji/twitter/trophy.png b/public/images/emoji/twitter/trophy.png new file mode 100644 index 0000000000..8010d8cdfa Binary files /dev/null and b/public/images/emoji/twitter/trophy.png differ diff --git a/public/images/emoji/twitter/tropical_drink.png b/public/images/emoji/twitter/tropical_drink.png new file mode 100644 index 0000000000..16f2c8822c Binary files /dev/null and b/public/images/emoji/twitter/tropical_drink.png differ diff --git a/public/images/emoji/twitter/tropical_fish.png b/public/images/emoji/twitter/tropical_fish.png new file mode 100644 index 0000000000..4a02ce1c35 Binary files /dev/null and b/public/images/emoji/twitter/tropical_fish.png differ diff --git a/public/images/emoji/twitter/truck.png b/public/images/emoji/twitter/truck.png new file mode 100644 index 0000000000..5baf34cff6 Binary files /dev/null and b/public/images/emoji/twitter/truck.png differ diff --git a/public/images/emoji/twitter/trumpet.png b/public/images/emoji/twitter/trumpet.png new file mode 100644 index 0000000000..d46d206be2 Binary files /dev/null and b/public/images/emoji/twitter/trumpet.png differ diff --git a/public/images/emoji/twitter/tshirt.png b/public/images/emoji/twitter/tshirt.png new file mode 100644 index 0000000000..bf78d21c06 Binary files /dev/null and b/public/images/emoji/twitter/tshirt.png differ diff --git a/public/images/emoji/twitter/tulip.png b/public/images/emoji/twitter/tulip.png new file mode 100644 index 0000000000..64b50b690b Binary files /dev/null and b/public/images/emoji/twitter/tulip.png differ diff --git a/public/images/emoji/twitter/turtle.png b/public/images/emoji/twitter/turtle.png new file mode 100644 index 0000000000..dc2d666ffa Binary files /dev/null and b/public/images/emoji/twitter/turtle.png differ diff --git a/public/images/emoji/twitter/tv.png b/public/images/emoji/twitter/tv.png new file mode 100644 index 0000000000..2bbee71c3e Binary files /dev/null and b/public/images/emoji/twitter/tv.png differ diff --git a/public/images/emoji/twitter/twisted_rightwards_arrows.png b/public/images/emoji/twitter/twisted_rightwards_arrows.png new file mode 100644 index 0000000000..fc1e421606 Binary files /dev/null and b/public/images/emoji/twitter/twisted_rightwards_arrows.png differ diff --git a/public/images/emoji/twitter/two.png b/public/images/emoji/twitter/two.png new file mode 100644 index 0000000000..886ccfcd7b Binary files /dev/null and b/public/images/emoji/twitter/two.png differ diff --git a/public/images/emoji/twitter/two_hearts.png b/public/images/emoji/twitter/two_hearts.png new file mode 100644 index 0000000000..0d0068f308 Binary files /dev/null and b/public/images/emoji/twitter/two_hearts.png differ diff --git a/public/images/emoji/twitter/two_men_holding_hands.png b/public/images/emoji/twitter/two_men_holding_hands.png new file mode 100644 index 0000000000..f0cbbb5438 Binary files /dev/null and b/public/images/emoji/twitter/two_men_holding_hands.png differ diff --git a/public/images/emoji/twitter/two_women_holding_hands.png b/public/images/emoji/twitter/two_women_holding_hands.png new file mode 100644 index 0000000000..f13ef4254b Binary files /dev/null and b/public/images/emoji/twitter/two_women_holding_hands.png differ diff --git a/public/images/emoji/twitter/u5272.png b/public/images/emoji/twitter/u5272.png new file mode 100644 index 0000000000..212a675858 Binary files /dev/null and b/public/images/emoji/twitter/u5272.png differ diff --git a/public/images/emoji/twitter/u5408.png b/public/images/emoji/twitter/u5408.png new file mode 100644 index 0000000000..e4ee50c85c Binary files /dev/null and b/public/images/emoji/twitter/u5408.png differ diff --git a/public/images/emoji/twitter/u55b6.png b/public/images/emoji/twitter/u55b6.png new file mode 100644 index 0000000000..6fe0b6f229 Binary files /dev/null and b/public/images/emoji/twitter/u55b6.png differ diff --git a/public/images/emoji/twitter/u6307.png b/public/images/emoji/twitter/u6307.png new file mode 100644 index 0000000000..53ea0942fa Binary files /dev/null and b/public/images/emoji/twitter/u6307.png differ diff --git a/public/images/emoji/twitter/u6708.png b/public/images/emoji/twitter/u6708.png new file mode 100644 index 0000000000..75c4af6975 Binary files /dev/null and b/public/images/emoji/twitter/u6708.png differ diff --git a/public/images/emoji/twitter/u6709.png b/public/images/emoji/twitter/u6709.png new file mode 100644 index 0000000000..f2cb50f981 Binary files /dev/null and b/public/images/emoji/twitter/u6709.png differ diff --git a/public/images/emoji/twitter/u6e80.png b/public/images/emoji/twitter/u6e80.png new file mode 100644 index 0000000000..597b02eefb Binary files /dev/null and b/public/images/emoji/twitter/u6e80.png differ diff --git a/public/images/emoji/twitter/u7121.png b/public/images/emoji/twitter/u7121.png new file mode 100644 index 0000000000..75e6e0fae0 Binary files /dev/null and b/public/images/emoji/twitter/u7121.png differ diff --git a/public/images/emoji/twitter/u7533.png b/public/images/emoji/twitter/u7533.png new file mode 100644 index 0000000000..b94f801411 Binary files /dev/null and b/public/images/emoji/twitter/u7533.png differ diff --git a/public/images/emoji/twitter/u7981.png b/public/images/emoji/twitter/u7981.png new file mode 100644 index 0000000000..82764f6d4d Binary files /dev/null and b/public/images/emoji/twitter/u7981.png differ diff --git a/public/images/emoji/twitter/u7a7a.png b/public/images/emoji/twitter/u7a7a.png new file mode 100644 index 0000000000..12205ed88a Binary files /dev/null and b/public/images/emoji/twitter/u7a7a.png differ diff --git a/public/images/emoji/twitter/uk.png b/public/images/emoji/twitter/uk.png new file mode 100644 index 0000000000..f31e458c1d Binary files /dev/null and b/public/images/emoji/twitter/uk.png differ diff --git a/public/images/emoji/twitter/umbrella.png b/public/images/emoji/twitter/umbrella.png new file mode 100644 index 0000000000..8e4e3508c8 Binary files /dev/null and b/public/images/emoji/twitter/umbrella.png differ diff --git a/public/images/emoji/twitter/unamused.png b/public/images/emoji/twitter/unamused.png new file mode 100644 index 0000000000..0ceb370828 Binary files /dev/null and b/public/images/emoji/twitter/unamused.png differ diff --git a/public/images/emoji/twitter/underage.png b/public/images/emoji/twitter/underage.png new file mode 100644 index 0000000000..702cf425b8 Binary files /dev/null and b/public/images/emoji/twitter/underage.png differ diff --git a/public/images/emoji/twitter/unlock.png b/public/images/emoji/twitter/unlock.png new file mode 100644 index 0000000000..764cabd930 Binary files /dev/null and b/public/images/emoji/twitter/unlock.png differ diff --git a/public/images/emoji/twitter/up.png b/public/images/emoji/twitter/up.png new file mode 100644 index 0000000000..799607c2b3 Binary files /dev/null and b/public/images/emoji/twitter/up.png differ diff --git a/public/images/emoji/twitter/us.png b/public/images/emoji/twitter/us.png new file mode 100644 index 0000000000..2a0cbf0009 Binary files /dev/null and b/public/images/emoji/twitter/us.png differ diff --git a/public/images/emoji/twitter/v.png b/public/images/emoji/twitter/v.png new file mode 100644 index 0000000000..c6e9b64534 Binary files /dev/null and b/public/images/emoji/twitter/v.png differ diff --git a/public/images/emoji/twitter/vertical_traffic_light.png b/public/images/emoji/twitter/vertical_traffic_light.png new file mode 100644 index 0000000000..286901f2fe Binary files /dev/null and b/public/images/emoji/twitter/vertical_traffic_light.png differ diff --git a/public/images/emoji/twitter/vhs.png b/public/images/emoji/twitter/vhs.png new file mode 100644 index 0000000000..4583077e22 Binary files /dev/null and b/public/images/emoji/twitter/vhs.png differ diff --git a/public/images/emoji/twitter/vibration_mode.png b/public/images/emoji/twitter/vibration_mode.png new file mode 100644 index 0000000000..cb81a51c3a Binary files /dev/null and b/public/images/emoji/twitter/vibration_mode.png differ diff --git a/public/images/emoji/twitter/video_camera.png b/public/images/emoji/twitter/video_camera.png new file mode 100644 index 0000000000..acc4578753 Binary files /dev/null and b/public/images/emoji/twitter/video_camera.png differ diff --git a/public/images/emoji/twitter/video_game.png b/public/images/emoji/twitter/video_game.png new file mode 100644 index 0000000000..d84ae0786b Binary files /dev/null and b/public/images/emoji/twitter/video_game.png differ diff --git a/public/images/emoji/twitter/violin.png b/public/images/emoji/twitter/violin.png new file mode 100644 index 0000000000..7d2e82d5d8 Binary files /dev/null and b/public/images/emoji/twitter/violin.png differ diff --git a/public/images/emoji/twitter/virgo.png b/public/images/emoji/twitter/virgo.png new file mode 100644 index 0000000000..7d10568d7c Binary files /dev/null and b/public/images/emoji/twitter/virgo.png differ diff --git a/public/images/emoji/twitter/volcano.png b/public/images/emoji/twitter/volcano.png new file mode 100644 index 0000000000..fc733b42ae Binary files /dev/null and b/public/images/emoji/twitter/volcano.png differ diff --git a/public/images/emoji/twitter/vs.png b/public/images/emoji/twitter/vs.png new file mode 100644 index 0000000000..706965c12e Binary files /dev/null and b/public/images/emoji/twitter/vs.png differ diff --git a/public/images/emoji/twitter/walking.png b/public/images/emoji/twitter/walking.png new file mode 100644 index 0000000000..7f6b71175c Binary files /dev/null and b/public/images/emoji/twitter/walking.png differ diff --git a/public/images/emoji/twitter/waning_crescent_moon.png b/public/images/emoji/twitter/waning_crescent_moon.png new file mode 100644 index 0000000000..38edbadb37 Binary files /dev/null and b/public/images/emoji/twitter/waning_crescent_moon.png differ diff --git a/public/images/emoji/twitter/waning_gibbous_moon.png b/public/images/emoji/twitter/waning_gibbous_moon.png new file mode 100644 index 0000000000..d8072a3e4d Binary files /dev/null and b/public/images/emoji/twitter/waning_gibbous_moon.png differ diff --git a/public/images/emoji/twitter/warning.png b/public/images/emoji/twitter/warning.png new file mode 100644 index 0000000000..310d2256af Binary files /dev/null and b/public/images/emoji/twitter/warning.png differ diff --git a/public/images/emoji/twitter/watch.png b/public/images/emoji/twitter/watch.png new file mode 100644 index 0000000000..c52c19b273 Binary files /dev/null and b/public/images/emoji/twitter/watch.png differ diff --git a/public/images/emoji/twitter/water_buffalo.png b/public/images/emoji/twitter/water_buffalo.png new file mode 100644 index 0000000000..4787a61a7a Binary files /dev/null and b/public/images/emoji/twitter/water_buffalo.png differ diff --git a/public/images/emoji/twitter/watermelon.png b/public/images/emoji/twitter/watermelon.png new file mode 100644 index 0000000000..4d84a2b306 Binary files /dev/null and b/public/images/emoji/twitter/watermelon.png differ diff --git a/public/images/emoji/twitter/wave.png b/public/images/emoji/twitter/wave.png new file mode 100644 index 0000000000..2f219a4a6c Binary files /dev/null and b/public/images/emoji/twitter/wave.png differ diff --git a/public/images/emoji/twitter/wavy_dash.png b/public/images/emoji/twitter/wavy_dash.png new file mode 100644 index 0000000000..3fcfdfb0ec Binary files /dev/null and b/public/images/emoji/twitter/wavy_dash.png differ diff --git a/public/images/emoji/twitter/waxing_crescent_moon.png b/public/images/emoji/twitter/waxing_crescent_moon.png new file mode 100644 index 0000000000..526f78da3b Binary files /dev/null and b/public/images/emoji/twitter/waxing_crescent_moon.png differ diff --git a/public/images/emoji/twitter/waxing_gibbous_moon.png b/public/images/emoji/twitter/waxing_gibbous_moon.png new file mode 100644 index 0000000000..319561d710 Binary files /dev/null and b/public/images/emoji/twitter/waxing_gibbous_moon.png differ diff --git a/public/images/emoji/twitter/wc.png b/public/images/emoji/twitter/wc.png new file mode 100644 index 0000000000..0c779cd477 Binary files /dev/null and b/public/images/emoji/twitter/wc.png differ diff --git a/public/images/emoji/twitter/weary.png b/public/images/emoji/twitter/weary.png new file mode 100644 index 0000000000..1ef63c7e73 Binary files /dev/null and b/public/images/emoji/twitter/weary.png differ diff --git a/public/images/emoji/twitter/wedding.png b/public/images/emoji/twitter/wedding.png new file mode 100644 index 0000000000..6483199e4a Binary files /dev/null and b/public/images/emoji/twitter/wedding.png differ diff --git a/public/images/emoji/twitter/whale.png b/public/images/emoji/twitter/whale.png new file mode 100644 index 0000000000..2e5f613f4e Binary files /dev/null and b/public/images/emoji/twitter/whale.png differ diff --git a/public/images/emoji/twitter/whale2.png b/public/images/emoji/twitter/whale2.png new file mode 100644 index 0000000000..e1fd1fdd67 Binary files /dev/null and b/public/images/emoji/twitter/whale2.png differ diff --git a/public/images/emoji/twitter/wheelchair.png b/public/images/emoji/twitter/wheelchair.png new file mode 100644 index 0000000000..52966c6889 Binary files /dev/null and b/public/images/emoji/twitter/wheelchair.png differ diff --git a/public/images/emoji/twitter/white_check_mark.png b/public/images/emoji/twitter/white_check_mark.png new file mode 100644 index 0000000000..2ce525a314 Binary files /dev/null and b/public/images/emoji/twitter/white_check_mark.png differ diff --git a/public/images/emoji/twitter/white_circle.png b/public/images/emoji/twitter/white_circle.png new file mode 100644 index 0000000000..1f3f7874e9 Binary files /dev/null and b/public/images/emoji/twitter/white_circle.png differ diff --git a/public/images/emoji/twitter/white_flower.png b/public/images/emoji/twitter/white_flower.png new file mode 100644 index 0000000000..1f435957c7 Binary files /dev/null and b/public/images/emoji/twitter/white_flower.png differ diff --git a/public/images/emoji/twitter/white_large_square.png b/public/images/emoji/twitter/white_large_square.png new file mode 100644 index 0000000000..bc9de693ad Binary files /dev/null and b/public/images/emoji/twitter/white_large_square.png differ diff --git a/public/images/emoji/twitter/white_medium_small_square.png b/public/images/emoji/twitter/white_medium_small_square.png new file mode 100644 index 0000000000..83df4c86df Binary files /dev/null and b/public/images/emoji/twitter/white_medium_small_square.png differ diff --git a/public/images/emoji/twitter/white_medium_square.png b/public/images/emoji/twitter/white_medium_square.png new file mode 100644 index 0000000000..78a5940ebe Binary files /dev/null and b/public/images/emoji/twitter/white_medium_square.png differ diff --git a/public/images/emoji/twitter/white_small_square.png b/public/images/emoji/twitter/white_small_square.png new file mode 100644 index 0000000000..bcff71a524 Binary files /dev/null and b/public/images/emoji/twitter/white_small_square.png differ diff --git a/public/images/emoji/twitter/white_square_button.png b/public/images/emoji/twitter/white_square_button.png new file mode 100644 index 0000000000..59089e757c Binary files /dev/null and b/public/images/emoji/twitter/white_square_button.png differ diff --git a/public/images/emoji/twitter/wind_chime.png b/public/images/emoji/twitter/wind_chime.png new file mode 100644 index 0000000000..c6694e8a28 Binary files /dev/null and b/public/images/emoji/twitter/wind_chime.png differ diff --git a/public/images/emoji/twitter/wine_glass.png b/public/images/emoji/twitter/wine_glass.png new file mode 100644 index 0000000000..5cfda51425 Binary files /dev/null and b/public/images/emoji/twitter/wine_glass.png differ diff --git a/public/images/emoji/twitter/wink.png b/public/images/emoji/twitter/wink.png new file mode 100644 index 0000000000..ed1b91c16f Binary files /dev/null and b/public/images/emoji/twitter/wink.png differ diff --git a/public/images/emoji/twitter/wolf.png b/public/images/emoji/twitter/wolf.png new file mode 100644 index 0000000000..13cb94f7b1 Binary files /dev/null and b/public/images/emoji/twitter/wolf.png differ diff --git a/public/images/emoji/twitter/woman.png b/public/images/emoji/twitter/woman.png new file mode 100644 index 0000000000..a507281935 Binary files /dev/null and b/public/images/emoji/twitter/woman.png differ diff --git a/public/images/emoji/twitter/womans_clothes.png b/public/images/emoji/twitter/womans_clothes.png new file mode 100644 index 0000000000..5cc359fd15 Binary files /dev/null and b/public/images/emoji/twitter/womans_clothes.png differ diff --git a/public/images/emoji/twitter/womans_hat.png b/public/images/emoji/twitter/womans_hat.png new file mode 100644 index 0000000000..be1d7002a4 Binary files /dev/null and b/public/images/emoji/twitter/womans_hat.png differ diff --git a/public/images/emoji/twitter/womens.png b/public/images/emoji/twitter/womens.png new file mode 100644 index 0000000000..c4ef94ed79 Binary files /dev/null and b/public/images/emoji/twitter/womens.png differ diff --git a/public/images/emoji/twitter/worried.png b/public/images/emoji/twitter/worried.png new file mode 100644 index 0000000000..fefb226c9c Binary files /dev/null and b/public/images/emoji/twitter/worried.png differ diff --git a/public/images/emoji/twitter/wrench.png b/public/images/emoji/twitter/wrench.png new file mode 100644 index 0000000000..f3fbe3360e Binary files /dev/null and b/public/images/emoji/twitter/wrench.png differ diff --git a/public/images/emoji/twitter/x.png b/public/images/emoji/twitter/x.png new file mode 100644 index 0000000000..a245297866 Binary files /dev/null and b/public/images/emoji/twitter/x.png differ diff --git a/public/images/emoji/twitter/yellow_heart.png b/public/images/emoji/twitter/yellow_heart.png new file mode 100644 index 0000000000..4e7688b61a Binary files /dev/null and b/public/images/emoji/twitter/yellow_heart.png differ diff --git a/public/images/emoji/twitter/yen.png b/public/images/emoji/twitter/yen.png new file mode 100644 index 0000000000..0700b6cbc8 Binary files /dev/null and b/public/images/emoji/twitter/yen.png differ diff --git a/public/images/emoji/twitter/yum.png b/public/images/emoji/twitter/yum.png new file mode 100644 index 0000000000..cd81753f0e Binary files /dev/null and b/public/images/emoji/twitter/yum.png differ diff --git a/public/images/emoji/twitter/zap.png b/public/images/emoji/twitter/zap.png new file mode 100644 index 0000000000..ee193678fc Binary files /dev/null and b/public/images/emoji/twitter/zap.png differ diff --git a/public/images/emoji/twitter/zero.png b/public/images/emoji/twitter/zero.png new file mode 100644 index 0000000000..a14ee8780c Binary files /dev/null and b/public/images/emoji/twitter/zero.png differ diff --git a/public/images/emoji/twitter/zzz.png b/public/images/emoji/twitter/zzz.png new file mode 100644 index 0000000000..9e1ab21bcf Binary files /dev/null and b/public/images/emoji/twitter/zzz.png differ diff --git a/public/images/welcome/quote-reply-cs.png b/public/images/welcome/quote-reply-cs.png new file mode 100644 index 0000000000..d3bb1bc934 Binary files /dev/null and b/public/images/welcome/quote-reply-cs.png differ diff --git a/public/images/welcome/username-completion-cs.png b/public/images/welcome/username-completion-cs.png new file mode 100644 index 0000000000..bce11a1a12 Binary files /dev/null and b/public/images/welcome/username-completion-cs.png differ diff --git a/script/bench.rb b/script/bench.rb index 46720b83f3..fa569991b7 100644 --- a/script/bench.rb +++ b/script/bench.rb @@ -2,6 +2,7 @@ require "socket" require "csv" require "yaml" require "optparse" +require "fileutils" @include_env = false @result_file = nil @@ -9,6 +10,7 @@ require "optparse" @best_of = 1 @mem_stats = false @unicorn = false +@dump_heap = false opts = OptionParser.new do |o| o.banner = "Usage: ruby bench.rb [options]" @@ -25,6 +27,11 @@ opts = OptionParser.new do |o| o.on("-b", "--best_of [NUM]", "Number of times to run the bench taking best as result") do |i| @best_of = i.to_i end + o.on("-d", "--heap_dump") do + @dump_heap = true + # We need an env var for config/boot.rb to enable allocation tracing prior to framework init + ENV['DISCOURSE_DUMP_HEAP'] = "1" + end o.on("-m", "--memory_stats") do @mem_stats = true end @@ -35,11 +42,14 @@ end opts.parse! def run(command, opt = nil) - if opt == :quiet - system(command, out: "/dev/null", err: :out) - else - system(command, out: $stdout, err: :out) - end + exit_status = + if opt == :quiet + system(command, out: "/dev/null", err: :out) + else + system(command, out: $stdout, err: :out) + end + + exit unless exit_status end begin @@ -75,7 +85,7 @@ sudo apt-get install redis-server end puts "Running bundle" -if !run("bundle", :quiet) +if run("bundle", :quiet) puts "Quitting, some of the gems did not install" prereqs exit @@ -102,16 +112,18 @@ end ENV["RAILS_ENV"] = "profile" +discourse_env_vars = %w(DISCOURSE_DUMP_HEAP RUBY_GC_HEAP_INIT_SLOTS RUBY_GC_HEAP_FREE_SLOTS RUBY_GC_HEAP_GROWTH_FACTOR RUBY_GC_HEAP_GROWTH_MAX_SLOTS RUBY_GC_MALLOC_LIMIT RUBY_GC_OLDMALLOC_LIMIT RUBY_GC_MALLOC_LIMIT_MAX RUBY_GC_OLDMALLOC_LIMIT_MAX RUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR RUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR) + if @include_env puts "Running with tuned environment" ENV["RUBY_GC_MALLOC_LIMIT"] = "50_000_000" - ENV.delete "RUBY_HEAP_SLOTS_GROWTH_FACTOR" - ENV.delete "RUBY_HEAP_MIN_SLOTS" - ENV.delete "RUBY_FREE_MIN" + discourse_env_vars - %w(RUBY_GC_MALLOC_LIMIT).each do |v| + ENV.delete v + end else # clean env puts "Running with the following custom environment" - %w{RUBY_GC_MALLOC_LIMIT RUBY_HEAP_MIN_SLOTS RUBY_FREE_MIN}.each do |w| + discourse_env_vars.each do |w| puts "#{w}: #{ENV[w]}" end end @@ -168,6 +180,7 @@ begin pid = if @unicorn ENV['UNICORN_PORT'] = @port.to_s + FileUtils.mkdir_p(File.join('tmp', 'pids')) spawn("bundle exec unicorn -c config/unicorn.conf.rb") else spawn("bundle exec thin start -p #{@port}") @@ -252,6 +265,11 @@ begin puts open("http://127.0.0.1:#{@port}/admin/memory_stats#{append}").read end + if @dump_heap + puts + puts open("http://127.0.0.1:#{@port}/admin/dump_heap#{append}").read + end + if @result_file File.open(@result_file,"wb") do |f| f.write(results) diff --git a/script/import_scripts/base.rb b/script/import_scripts/base.rb index 09d0c805b6..4895661f60 100644 --- a/script/import_scripts/base.rb +++ b/script/import_scripts/base.rb @@ -11,12 +11,17 @@ if ARGV.include?('bbcode-to-md') require 'ruby-bbcode-to-md' end +require_relative '../../config/environment' +require_dependency 'url_helper' +require_dependency 'file_helper' + module ImportScripts; end class ImportScripts::Base + include ActionView::Helpers::NumberHelper + def initialize - require File.expand_path(File.dirname(__FILE__) + "/../../config/environment") preload_i18n @bbcode_to_md = true if ARGV.include?('bbcode-to-md') @@ -28,6 +33,7 @@ class ImportScripts::Base @existing_posts = {} @topic_lookup = {} @old_site_settings = {} + @start_time = Time.now puts "loading existing groups..." GroupCustomField.where(name: 'import_id').pluck(:group_id, :value).each do |group_id, import_id| @@ -74,12 +80,14 @@ class ImportScripts::Base update_bumped_at update_last_posted_at + update_last_seen_at update_feature_topic_users update_category_featured_topics update_topic_count_replies reset_topic_counters - puts "", "Done" + elapsed = Time.now - @start_time + puts '', "Done (#{elapsed.to_s} seconds)" ensure reset_site_settings @@ -220,16 +228,14 @@ class ImportScripts::Base # user in the original datasource. The given id will not be used to # create the Discourse user record. def create_users(results, opts={}) - num_users_before = User.count users_created = 0 users_skipped = 0 - progress = 0 total = opts[:total] || results.size results.each do |result| u = yield(result) - # block returns nil to skip a post + # block returns nil to skip a user if u.nil? users_skipped += 1 else @@ -269,20 +275,21 @@ class ImportScripts::Base bio_raw = opts.delete(:bio_raw) website = opts.delete(:website) + location = opts.delete(:location) avatar_url = opts.delete(:avatar_url) opts[:name] = User.suggest_name(opts[:email]) unless opts[:name] if opts[:username].blank? || - opts[:username].length < User.username_length.begin || - opts[:username].length > User.username_length.end || - opts[:username] =~ /[^A-Za-z0-9_]/ || - opts[:username][0] =~ /[^A-Za-z0-9]/ || - !User.username_available?(opts[:username]) + opts[:username].length < User.username_length.begin || + opts[:username].length > User.username_length.end || + opts[:username] =~ /[^A-Za-z0-9_]/ || + opts[:username][0] =~ /[^A-Za-z0-9]/ || + !User.username_available?(opts[:username]) opts[:username] = UserNameSuggester.suggest(opts[:username] || opts[:name] || opts[:email]) end opts[:email] = opts[:email].downcase opts[:trust_level] = TrustLevel[1] unless opts[:trust_level] - opts[:active] = true + opts[:active] = opts.fetch(:active, true) opts[:import_mode] = true u = User.new(opts) @@ -296,6 +303,7 @@ class ImportScripts::Base if bio_raw.present? || website.present? u.user_profile.bio_raw = bio_raw if bio_raw.present? u.user_profile.website = website if website.present? + u.user_profile.location = location if location.present? u.user_profile.save! end end @@ -323,6 +331,8 @@ class ImportScripts::Base results.each do |c| params = yield(c) + next if params.nil? # block returns nil to skip + # Basic massaging on the category name params[:name] = "Blank" if params[:name].blank? params[:name].strip! @@ -453,15 +463,15 @@ class ImportScripts::Base src.close tmp.rewind - Upload.create_for(user_id, tmp, source_filename, File.size(tmp)) + Upload.create_for(user_id, tmp, source_filename, tmp.size) ensure tmp.close rescue nil tmp.unlink rescue nil end def close_inactive_topics(opts={}) - puts "", "Closing topics that have been inactive for more than #{num_days} days." num_days = opts[:days] || 30 + puts '', "Closing topics that have been inactive for more than #{num_days} days." query = Topic.where('last_posted_at < ?', num_days.days.ago).where(closed: false) total_count = query.count @@ -499,6 +509,14 @@ class ImportScripts::Base User.exec_sql(sql) end + # scripts that are able to import last_seen_at from the source data should override this method + def update_last_seen_at + puts "", "updating last seen at on users" + + User.exec_sql("UPDATE users SET last_seen_at = created_at WHERE last_seen_at IS NULL") + User.exec_sql("UPDATE users SET last_seen_at = last_posted_at WHERE last_posted_at IS NOT NULL") + end + def update_feature_topic_users puts "", "updating featured topic users" @@ -513,7 +531,7 @@ class ImportScripts::Base end def reset_topic_counters - puts "", "reseting topic counters" + puts "", "resetting topic counters" total_count = Topic.count progress_count = 0 @@ -552,6 +570,28 @@ class ImportScripts::Base end end + def update_tl0 + User.all.each do |user| + user.change_trust_level!(0) if Post.where(user_id: user.id).count == 0 + end + end + + def html_for_upload(upload, display_filename) + if FileHelper.is_image?(upload.url) + embedded_image_html(upload) + else + attachment_html(upload, display_filename) + end + end + + def embedded_image_html(upload) + %Q[
] + end + + def attachment_html(upload, display_filename) + "#{display_filename} (#{number_to_human_size(upload.filesize)})" + end + def print_status(current, max) print "\r%9d / %d (%5.1f%%) " % [current, max, ((current.to_f / max.to_f) * 100).round(1)] end diff --git a/script/import_scripts/bespoke_1.rb b/script/import_scripts/bespoke_1.rb index e91a001a57..2b94283aea 100644 --- a/script/import_scripts/bespoke_1.rb +++ b/script/import_scripts/bespoke_1.rb @@ -28,8 +28,8 @@ class ImportScripts::Bespoke < ImportScripts::Base end def execute - #import_users - #import_categories + import_users + import_categories import_posts end @@ -146,6 +146,8 @@ class ImportScripts::Bespoke < ImportScripts::Base created_at = DateTime.parse(row.dcreate) username = name if username == "NULL" + username = email.split("@")[0] if username.blank? + name = email.split("@")[0] if name.blank? users << { id: id, @@ -180,7 +182,7 @@ class ImportScripts::Bespoke < ImportScripts::Base # purple and #1223f3 raw.gsub!(/\[color=[#a-z0-9]+\]/i, "") raw.gsub!(/\[\/color\]/i, "") - raw.gsub!(/\[signature\].+\[\/signature\]/i,"") + raw.gsub!(/\[signature\].+\[\/signature\]/im,"") raw end diff --git a/script/import_scripts/discuz_x.rb b/script/import_scripts/discuz_x.rb new file mode 100644 index 0000000000..a3795bf7b9 --- /dev/null +++ b/script/import_scripts/discuz_x.rb @@ -0,0 +1,632 @@ +# encoding: utf-8 +# +# Author: Erick Guan +# +# This script import the data from latest Discuz! X +# Should work among Discuz! X3.x +# This script is tested only on Simplified Chinese Discuz! X instances +# If you want to import data other than Simplified Chinese, email me. + +require File.expand_path(File.dirname(__FILE__) + "/base.rb") + +require 'mysql2' + +class ImportScripts::DiscuzX < ImportScripts::Base + + DISCUZX_DB = "ultrax" + DB_TABLE_PREFIX = 'pre_' + BATCH_SIZE = 1000 + ORIGINAL_SITE_PREFIX = "oldsite.example.com/forums" # without http(s):// + NEW_SITE_PREFIX = "http://discourse.example.com" # with http:// or https:// + + # Set DISCUZX_BASE_DIR to the base directory of your discuz installation. + DISCUZX_BASE_DIR = '/var/www/discuz/upload' + AVATAR_DIR = '/uc_server/data/avatar' + ATTACHMENT_DIR = '/data/attachment/forum' + AUTHORIZED_EXTENSIONS = ['jpg', 'jpeg', 'png', 'gif', 'zip', 'rar', 'pdf'] + + def initialize + super + + @client = Mysql2::Client.new( + host: "localhost", + username: "root", + #password: "password", + database: DISCUZX_DB + ) + @first_post_id_by_topic_id = {} + end + + def execute + import_users + import_categories + import_posts + import_private_messages + import_attachments + end + + # add the prefix to the table name + def table_name(name = nil) + DB_TABLE_PREFIX + name + end + + # find which group members can be granted as admin + def get_knowledge_about_group + group_table = table_name 'common_usergroup' + result = mysql_query( + "SELECT groupid group_id, radminid role_id, type, grouptitle title + FROM #{group_table};") + @moderator_group_id = -1 + @admin_group_id = -1 + + result.each do |group| + role_id = group['role_id'] + group_id = group['group_id'] + case group['title'].strip + when '管理员' + @admin_admin_id = role_id + when '超级版主' + @moderator_admin_id = role_id + end + end + end + + def import_users + puts '', "creating users" + + get_knowledge_about_group + + sensitive_user_table = table_name 'ucenter_members' + user_table = table_name 'common_member' + profile_table = table_name 'common_member_profile' + status_table = table_name 'common_member_status' + total_count = mysql_query("SELECT count(*) count FROM #{user_table};").first['count'] + + batches(BATCH_SIZE) do |offset| + results = mysql_query( + "SELECT u.uid id, u.username username, u.email email, u.adminid admin_id, su.regdate regdate, s.regip regip, + u.emailstatus email_confirmed, u.avatarstatus avatar_exists, p.site website, p.resideprovince province, + p.residecity city, p.residedist country, p.residecommunity community, p.residesuite apartment, + p.bio bio, s.lastip last_visit_ip, s.lastvisit last_visit_time, s.lastpost last_posted_at, + s.lastsendmail last_emailed_at + FROM #{user_table} u + JOIN #{sensitive_user_table} su ON su.uid = u.uid + JOIN #{profile_table} p ON p.uid = u.uid + JOIN #{status_table} s ON s.uid = u.uid + ORDER BY u.uid ASC + LIMIT #{BATCH_SIZE} + OFFSET #{offset};") + + break if results.size < 1 + + create_users(results, total: total_count, offset: offset) do |user| + { id: user['id'], + email: user['email'], + username: user['username'], + name: user['username'], + created_at: Time.zone.at(user['regdate']), + registration_ip_address: user['regip'], + ip_address: user['last_visit_ip'], + last_seen_at: user['last_visit_time'], + last_emailed_at: user['last_emailed_at'], + last_posted_at: user['last_posted_at'], + moderator: user['admin_id'] == @moderator_admin_id, + admin: user['admin_id'] == @admin_admin_id, + active: true, + website: user['website'], + bio_raw: user['bio'], + location: "#{user['province']}#{user['city']}#{user['country']}#{user['community']}#{user['apartment']}", + post_create_action: lambda do |newmember| + if user['avatar_exists'] == 1 and newmember.uploaded_avatar_id.blank? + path, filename = discuzx_avatar_fullpath(user['id']) + if path + begin + upload = create_upload(newmember.id, path, filename) + if upload.persisted? + newmember.import_mode = false + newmember.create_user_avatar + newmember.import_mode = true + newmember.user_avatar.update(custom_upload_id: upload.id) + newmember.update(uploaded_avatar_id: upload.id) + else + puts "Error: Upload did not persist!" + end + rescue SystemCallError => err + puts "Could not import avatar: #{err.message}" + end + end + end + + # we don't send email to the unconfirmed user + newmember.update(email_digests: user['email_confirmed'] == 1) if newmember.email_digests + end + } + end + end + end + + def import_categories + puts '', "creating categories" + + forums_table = table_name 'forum_forum' + forums_data_table = table_name 'forum_forumfield' + + results = mysql_query(" + SELECT f.fid id, f.fup parent_id, f.name, f.type type, f.status status, f.displayorder position, + d.description description + FROM #{forums_table} f + JOIN #{forums_data_table} d ON f.fid = d.fid + ORDER BY parent_id ASC, id ASC + ") + + max_position = Category.all.max_by(&:position).position + create_categories(results) do |row| + next if row['type'] == 'group' || row['status'].to_i == 3 + + Category.all.max_by(&:position).position + h = { + id: row['id'], + name: row['name'], + description: row['description'], + position: row['position'].to_i + max_position + } + if row['parent_id'].to_i > 0 + parent = category_from_imported_category_id(row['parent_id']) + h[:parent_category_id] = parent.id if parent + end + h + end + end + + def import_posts + puts "", "creating topics and posts" + + posts_table = table_name 'forum_post' + topics_table = table_name 'forum_thread' + + total_count = mysql_query("SELECT count(*) count FROM #{posts_table}").first['count'] + + batches(BATCH_SIZE) do |offset| + results = mysql_query(" + SELECT p.pid id, + p.tid topic_id, + t.fid category_id, + t.subject title, + p.authorid user_id, + p.message raw, + p.dateline post_time, + p.first is_first_post, + p.invisible status + FROM #{posts_table} p, + #{topics_table} t + WHERE p.tid = t.tid + ORDER BY id ASC, topic_id ASC + LIMIT #{BATCH_SIZE} + OFFSET #{offset}; + ") + + break if results.size < 1 + + create_posts(results, total: total_count, offset: offset) do |m| + skip = false + mapped = {} + + mapped[:id] = m['id'] + mapped[:user_id] = user_id_from_imported_user_id(m['user_id']) || -1 + mapped[:raw] = process_discuzx_post(m['raw'], m['id']) + mapped[:created_at] = Time.zone.at(m['post_time']) + + if m['is_first_post'] == 1 + mapped[:category] = category_from_imported_category_id(m['category_id']).try(:name) + mapped[:title] = CGI.unescapeHTML(m['title']) + @first_post_id_by_topic_id[m['topic_id']] = m['id'] + else + parent = topic_lookup_from_imported_post_id(@first_post_id_by_topic_id[m['topic_id']]) + + if parent + mapped[:topic_id] = parent[:topic_id] + post_id = post_id_from_imported_post_id(find_post_id_by_quote_number(m['raw']).to_i) + if (post = Post.find_by(id: post_id)) + mapped[:reply_to_post_number] = post.post_number + end + else + puts "Parent topic #{m['topic_id']} doesn't exist. Skipping #{m['id']}: #{m['title'][0..40]}" + skip = true + end + end + + if [-5, -3, -1].include? m['status'] || mapped[:raw].blank? + mapped[:post_create_action] = lambda do |post| + PostDestroyer.new(Discourse.system_user, post).perform_delete + end + elsif m['status'] == -2# waiting for approve + mapped[:post_create_action] = lambda do |post| + PostAction.act(Discourse.system_user, post, 6, {take_action: false}) + end + end + + skip ? nil : mapped + end + end + end + + def import_private_messages + puts '', 'creating private messages' + + pm_indexes = table_name 'ucenter_pm_indexes' + pm_messages = table_name 'ucenter_pm_messages' + total_count = mysql_query("SELECT count(*) count FROM #{pm_indexes}").first['count'] + + batches(BATCH_SIZE) do |offset| + results = mysql_query(" + SELECT pmid id, plid thread_id, authorid user_id, message, dateline created_at + FROM #{pm_messages}_1 + UNION SELECT pmid id, plid thread_id, authorid user_id, message, dateline created_at + FROM #{pm_messages}_2 + UNION SELECT pmid id, plid thread_id, authorid user_id, message, dateline created_at + FROM #{pm_messages}_3 + UNION SELECT pmid id, plid thread_id, authorid user_id, message, dateline created_at + FROM #{pm_messages}_4 + UNION SELECT pmid id, plid thread_id, authorid user_id, message, dateline created_at + FROM #{pm_messages}_5 + UNION SELECT pmid id, plid thread_id, authorid user_id, message, dateline created_at + FROM #{pm_messages}_6 + UNION SELECT pmid id, plid thread_id, authorid user_id, message, dateline created_at + FROM #{pm_messages}_7 + UNION SELECT pmid id, plid thread_id, authorid user_id, message, dateline created_at + FROM #{pm_messages}_8 + UNION SELECT pmid id, plid thread_id, authorid user_id, message, dateline created_at + FROM #{pm_messages}_9 + ORDER BY thread_id ASC, id ASC + LIMIT #{BATCH_SIZE} + OFFSET #{offset};") + + break if results.size < 1 + + create_posts(results, total: total_count, offset: offset) do |m| + skip = false + mapped = {} + + mapped[:id] = "pm:#{m['id']}" + mapped[:user_id] = user_id_from_imported_user_id(m['user_id']) || -1 + mapped[:raw] = process_discuzx_post(m['message'], m['id']) + mapped[:created_at] = Time.zone.at(m['created_at']) + thread_id = "pm_#{m['thread_id']}" + + if is_first_pm(m['id'], m['thread_id']) + # find the title from list table + pm_thread = mysql_query(" + SELECT plid thread_id, subject + FROM #{table_name 'ucenter_pm_lists'} + WHERE plid = #{m['thread_id']};").first + mapped[:title] = pm_thread['subject'] + mapped[:archetype] = Archetype.private_message + + # Find the users who are part of this private message. + import_user_ids = mysql_query(" + SELECT plid thread_id, uid user_id + FROM #{table_name 'ucenter_pm_members'} + WHERE plid = #{m['thread_id']}; + ").map {|r| r['user_id']}.uniq + + mapped[:target_usernames] = import_user_ids.map! do |import_user_id| + import_user_id.to_s == m['user_id'].to_s ? nil : User.find_by(id: user_id_from_imported_user_id(import_user_id)).try(:username) + end.compact + + if mapped[:target_usernames].empty? # pm with yourself? + skip = true + puts "Skipping pm:#{m['id']} due to no target" + else + @first_post_id_by_topic_id[thread_id] = mapped[:id] + end + else + parent = topic_lookup_from_imported_post_id(@first_post_id_by_topic_id[thread_id]) + if parent + mapped[:topic_id] = parent[:topic_id] + else + puts "Parent post pm thread:#{thread_id} doesn't exist. Skipping #{m["id"]}: #{m["message"][0..40]}" + skip = true + end + end + + skip ? nil : mapped + end + + end + end + + # search for first pm id for the series of pm + def is_first_pm(pm_id, thread_id) + result = mysql_query(" + SELECT pmid id + FROM #{table_name 'ucenter_pm_indexes'} + WHERE plid = #{thread_id} + ORDER BY id") + result.first['id'].to_s == pm_id.to_s + end + + def process_discuzx_post(raw, import_id) + inline_image_regex = /\[img\]([\s\S]*?)\[\/img\]/ + s = raw.dup + + s.gsub!(inline_image_regex) do |d| + matches = inline_image_regex.match(d) + data = matches[1] + + upload, filename = upload_inline_image data + upload ? html_for_upload(upload, filename) : nil + end + + # Strip the quote + # [quote] quotation includes the topic which is the same as reply to in Discourse + # We get the pid to find the post number the post reply to. So it can be stripped + s = s.gsub(/\[quote\][\s\S]*?\[\/quote\]/i, '').strip + s = s.gsub(/\[b\]回复 \[url=forum.php\?mod=redirect&goto=findpost&pid=\d+&ptid=\d+\].* 的帖子\[\/url\]\[\/b\]/i, '').strip + + # Convert image bbcode + s.gsub!(/\[img=(\d+),(\d+)\]([^\]]*)\[\/img\]/i, '') + + # Remove the font tag + # Discourse doesn't support the font tag + s.gsub!(/\[font=[^ \t\r\n\f\]]*?\]/i, '') + s.gsub!(/\[\/font\]/i, '') + + # Remove the size tag + # I really have no idea what is this + s.gsub!(/\[size=[^ \t\r\n\f\]]*?\]/i, '') + s.gsub!(/\[\/size\]/i, '') + + # Remove the color tag + s.gsub!(/\[color=[^ \t\r\n\f\]]*?\]/i, '') + s.gsub!(/\[\/color\]/i, '') + + # Remove the hide tag + s.gsub!(/\[\/?hide\]/i, '') + + # Remove the align tag + # still don't know what it is + s.gsub!(/\[align=[^ \t\r\n\f\]]*?\]/i, '') + s.gsub!(/\[\/align\]/i, "\n") + + # Convert code + s.gsub!(/\[\/?code\]/i, "\n```\n") + + # The edit notice should be removed + # example: 本帖最后由 Helloworld 于 2015-1-28 22:05 编辑 + s.gsub!(/\[i=s\] 本帖最后由[\s\S]*?编辑 \[\/i\]/, '') + + # Convert the custom smileys to emojis + # `{:cry:}` to `:cry` + s.gsub!(/\{(\:\S*?\:)\}/, '\1') + + # Replace internal forum links that aren't in the format + # convert list tags to ul and list=1 tags to ol + # (basically, we're only missing list=a here...) + s.gsub!(/\[list\](.*?)\[\/list:u\]/m, '[ul]\1[/ul]') + s.gsub!(/\[list=1\](.*?)\[\/list:o\]/m, '[ol]\1[/ol]') + # convert *-tags to li-tags so bbcode-to-md can do its magic on phpBB's lists: + s.gsub!(/\[\*\](.*?)\[\/\*:m\]/, '[li]\1[/li]') + + # Discuz can create PM out of a post, which will generates like + # [url=http://example.com/forum.php?mod=redirect&goto=findpost&pid=111&ptid=11][b]关于您在“主题名称”的帖子[/b][/url] + s.gsub!(pm_url_regexp) do |discuzx_link| + replace_internal_link(discuzx_link, $1) + end + + # [url][b]text[/b][/url] to **[url]text[/url]** + s.gsub!(/(\[url=[^\[\]]*?\])\[b\](\S*)\[\/b\](\[\/url\])/, '**\1\2\3**') + + s.gsub!(internal_url_regexp) do |discuzx_link| + replace_internal_link(discuzx_link, $1) + end + + # @someone without the url + s.gsub!(/@\[url=[^\[\]]*?\](\S*)\[\/url\]/i, '@\1') + + s.strip + end + + def replace_internal_link(discuzx_link, import_topic_id) + results = mysql_query("SELECT pid + FROM #{table_name 'forum_post'} + WHERE tid = #{import_topic_id} + ORDER BY pid ASC + LIMIT 1") + + return discuzx_link unless results.size > 0 + + linked_topic_id = results.first['pid'] + lookup = topic_lookup_from_imported_post_id(linked_topic_id) + + return discuzx_link unless lookup + + if (t = Topic.find_by(id: lookup[:topic_id])) + "#{NEW_SITE_PREFIX}/t/#{t.slug}/#{t.id}" + else + discuzx_link + end + end + + def internal_url_regexp + @internal_url_regexp ||= Regexp.new("http(?:s)?://#{ORIGINAL_SITE_PREFIX.gsub('.', '\.')}/forum\\.php\\?mod=viewthread&tid=(\\d+)(?:[^\\]\\[]*)") + end + + def pm_url_regexp + @pm_url_regexp ||= Regexp.new("http(?:s)?://#{ORIGINAL_SITE_PREFIX.gsub('.', '\.')}/forum\\.php\\?mod=redirect&goto=findpost&pid=\\d+&ptid=(\\d+)") + end + + # This step is done separately because it can take multiple attempts to get right (because of + # missing files, wrong paths, authorized extensions, etc.). + def import_attachments + setting = AUTHORIZED_EXTENSIONS.join('|') + SiteSetting.authorized_extensions = setting if setting != SiteSetting.authorized_extensions + + attachment_regex = /\[attach\](\d+)\[\/attach\]/ + + user = Discourse.system_user + + current_count = 0 + total_count = mysql_query("SELECT count(*) count FROM #{table_name 'forum_post'};").first['count'] + + success_count = 0 + fail_count = 0 + + puts '', "Importing attachments...", '' + + Post.find_each do |post| + current_count += 1 + print_status current_count, total_count + + new_raw = post.raw.dup + new_raw.gsub!(attachment_regex) do |s| + matches = attachment_regex.match(s) + attachment_id = matches[1] + + upload, filename = find_upload(user, post, attachment_id) + unless upload + fail_count += 1 + next + end + + html_for_upload(upload, filename) + end + + if new_raw != post.raw + PostRevisor.new(post).revise!(post.user, { raw: new_raw }, { bypass_bump: true, edit_reason: '从 Discuz 中导入附件' }) + end + + success_count += 1 + end + + puts '', '' + puts "succeeded: #{success_count}" + puts " failed: #{fail_count}" if fail_count > 0 + puts '' + end + + # Create the full path to the discuz avatar specified from user id + def discuzx_avatar_fullpath(user_id) + padded_id = user_id.to_s.rjust(9, '0') + + part_1 = padded_id[0..2] + part_2 = padded_id[3..4] + part_3 = padded_id[5..6] + part_4 = padded_id[-2..-1] + file_name = "#{part_4}_avatar_big.jpg" + + return File.join(DISCUZX_BASE_DIR, AVATAR_DIR, part_1, part_2, part_3, file_name), file_name + end + + # post id is in the quote block + def find_post_id_by_quote_number(raw) + s = raw.dup + quote_reply = s.match(/\[quote\][\S\s]*pid=(\d+)[\S\s]*\[\/quote\]/) + reply = s.match(/url=forum.php\?mod=redirect&goto=findpost&pid=(\d+)&ptid=\d+/) + + quote_reply ? quote_reply[1] : (reply ? reply[1] : nil) + end + + # for some reason, discuz inlined some png file + # the corresponding image stored is broken in a way + def upload_inline_image(data) + return unless data + + puts 'Creating inline image' + + encoded_photo = data['data:image/png;base64,'.length .. -1] + if encoded_photo + raw_file = Base64.decode64(encoded_photo) + else + puts 'Error parsed inline photo', data[0..20] + return + end + + real_filename = "#{SecureRandom.hex}.png" + filename = Tempfile.new(['inline', '.png']) + begin + filename.binmode + filename.write(raw_file) + filename.rewind + + upload = create_upload(Discourse::SYSTEM_USER_ID, filename, real_filename) + ensure + filename.close rescue nil + filename.unlink rescue nil + end + + if upload.nil? || !upload.valid? + puts "Upload not valid :(" + puts upload.errors.inspect if upload + return nil + end + + return upload, real_filename + end + + # find the uploaded file and real name from the db + def find_upload(user, post, upload_id) + attachment_table = table_name 'forum_attachment' + # search for table id + sql = "SELECT a.pid post_id, + a.aid upload_id, + a.tableid table_id + FROM #{attachment_table} a + WHERE a.pid = #{post.custom_fields['import_id']} + AND a.aid = #{upload_id};" + results = mysql_query(sql) + + unless (meta_data = results.first) + puts "Couldn't find forum_attachment record meta data for post.id = #{post.id}, import_id = #{post.custom_fields['import_id']}" + return nil + end + + # search for uploaded file meta data + sql = "SELECT a.pid post_id, + a.aid upload_id, + a.tid topic_id, + a.uid user_id, + a.dateline uploaded_time, + a.filename real_filename, + a.attachment attachment_path, + a.remote is_remote, + a.description description, + a.isimage is_image, + a.thumb is_thumb + FROM #{attachment_table}_#{meta_data['table_id']} a + WHERE a.aid = #{upload_id};" + results = mysql_query(sql) + + unless (row = results.first) + puts "Couldn't find attachment record for post.id = #{post.id}, import_id = #{post.custom_fields['import_id']}" + return nil + end + + filename = File.join(DISCUZX_BASE_DIR, ATTACHMENT_DIR, row['attachment_path']) + unless File.exists?(filename) + puts "Attachment file doesn't exist: #{filename}" + return nil + end + real_filename = row['real_filename'] + real_filename.prepend SecureRandom.hex if real_filename[0] == '.' + upload = create_upload(user.id, filename, real_filename) + + if upload.nil? || !upload.valid? + puts "Upload not valid :(" + puts upload.errors.inspect if upload + return nil + end + + return upload, real_filename + rescue Mysql2::Error => e + puts "SQL Error" + puts e.message + puts sql + return nil + end + + def mysql_query(sql) + @client.query(sql, cache_rows: false) + end +end + +ImportScripts::DiscuzX.new.perform diff --git a/script/import_scripts/ning.rb b/script/import_scripts/ning.rb new file mode 100644 index 0000000000..5d81154204 --- /dev/null +++ b/script/import_scripts/ning.rb @@ -0,0 +1,337 @@ +require File.expand_path(File.dirname(__FILE__) + "/base.rb") + +# Edit the constants and initialize method for your import data. + +class ImportScripts::Ning < ImportScripts::Base + + JSON_FILES_DIR = "/path/to/json/archive/json/files" + ATTACHMENT_PREFIXES = ["discussions", "pages", "blogs", "members", "photos"] + EXTRA_AUTHORIZED_EXTENSIONS = ["bmp", "ico", "txt", "pdf"] + + def initialize + super + + @system_user = Discourse.system_user + + @users_json = load_ning_json("ning-members-local.json") + @discussions_json = load_ning_json("ning-discussions-local.json") + + # An example of a custom category from Ning: + @blogs_json = load_ning_json("ning-blogs-local.json") + + #SiteSetting.max_image_size_kb = 3072 + #SiteSetting.max_attachment_size_kb = 1024 + SiteSetting.authorized_extensions = (SiteSetting.authorized_extensions.split("|") + EXTRA_AUTHORIZED_EXTENSIONS).uniq.join("|") + + # Example of importing a custom profile field: + # @interests_field = UserField.find_by_name("My interests") + # unless @interests_field + # @interests_field = UserField.create(name: "My interests", description: "Do you like stuff?", field_type: "text", editable: true, required: false, show_on_profile: true) + # end + end + + def execute + puts "", "Importing from Ning..." + + import_users + import_categories + import_discussions + + import_blogs # Remove this and/or add more as necessary + + suspend_users + update_tl0 + + puts "", "Done" + end + + def load_ning_json(arg) + filename = File.join(JSON_FILES_DIR, arg) + raise RuntimeError.new("File #{filename} not found!") if !File.exists?(filename) + JSON.parse(repair_json(File.read(filename))).reverse + end + + def repair_json(arg) + arg.gsub!(/^\(/, "") # content of file is surround by ( ) + arg.gsub!(/\)$/, "") + + arg.gsub!(/\}\{/, "},{") # missing commas sometimes! + + arg.gsub!("}]{", "},{") # surprise square brackets + arg.gsub!("}[{", "},{") # :troll: + + arg + end + + def import_users + puts '', "Importing users" + + staff_levels = ["admin", "moderator", "owner"] + + create_users(@users_json) do |u| + { + id: u["contributorName"], + name: u["fullName"], + email: u["email"], + created_at: Time.zone.parse(u["createdDate"]), + date_of_birth: u["birthdate"] ? Time.zone.parse(u["birthdate"]) : nil, + location: "#{u["location"]} #{u["country"]}", + avatar_url: u["profilePhoto"], + bio_raw: u["profileQuestions"].is_a?(Hash) ? u["profileQuestions"]["About Me"] : nil, + post_create_action: proc do |newuser| + # if u["profileQuestions"].is_a?(Hash) + # newuser.custom_fields = {"user_field_#{@interests_field.id}" => u["profileQuestions"]["My interests"]} + # end + + if staff_levels.include?(u["level"].downcase) + if u["level"].downcase == "admin" || u["level"].downcase == "owner" + newuser.admin = true + else + newuser.moderator = true + end + end + + # states: ["active", "suspended", "left", "pending"] + if u["state"] == "active" && newuser.approved_at.nil? + newuser.approved = true + newuser.approved_by_id = @system_user.id + newuser.approved_at = newuser.created_at + end + + newuser.save + + if u["profilePhoto"] && newuser.user_avatar.try(:custom_upload_id).nil? + photo_path = file_full_path(u["profilePhoto"]) + if File.exists?(photo_path) + begin + upload = create_upload(newuser.id, photo_path, File.basename(photo_path)) + if upload.persisted? + newuser.import_mode = false + newuser.create_user_avatar + newuser.import_mode = true + newuser.user_avatar.update(custom_upload_id: upload.id) + newuser.update(uploaded_avatar_id: upload.id) + else + puts "Error: Upload did not persist for #{photo_path}!" + end + rescue SystemCallError => err + puts "Could not import avatar #{photo_path}: #{err.message}" + end + else + puts "avatar file not found at #{photo_path}" + end + end + end + } + end + EmailToken.delete_all + end + + def suspend_users + puts '', "Updating suspended users" + + count = 0 + suspended = 0 + total = @users_json.size + + @users_json.each do |u| + if u["state"].downcase == "suspended" + if user = find_user_by_import_id(u["contributorName"]) + user.suspended_at = Time.zone.now + user.suspended_till = 200.years.from_now + + if user.save + StaffActionLogger.new(@system_user).log_user_suspend(user, "Import data indicates account is suspended.") + suspended += 1 + else + puts "Failed to suspend user #{user.username}. #{user.errors.try(:full_messages).try(:inspect)}" + end + end + end + + count += 1 + print_status count, total + end + + puts "", "Marked #{suspended} users as suspended." + end + + + def import_categories + puts "", "Importing categories" + create_categories((["Blog", "Pages"] + @discussions_json.map { |d| d["category"] }).uniq.compact) do |name| + if name.downcase == "uncategorized" + nil + else + { + id: name, # ning has no id for categories, so use the name + name: name + } + end + end + end + + + def import_discussions + puts "", "Importing discussions" + import_topics(@discussions_json) + end + + def import_blogs + puts "", "Importing blogs" + import_topics(@blogs_json, "Blog") + end + + def import_topics(topics_json, default_category=nil) + topics = 0 + posts = 0 + total = topics_json.size # number of topics. posts are embedded in the topic json, so we can't get total post count quickly. + + topics_json.each do |topic| + if topic["title"].present? && topic["description"].present? + @current_topic_title = topic["title"] # for debugging + parent_post = nil + + if parent_post_id = post_id_from_imported_post_id(topic["id"]) + parent_post = Post.find(parent_post_id) # already imported this post + else + mapped = {} + mapped[:id] = topic["id"] + mapped[:user_id] = user_id_from_imported_user_id(topic["contributorName"]) || -1 + mapped[:created_at] = Time.zone.parse(topic["createdDate"]) + unless topic["category"].nil? || topic["category"].downcase == "uncategorized" + mapped[:category] = category_from_imported_category_id(topic["category"]).try(:name) + end + if topic["category"].nil? && default_category + mapped[:category] = default_category + end + mapped[:title] = CGI.unescapeHTML(topic["title"]) + mapped[:raw] = process_ning_post_body(topic["description"]) + + if topic["fileAttachments"] + mapped[:raw] = add_file_attachments(mapped[:raw], topic["fileAttachments"]) + end + + parent_post = create_post(mapped, mapped[:id]) + unless parent_post.is_a?(Post) + puts "Error creating topic #{mapped[:id]}. Skipping." + puts parent_post.inspect + end + end + + if topic["comments"].present? + topic["comments"].reverse.each do |post| + + if post_id_from_imported_post_id(post["id"]) + next # already imported this post + end + + raw = process_ning_post_body(post["description"]) + if post["fileAttachments"] + raw = add_file_attachments(raw, post["fileAttachments"]) + end + + new_post = create_post({ + id: post["id"], + topic_id: parent_post.topic_id, + user_id: user_id_from_imported_user_id(post["contributorName"]) || -1, + raw: raw, + created_at: Time.zone.parse(post["createdDate"]) + }, post["id"]) + + if new_post.is_a?(Post) + posts += 1 + else + puts "Error creating post #{post["id"]}. Skipping." + puts new_post.inspect + end + end + end + end + topics += 1 + print_status topics, total + end + + puts "", "Imported #{topics} topics with #{topics + posts} posts." + + [topics, posts] + end + + def file_full_path(relpath) + File.join JSON_FILES_DIR, relpath.split("?").first + end + + def attachment_regex + @_attachment_regex ||= Regexp.new(%Q[]*)href="(?:#{ATTACHMENT_PREFIXES.join('|')})\/(?:[^"]+)"(?:[^>]*)>]*)src="([^"]+)"(?:[^>]*)><\/a>]) + end + + def youtube_iframe_regex + @_youtube_iframe_regex ||= Regexp.new(%Q[

]*)src="\/\/www.youtube.com\/embed\/([^"]+)"(?:[^>]*)><\/iframe>(?:[^<]*)<\/p>]) + end + + def process_ning_post_body(arg) + raw = arg.gsub("

\n", "

") + + # youtube iframe + raw.gsub!(youtube_iframe_regex) do |s| + matches = youtube_iframe_regex.match(s) + video_id = matches[1].split("?").first + + next s unless video_id + + "\n\nhttps://www.youtube.com/watch?v=#{video_id}\n" + end + + # attachments + raw.gsub!(attachment_regex) do |s| + matches = attachment_regex.match(s) + ning_filename = matches[1] + + filename = File.join(JSON_FILES_DIR, ning_filename.split("?").first) + if !File.exists?(filename) + puts "Attachment file doesn't exist: #{filename}" + next s + end + + upload = create_upload(@system_user.id, filename, File.basename(filename)) + + if upload.nil? || !upload.valid? + puts "Upload not valid :( #{filename}" + puts upload.errors.inspect if upload + next s + end + + html_for_upload(upload, File.basename(filename)) + end + + raw + end + + def add_file_attachments(arg, file_names) + raw = arg + + file_names.each do |f| + filename = File.join(JSON_FILES_DIR, f.split("?").first) + if !File.exists?(filename) + puts "Attachment file doesn't exist: #{filename}" + next + end + + upload = create_upload(@system_user.id, filename, File.basename(filename)) + + if upload.nil? || !upload.valid? + puts "Upload not valid :( #{filename}" + puts upload.errors.inspect if upload + next + end + + raw += "\n" + attachment_html(upload, File.basename(filename)) + end + + raw + end +end + +if __FILE__==$0 + ImportScripts::Ning.new.perform +end diff --git a/script/import_scripts/phpbb3.rb b/script/import_scripts/phpbb3.rb index 01f80f7feb..289141372c 100644 --- a/script/import_scripts/phpbb3.rb +++ b/script/import_scripts/phpbb3.rb @@ -1,15 +1,9 @@ -require File.expand_path(File.dirname(__FILE__) + "/../../config/environment") require File.expand_path(File.dirname(__FILE__) + "/base.rb") -require_dependency 'url_helper' -require_dependency 'file_helper' - require "mysql2" class ImportScripts::PhpBB3 < ImportScripts::Base - include ActionView::Helpers::NumberHelper - PHPBB_DB = "phpbb" BATCH_SIZE = 1000 @@ -86,8 +80,10 @@ class ImportScripts::PhpBB3 < ImportScripts::Base admin: user['group_name'] == 'ADMINISTRATORS', post_create_action: proc do |newmember| if not PHPBB_BASE_DIR.nil? and IMPORT_AVATARS.include?(user['user_avatar_type']) and newmember.uploaded_avatar_id.blank? - path = phpbb_avatar_fullpath(user['user_avatar_type'], user['user_avatar']) and begin - upload = create_upload(newmember.id, path, user['user_avatar']) + path = phpbb_avatar_fullpath(user['user_avatar_type'], user['user_avatar']) + if path + begin + upload = create_upload(newmember.id, path, user['user_avatar']) if upload.persisted? newmember.import_mode = false newmember.create_user_avatar @@ -99,6 +95,7 @@ class ImportScripts::PhpBB3 < ImportScripts::Base end rescue SystemCallError => err puts "Could not import avatar: #{err.message}" + end end end end @@ -312,7 +309,7 @@ class ImportScripts::PhpBB3 < ImportScripts::Base s.gsub!(/\[list=1\](.*?)\[\/list:o\]/m, '[ol]\1[/ol]') # convert *-tags to li-tags so bbcode-to-md can do its magic on phpBB's lists: s.gsub!(/\[\*\](.*?)\[\/\*:m\]/, '[li]\1[/li]') - + s end @@ -411,11 +408,7 @@ class ImportScripts::PhpBB3 < ImportScripts::Base success_count += 1 - if FileHelper.is_image?(upload.url) - %Q[
] - else - "
#{real_filename} (#{number_to_human_size(upload.filesize)})" - end + html_for_upload(upload, real_filename) end if new_raw != post.raw diff --git a/script/import_scripts/smf2.rb b/script/import_scripts/smf2.rb index e657fac4d2..9f08e63a11 100644 --- a/script/import_scripts/smf2.rb +++ b/script/import_scripts/smf2.rb @@ -7,6 +7,7 @@ require 'tsort' require 'set' require 'optparse' require 'etc' +require 'open3' class ImportScripts::Smf2 < ImportScripts::Base @@ -28,19 +29,21 @@ class ImportScripts::Smf2 < ImportScripts::Base attr_reader :options def initialize(options) + if options.timezone.nil? + $stderr.puts "No source timezone given and autodetection from PHP failed." + $stderr.puts "Use -t option to specify correct source timezone:" + $stderr.puts options.usage + exit 1 + end + super() @options = options begin - timezone = `php -i`.lines.each do |line| - key, *vals = line.split(' => ').map(&:strip) - break vals[0] if key == 'Default timezone' - end - Time.zone = timezone - rescue Errno::ENOENT - $stderr.puts "Cannot autodetect PHP timezone setting, php not found in $PATH" + Time.zone = options.timezone rescue ArgumentError - $stderr.puts "Cannot set timezone '#{timezone}' (from PHP)" + $stderr.puts "Timezone name '#{options.timezone}' is invalid." + exit 1 end if options.database.blank? @@ -515,6 +518,7 @@ class ImportScripts::Smf2 < ImportScripts::Base self.host ||= 'localhost' self.username ||= Etc.getlogin self.prefix ||= 'smf_' + self.timezone ||= get_php_timezone end def usage @@ -527,9 +531,20 @@ class ImportScripts::Smf2 < ImportScripts::Base attr_accessor :database attr_accessor :prefix attr_accessor :smfroot + attr_accessor :timezone private + def get_php_timezone + phpinfo, status = Open3.capture2('phpnope', '-i') + phpinfo.lines.each do |line| + key, *vals = line.split(' => ').map(&:strip) + break vals[0] if key == 'Default timezone' + end + rescue Errno::ENOENT + $stderr.puts "Error: PHP CLI executable not found" + end + def read_smf_settings settings = File.join(self.smfroot, 'Settings.php') IO.readlines(settings).each do |line| @@ -555,6 +570,7 @@ class ImportScripts::Smf2 < ImportScripts::Base o.on('-p [PASS]', :OPTIONAL, 'MySQL password. Without argument, reads password from STDIN.') {|s| self.password = s || :ask } o.on('-d DBNAME', :REQUIRED, 'Name of SMF database') {|s| self.database = s } o.on('-f PREFIX', :REQUIRED, "Table names prefix [\"#{self.prefix}\"]") {|s| self.prefix = s } + o.on('-t TIMEZONE', :REQUIRED, 'Timezone used by SMF2 [auto-detected from PHP]') {|s| self.timezone = s } end end diff --git a/script/import_scripts/vbulletin.rb b/script/import_scripts/vbulletin.rb index 5fc8f17a0f..a3543c8b08 100644 --- a/script/import_scripts/vbulletin.rb +++ b/script/import_scripts/vbulletin.rb @@ -1,680 +1,486 @@ require File.expand_path(File.dirname(__FILE__) + "/base.rb") -require "optparse" -require "csv" +require 'mysql2' +require 'htmlentities' class ImportScripts::VBulletin < ImportScripts::Base + BATCH_SIZE = 1000 - attr_reader :options + # CHANGE THESE BEFORE RUNNING THE IMPORTER + DATABASE = "iref" + TIMEZONE = "Asia/Kolkata" - def self.run - options = Options.new + def initialize + super - begin - options.parse! - rescue OptionParser::MissingArgument, OptionParser::InvalidArgument => e - $stderr.puts e.to_s.capitalize - $stderr.puts options.usage - exit 1 - end + @old_username_to_new_usernames = {} - new(options).perform - end + @tz = TZInfo::Timezone.get(TIMEZONE) - def initialize(options) - super() + @htmlentities = HTMLEntities.new - @options = options + @client = Mysql2::Client.new( + host: "localhost", + username: "root", + database: DATABASE + ) end def execute - load_groups_mapping - load_groups - load_users - - load_categories_mappings - load_categories - load_categories_permissions - - load_topics - load_posts - import_groups import_users - create_groups_membership - import_categories - # import_category_groups - - preprocess_posts - import_topics import_posts - postprocess_posts - close_topics - - puts "", "Done" + post_process_posts end - private + def import_groups + puts "", "importing groups..." - ############################################################################ - # LOAD # - ############################################################################ + groups = mysql_query <<-SQL + SELECT usergroupid, title + FROM usergroup + ORDER BY usergroupid + SQL - def default_csv_options - @@default_csv_options ||= { headers: true, header_converters: :symbol } + create_groups(groups) do |group| + { + id: group["usergroupid"], + name: @htmlentities.decode(group["title"]).strip + } end + end - def load_groups_mapping - return if @options.group_mapping.blank? + def import_users + puts "", "importing users" - puts "", "Loading groups mappings from '#{@options.group_mapping}'..." + user_count = mysql_query("SELECT COUNT(userid) count FROM user").first["count"] - data = File.read(@options.group_mapping) - @groups_mappings = CSV.parse(data, default_csv_options).map { |row| row.to_hash } + # TODO: add email back in when using real data - @mapped_groups = {} - @new_group_ids = {} + batches(BATCH_SIZE) do |offset| + users = mysql_query <<-SQL + SELECT userid, username, homepage, usertitle, usergroupid, joindate + FROM user + ORDER BY userid + LIMIT #{BATCH_SIZE} + OFFSET #{offset} + SQL - @groups_mappings.each do |gm| - @mapped_groups[gm[:old_id]] = gm - @new_group_ids[gm[:new_id]] ||= true - end + break if users.size < 1 - puts "Loaded #{@groups_mappings.count} groups mappings for #{@new_group_ids.count} groups!" - end - - def load_groups - puts "", "Loading groups from '#{@options.user_group}'..." - - data = File.read(@options.user_group) - @all_groups = CSV.parse(data, default_csv_options).map { |row| row.to_hash } - - # reject unmapped groups - @groups = @all_groups.reject { |group| !@new_group_ids.has_key?(group[:usergroupid]) } - - puts "Loaded #{@groups.count} out of #{@all_groups.count} groups!" - end - - def load_users - puts "", "Loading users from '#{@options.user}'..." - - data = File.read(@options.user) - csv_options = default_csv_options.merge({ col_sep: "\t", quote_char: "\u200B" }) - @users = CSV.parse(data, csv_options).map { |row| row.to_hash } - original_count = @users.count - - if @mapped_groups.try(:size) > 0 - # show some stats - group_ids = Set.new(@users.map { |user| user[:usergroupid].to_i }) - group_ids.sort.each do |group_id| - count = @users.select { |user| user[:usergroupid].to_i == group_id }.count - group = @all_groups.select { |group| group[:usergroupid].to_i == group_id }.first.try(:[], :title) - puts "\t- #{count} users in usergroup ##{group_id} (#{group})" - end - # reject users from unmapped groups - @users.reject! { |user| !@mapped_groups.has_key?(user[:usergroupid]) } - # change mapped groups - @users.each { |user| user[:usergroupid] = @mapped_groups[user[:usergroupid]][:new_id] } - end - - puts "Loaded #{@users.count} out of #{original_count} users!" - end - - def load_categories_mappings - return if @options.forum_mapping.blank? - - puts "", "Loading categories mappings from '#{@options.forum_mapping}'..." - - data = File.read(@options.forum_mapping) - @categories_mappings = CSV.parse(data, default_csv_options).map { |row| row.to_hash } - - @mapped_categories = {} - @new_category_ids = {} - - @categories_mappings.each do |cm| - @mapped_categories[cm[:old_id]] = cm - @new_category_ids[cm[:new_id]] ||= true - end - - puts "Loaded #{@categories_mappings.count} categories mappings for #{@new_category_ids.count} categories!" - end - - def load_categories - puts "", "Loading categories from '#{@options.forum}'..." - - data = File.read(@options.forum) - @categories = CSV.parse(data, default_csv_options).map { |row| row.to_hash } - original_count = @categories.count - - if @new_category_ids.try(:size) > 0 - # reject unmapped categories - @categories.reject! { |category| !@new_category_ids.has_key?(category[:forumid]) } - # update mapped categories' title - @categories.each { |category| category[:title] = @mapped_categories[category[:forumid]][:new_name] } - end - - puts "Loaded #{@categories.count} out of #{original_count} categories!" - end - - # extracted from the "bitfield_vbulletin.xml" file - VB_FORUM_PERMISSIONS_CAN_VIEW = 1 - VB_FORUM_PERMISSIONS_CAN_VIEW_THREADS = 524288 - VB_FORUM_PERMISSIONS_CAN_REPLY_OWN = 32 - VB_FORUM_PERMISSIONS_CAN_REPLY_OTHERS = 64 - VB_FORUM_PERMISSIONS_CAN_POST_NEW = 16 - - def translate_forum_permissions(permissions) - can_see = ((permissions & VB_FORUM_PERMISSIONS_CAN_VIEW) | (permissions & VB_FORUM_PERMISSIONS_CAN_VIEW_THREADS)) > 0 - can_reply = ((permissions & VB_FORUM_PERMISSIONS_CAN_REPLY_OWN) | (permissions & VB_FORUM_PERMISSIONS_CAN_REPLY_OTHERS)) > 0 - can_create = (permissions & VB_FORUM_PERMISSIONS_CAN_POST_NEW) > 0 - return CategoryGroup.permission_types[:full] if can_create - return CategoryGroup.permission_types[:create_post] if can_reply - return CategoryGroup.permission_types[:readonly] if can_see - nil - end - - def load_categories_permissions - puts "", "Loading categories permissions from '#{@options.forum_permission}'..." - - data = File.read(@options.forum_permission) - @categories_permissions = CSV.parse(data, default_csv_options).map { |row| row.to_hash } - original_count = @categories_permissions.count - - # reject unmapped groups - if @mapped_groups.try(:size) > 0 - @categories_permissions.reject! { |cp| !@mapped_groups.has_key?(cp[:usergroupid]) } - end - - # reject unmapped categories - if @mapped_categories.try(:size) > 0 - @categories_permissions.reject! { |cp| !@mapped_categories.has_key?(cp[:forumid]) } - end - - # translate permissions - @categories_permissions.each do |cp| - cp[:permission] = translate_forum_permissions(cp[:forumpermissions].to_i) - cp[:usergroupid] = @mapped_groups[cp[:usergroupid]][:new_id] - cp[:forumid] = @mapped_categories[cp[:forumid]][:new_id] - end - - # clean permissions up - @categories_permissions.reject! { |cp| cp[:permission].nil? } - - puts "Loaded #{@categories_permissions.count} out of #{original_count} categories permissions!" - end - - def load_topics - puts "", "Loading topics from '#{@options.thread}'..." - - data = File.read(@options.thread) - csv_options = default_csv_options.merge({ col_sep: "\t", quote_char: "\u200B" }) - @topics = CSV.parse(data, csv_options).map { |row| row.to_hash } - original_count = @topics.count - - if @mapped_categories.try(:size) > 0 - # reject topics from unmapped categories - @topics.reject! { |topic| !@mapped_categories.has_key?(topic[:forumid]) } - # change mapped categories - @topics.each do |topic| - topic[:old_forumid] = topic[:forumid] - topic[:forumid] = @mapped_categories[topic[:forumid]][:new_id] - end - end - - puts "Loaded #{@topics.count} out of #{original_count} topics!" - end - - def load_posts - puts "", "Loading posts from '#{@options.post}'..." - - data = File.read(@options.post) - csv_options = default_csv_options.merge({ col_sep: "\t", quote_char: "\u200B" }) - @posts = CSV.parse(data, csv_options).map { |row| row.to_hash } - original_count = @posts.count - - # reject posts without topics - topic_ids = Set.new(@topics.map { |t| t[:threadid] }) - @posts.reject! { |post| !topic_ids.include?(post[:threadid]) } - - puts "Loaded #{@posts.count} out of #{original_count} posts!" - end - - ############################################################################ - # IMPORT # - ############################################################################ - - def import_groups - puts "", "Importing groups..." - - # sort the groups - @groups.sort_by! { |group| group[:usergroupid].to_i } - - create_groups(@groups) do |group| - { - id: group[:usergroupid], - name: group[:title], - } - end - - end - - def import_users - puts "", "Importing users..." - - # sort the users - @users.sort_by! { |user| user[:userid].to_i } - - @old_username_to_new_usernames = {} - - create_users(@users) do |user| - @old_username_to_new_usernames[user[:username]] = UserNameSuggester.fix_username(user[:username]) + create_users(users, total: user_count, offset: offset) do |user| + username = @htmlentities.decode(user["username"]).strip { - id: user[:userid], - username: @old_username_to_new_usernames[user[:username]], - email: user[:email], - website: user[:homepage], - title: user[:usertitle], - primary_group_id: group_id_from_imported_group_id(user[:usergroupid]), - merge: true, + id: user["userid"], + name: username, + username: username, + email: user["email"].presence || fake_email, + website: user["homepage"].strip, + title: @htmlentities.decode(user["usertitle"]).strip, + primary_group_id: group_id_from_imported_group_id(user["usergroupid"]), + created_at: parse_timestamp(user["joindate"]), + post_create_action: proc do |u| + @old_username_to_new_usernames[user["username"]] = u.username + import_profile_picture(user, u) + import_profile_background(user, u) + end } end + end + end + def import_profile_picture(old_user, imported_user) + query = mysql_query <<-SQL + SELECT filedata, filename + FROM customavatar + WHERE userid = #{old_user["userid"]} + ORDER BY dateline DESC + LIMIT 1 + SQL + + picture = query.first + + return if picture.nil? + + file = Tempfile.new("profile-picture") + file.write(picture["filedata"].encode("ASCII-8BIT").force_encoding("UTF-8")) + file.rewind + + upload = Upload.create_for(imported_user.id, file, picture["filename"], file.size) + + return if !upload.persisted? + + imported_user.create_user_avatar + imported_user.user_avatar.update(custom_upload_id: upload.id) + imported_user.update(uploaded_avatar_id: upload.id) + ensure + file.close rescue nil + file.unlind rescue nil + end + + def import_profile_background(old_user, imported_user) + query = mysql_query <<-SQL + SELECT filedata, filename + FROM customprofilepic + WHERE userid = #{old_user["userid"]} + ORDER BY dateline DESC + LIMIT 1 + SQL + + background = query.first + + return if background.nil? + + file = Tempfile.new("profile-background") + file.write(background["filedata"].encode("ASCII-8BIT").force_encoding("UTF-8")) + file.rewind + + upload = Upload.create_for(imported_user.id, file, background["filename"], file.size) + + return if !upload.persisted? + + imported_user.user_profile.update(profile_background: upload.url) + ensure + file.close rescue nil + file.unlink rescue nil + end + + def import_categories + puts "", "importing top level categories..." + + categories = mysql_query("SELECT forumid, title, description, displayorder, parentid FROM forum ORDER BY forumid").to_a + + top_level_categories = categories.select { |c| c["parentid"] == -1 } + + create_categories(top_level_categories) do |category| + { + id: category["forumid"], + name: @htmlentities.decode(category["title"]).strip, + position: category["displayorder"], + description: @htmlentities.decode(category["description"]).strip + } end - def create_groups_membership - puts "", "Creating groups membership..." + puts "", "importing children categories..." - Group.find_each do |group| - begin - next if group.automatic + children_categories = categories.select { |c| c["parentid"] != -1 } + top_level_category_ids = Set.new(top_level_categories.map { |c| c["forumid"] }) - puts "\t#{group.name}" - - next if GroupUser.where(group_id: group.id).count > 0 - - user_ids_in_group = User.where(primary_group_id: group.id).pluck(:id).to_a - next if user_ids_in_group.size == 0 - - values = user_ids_in_group.map { |user_id| "(#{group.id}, #{user_id}, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)" }.join(",") - - User.exec_sql <<-SQL - BEGIN; - INSERT INTO group_users (group_id, user_id, created_at, updated_at) VALUES #{values}; - COMMIT; - SQL - - Group.reset_counters(group.id, :group_users) - rescue Exception => e - puts e.message - puts e.backtrace.join("\n") - end + # cut down the tree to only 2 levels of categories + children_categories.each do |cc| + while !top_level_category_ids.include?(cc["parentid"]) + cc["parentid"] = categories.detect { |c| c["forumid"] == cc["parentid"] }["parentid"] end end - def import_categories - puts "", "Importing categories..." - - # sort categories - @categories.sort_by! { |category| category[:forumid].to_i } - - create_categories(@categories) do |category| - { - id: category[:forumid], - name: category[:title].strip[0...50], - position: category[:displayorder].to_i, - description: category[:description], - } - end - + create_categories(children_categories) do |category| + { + id: category["forumid"], + name: @htmlentities.decode(category["title"]).strip, + position: category["displayorder"], + description: @htmlentities.decode(category["description"]).strip, + parent_category_id: category_from_imported_category_id(category["parentid"]).try(:[], "id") + } end + end - def import_category_groups - puts "", "Importing category groups..." + def import_topics + puts "", "importing topics..." - # TODO - end + # keep track of closed topics + @closed_topic_ids = [] - def preprocess_posts - puts "", "Preprocessing posts..." + topic_count = mysql_query("SELECT COUNT(threadid) count FROM thread").first["count"] - current = 0 - max = @posts.size + batches(BATCH_SIZE) do |offset| + topics = mysql_query <<-SQL + SELECT t.threadid threadid, t.title title, forumid, open, postuserid, t.dateline dateline, views, t.visible visible, sticky, + p.pagetext raw + FROM thread t + JOIN post p ON p.postid = t.firstpostid + ORDER BY t.threadid + LIMIT #{BATCH_SIZE} + OFFSET #{offset} + SQL - @posts.each do |post| - post[:raw] = preprocess_post_raw(post[:pagetext]) - current += 1 - print_status(current, max) - end - end - - def preprocess_post_raw(raw) - return "" if raw.blank? - - raw = raw.gsub(/(\\r)?\\n/, "\n") - .gsub("\\t", "\t") - - # remove attachments - raw = raw.gsub(/\[attach[^\]]*\]\d+\[\/attach\]/i, "") - - # [HTML]...[/HTML] - raw = raw.gsub(/\[html\]/i, "\n```html\n") - .gsub(/\[\/html\]/i, "\n```\n") - - # [PHP]...[/PHP] - raw = raw.gsub(/\[php\]/i, "\n```php\n") - .gsub(/\[\/php\]/i, "\n```\n") - - # [HIGHLIGHT="..."] - raw = raw.gsub(/\[highlight="?(\w+)"?\]/i) { "\n```#{$1.downcase}\n" } - - # [CODE]...[/CODE] - # [HIGHLIGHT]...[/HIGHLIGHT] - raw = raw.gsub(/\[\/?code\]/i, "\n```\n") - .gsub(/\[\/?highlight\]/i, "\n```\n") - - # [SAMP]...[/SAMP] - raw = raw.gsub(/\[\/?samp\]/i, "`") - - # replace all chevrons with HTML entities - # NOTE: must be done - # - AFTER all the "code" processing - # - BEFORE the "quote" processing - raw = raw.gsub(/`([^`]+)`/im) { "`" + $1.gsub("<", "\u2603") + "`" } - .gsub("<", "<") - .gsub("\u2603", "<") - - raw = raw.gsub(/`([^`]+)`/im) { "`" + $1.gsub(">", "\u2603") + "`" } - .gsub(">", ">") - .gsub("\u2603", ">") - - # [URL=...]...[/URL] - raw = raw.gsub(/\[url="?(.+?)"?\](.+)\[\/url\]/i) { "[#{$2}](#{$1})" } - - # [URL]...[/URL] - # [MP3]...[/MP3] - raw = raw.gsub(/\[\/?url\]/i, "") - .gsub(/\[\/?mp3\]/i, "") - - # [MENTION][/MENTION] - raw = raw.gsub(/\[mention\](.+?)\[\/mention\]/i) do - old_username = $1 - if @old_username_to_new_usernames.has_key?(old_username) - old_username = @old_username_to_new_usernames[old_username] - end - "@#{old_username}" - end - - # [MENTION=][/MENTION] - raw = raw.gsub(/\[mention="?(\d+)"?\](.+?)\[\/mention\]/i) do - user_id, old_username = $1, $2 - if user = @users.select { |u| u[:userid] == user_id }.first - old_username = @old_username_to_new_usernames[user[:username]] || user[:username] - end - "@#{old_username}" - end - - # [QUOTE]...[/QUOTE] - raw = raw.gsub(/\[quote\](.+?)\[\/quote\]/im) { "\n> #{$1}\n" } - - # [QUOTE=]...[/QUOTE] - raw = raw.gsub(/\[quote=([^;\]]+)\](.+?)\[\/quote\]/im) do - old_username, quote = $1, $2 - if @old_username_to_new_usernames.has_key?(old_username) - old_username = @old_username_to_new_usernames[old_username] - end - "\n[quote=\"#{old_username}\"]\n#{quote}\n[/quote]\n" - end - - # [YOUTUBE][/YOUTUBE] - raw = raw.gsub(/\[youtube\](.+?)\[\/youtube\]/i) { "\n//youtu.be/#{$1}\n" } - - # [VIDEO=youtube;]...[/VIDEO] - raw = raw.gsub(/\[video=youtube;([^\]]+)\].*?\[\/video\]/i) { "\n//youtu.be/#{$1}\n" } - - raw - end - - def import_topics - puts "", "Importing topics..." - - # keep track of closed topics - @closed_topic_ids = [] - - # sort the topics - @topics.sort_by! { |topic| topic[:threadid].to_i } - - create_posts(@topics) do |topic| - id = "thread#" + topic[:threadid] - - # store the list of closed topics - @closed_topic_ids << id if topic[:open] == "0" - - next if post_id_from_imported_post_id(id) - next unless post = @posts.select { |p| p[:postid] == topic[:firstpostid] }.first + break if topics.size < 1 + create_posts(topics, total: topic_count, offset: offset) do |topic| + raw = preprocess_post_raw(topic["raw"]) rescue nil + next if raw.blank? + topic_id = "thread-#{topic["threadid"]}" + @closed_topic_ids << topic_id if topic["open"] == "0" t = { - id: id, - user_id: user_id_from_imported_user_id(topic[:postuserid]) || Discourse::SYSTEM_USER_ID, - title: CGI.unescapeHTML(topic[:title]).strip[0...255], - category: category_from_imported_category_id(topic[:forumid]).try(:name), - raw: post[:raw], - created_at: Time.at(topic[:dateline].to_i), - visible: topic[:visible].to_i == 1, - views: topic[:views].to_i, + id: topic_id, + user_id: user_id_from_imported_user_id(topic["postuserid"]) || Discourse::SYSTEM_USER_ID, + title: @htmlentities.decode(topic["title"]).strip[0...255], + category: category_from_imported_category_id(topic["forumid"]).try(:name), + raw: raw, + created_at: parse_timestamp(topic["dateline"]), + visible: topic["visible"].to_i == 1, + views: topic["views"], } - - if topic[:sticky].to_i == 1 - t[:pinned_at] = t[:created_at] - end - - # tag - if (tag = @mapped_categories[topic[:old_forumid]][:tag] || "").present? - t[:custom_fields] ||= {} - t[:custom_fields]['tag'] = tag - end - + t[:pinned_at] = t[:created_at] if topic["sticky"].to_i == 1 t end end + end - def import_posts - puts "", "Importing posts..." + def import_posts + puts "", "importing posts..." - # reject all first posts - first_post_ids = Set.new(@topics.map { |t| t[:firstpostid] }) - posts_to_import = @posts.reject { |post| first_post_ids.include?(post[:postid]) } + # make sure `firstpostid` is indexed + mysql_query("CREATE INDEX firstpostid_index ON thread (firstpostid)") - # sort the posts - @posts.sort_by! { |post| post[:postid].to_i } + post_count = mysql_query("SELECT COUNT(postid) count FROM post WHERE postid NOT IN (SELECT firstpostid FROM thread)").first["count"] - create_posts(posts_to_import) do |post| - next unless t = topic_lookup_from_imported_post_id("thread#" + post[:threadid]) + batches(BATCH_SIZE) do |offset| + posts = mysql_query <<-SQL + SELECT postid, userid, threadid, pagetext raw, dateline, visible, parentid + FROM post + WHERE postid NOT IN (SELECT firstpostid FROM thread) + ORDER BY postid + LIMIT #{BATCH_SIZE} + OFFSET #{offset} + SQL + break if posts.size < 1 + + create_posts(posts, total: post_count, offset: offset) do |post| + raw = preprocess_post_raw(post["raw"]) rescue nil + next if raw.blank? + next unless topic = topic_lookup_from_imported_post_id("thread-#{post["threadid"]}") p = { - id: post[:postid], - user_id: user_id_from_imported_user_id(post[:userid]) || Discourse::SYSTEM_USER_ID, - topic_id: t[:topic_id], - raw: post[:raw], - created_at: Time.at(post[:dateline].to_i), - hidden: post[:visible].to_i == 0, + id: post["postid"], + user_id: user_id_from_imported_user_id(post["userid"]) || Discourse::SYSTEM_USER_ID, + topic_id: topic[:topic_id], + raw: raw, + created_at: parse_timestamp(post["dateline"]), + hidden: post["visible"].to_i == 0, } - - if (edit_reason = (post[:editreason] || "").gsub("NULL", "")).present? - p[:edit_reason] = edit_reason - end - - if parent = topic_lookup_from_imported_post_id(post[:parentid]) + if parent = topic_lookup_from_imported_post_id(post["parentid"]) p[:reply_to_post_number] = parent[:post_number] end - p end end + end - def postprocess_posts - puts "", "Postprocessing posts..." + def close_topics + puts "", "Closing topics..." - current = 0 - max = @posts.size + sql = <<-SQL + WITH closed_topic_ids AS ( + SELECT t.id AS topic_id + FROM post_custom_fields pcf + JOIN posts p ON p.id = pcf.post_id + JOIN topics t ON t.id = p.topic_id + WHERE pcf.name = 'import_id' + AND pcf.value IN (?) + ) + UPDATE topics + SET closed = true + WHERE id IN (SELECT topic_id FROM closed_topic_ids) + SQL - @posts.each do |post| - begin - new_raw = postprocess_post_raw(post[:raw]) + Topic.exec_sql(sql, @closed_topic_ids) + end - if new_raw != post[:raw] - new_id = post_id_from_imported_post_id(post[:postid]) - p = Post.find_by(id: new_id) - if p.nil? - puts "Could not save the post-processed raw of the post ##{new_id} (previous id: ##{post[:postid]})" - next - end - p.raw = new_raw - p.save - end - rescue Exception => e - puts "", "-" * 100 - puts e.message - puts e.backtrace.join("\n") - puts "-" * 100, "" - next - ensure - current += 1 - print_status(current, max) + def post_process_posts + puts "", "Postprocessing posts..." + + current = 0 + max = Post.count + + Post.find_each do |post| + begin + new_raw = postprocess_post_raw(post.raw) + if new_raw != post.raw + post.raw = new_raw + post.save end + rescue PrettyText::JavaScriptError + nil + ensure + print_status(current += 1, max) + end + end + end + + def preprocess_post_raw(raw) + return "" if raw.blank? + + # decode HTML entities + raw = @htmlentities.decode(raw) + + # fix whitespaces + raw = raw.gsub(/(\\r)?\\n/, "\n") + .gsub("\\t", "\t") + + # remove attachments + raw = raw.gsub(/\[attach[^\]]*\]\d+\[\/attach\]/i, "") + + # [HTML]...[/HTML] + raw = raw.gsub(/\[html\]/i, "\n```html\n") + .gsub(/\[\/html\]/i, "\n```\n") + + # [PHP]...[/PHP] + raw = raw.gsub(/\[php\]/i, "\n```php\n") + .gsub(/\[\/php\]/i, "\n```\n") + + # [HIGHLIGHT="..."] + raw = raw.gsub(/\[highlight="?(\w+)"?\]/i) { "\n```#{$1.downcase}\n" } + + # [CODE]...[/CODE] + # [HIGHLIGHT]...[/HIGHLIGHT] + raw = raw.gsub(/\[\/?code\]/i, "\n```\n") + .gsub(/\[\/?highlight\]/i, "\n```\n") + + # [SAMP]...[/SAMP] + raw = raw.gsub(/\[\/?samp\]/i, "`") + + # replace all chevrons with HTML entities + # NOTE: must be done + # - AFTER all the "code" processing + # - BEFORE the "quote" processing + raw = raw.gsub(/`([^`]+)`/im) { "`" + $1.gsub("<", "\u2603") + "`" } + .gsub("<", "<") + .gsub("\u2603", "<") + + raw = raw.gsub(/`([^`]+)`/im) { "`" + $1.gsub(">", "\u2603") + "`" } + .gsub(">", ">") + .gsub("\u2603", ">") + + # [URL=...]...[/URL] + raw = raw.gsub(/\[url="?(.+?)"?\](.+)\[\/url\]/i) { "[#{$2}](#{$1})" } + + # [URL]...[/URL] + # [MP3]...[/MP3] + raw = raw.gsub(/\[\/?url\]/i, "") + .gsub(/\[\/?mp3\]/i, "") + + # [MENTION][/MENTION] + raw = raw.gsub(/\[mention\](.+?)\[\/mention\]/i) do + old_username = $1 + if @old_username_to_new_usernames.has_key?(old_username) + old_username = @old_username_to_new_usernames[old_username] + end + "@#{old_username}" + end + + # [MENTION=][/MENTION] + # raw = raw.gsub(/\[mention="?(\d+)"?\](.+?)\[\/mention\]/i) do + # user_id, old_username = $1, $2 + # if user = @users.select { |u| u[:userid] == user_id }.first + # old_username = @old_username_to_new_usernames[user[:username]] || user[:username] + # end + # "@#{old_username}" + # end + + # [QUOTE]...[/QUOTE] + raw = raw.gsub(/\[quote\](.+?)\[\/quote\]/im) { "\n> #{$1}\n" } + + # [QUOTE=]...[/QUOTE] + raw = raw.gsub(/\[quote=([^;\]]+)\](.+?)\[\/quote\]/im) do + old_username, quote = $1, $2 + if @old_username_to_new_usernames.has_key?(old_username) + old_username = @old_username_to_new_usernames[old_username] + end + "\n[quote=\"#{old_username}\"]\n#{quote}\n[/quote]\n" + end + + # [YOUTUBE][/YOUTUBE] + raw = raw.gsub(/\[youtube\](.+?)\[\/youtube\]/i) { "\n//youtu.be/#{$1}\n" } + + # [VIDEO=youtube;]...[/VIDEO] + raw = raw.gsub(/\[video=youtube;([^\]]+)\].*?\[\/video\]/i) { "\n//youtu.be/#{$1}\n" } + + raw + end + + def postprocess_post_raw(raw) + # [QUOTE=;]...[/QUOTE] + raw = raw.gsub(/\[quote=([^;]+);(\d+)\](.+?)\[\/quote\]/im) do + old_username, post_id, quote = $1, $2, $3 + + if @old_username_to_new_usernames.has_key?(old_username) + old_username = @old_username_to_new_usernames[old_username] + end + + if topic_lookup = topic_lookup_from_imported_post_id(post_id) + post_number = topic_lookup[:post_number] + topic_id = topic_lookup[:topic_id] + "\n[quote=\"#{old_username},post:#{post_number},topic:#{topic_id}\"]\n#{quote}\n[/quote]\n" + else + "\n[quote=\"#{old_username}\"]\n#{quote}\n[/quote]\n" end end - def postprocess_post_raw(raw) - # [QUOTE=;]...[/QUOTE] - raw = raw.gsub(/\[quote=([^;]+);(\d+)\](.+?)\[\/quote\]/im) do - old_username, post_id, quote = $1, $2, $3 - - if @old_username_to_new_usernames.has_key?(old_username) - old_username = @old_username_to_new_usernames[old_username] - end - - if topic_lookup = topic_lookup_from_imported_post_id(post_id) - post_number = topic_lookup[:post_number] - topic_id = topic_lookup[:topic_id] - "\n[quote=\"#{old_username},post:#{post_number},topic:#{topic_id}\"]\n#{quote}\n[/quote]\n" - else - "\n[quote=\"#{old_username}\"]\n#{quote}\n[/quote]\n" - end + # [THREAD][/THREAD] + # ==> http://my.discourse.org/t/slug/ + raw = raw.gsub(/\[thread\](\d+)\[\/thread\]/i) do + thread_id = $1 + if topic_lookup = topic_lookup_from_imported_post_id("thread-#{thread_id}") + topic_lookup[:url] + else + $& end - - # [THREAD][/THREAD] - # ==> http://my.discourse.org/t/slug/ - raw = raw.gsub(/\[thread\](\d+)\[\/thread\]/i) do - thread_id = $1 - if topic_lookup = topic_lookup_from_imported_post_id("thread#" + thread_id) - topic_lookup[:url] - else - $& - end - end - - # [THREAD=]...[/THREAD] - # ==> [...](http://my.discourse.org/t/slug/) - raw = raw.gsub(/\[thread=(\d+)\](.+?)\[\/thread\]/i) do - thread_id, link = $1, $2 - if topic_lookup = topic_lookup_from_imported_post_id("thread#" + thread_id) - url = topic_lookup[:url] - "[#{link}](#{url})" - else - $& - end - end - - # [POST][/POST] - # ==> http://my.discourse.org/t/slug// - raw = raw.gsub(/\[post\](\d+)\[\/post\]/i) do - post_id = $1 - if topic_lookup = topic_lookup_from_imported_post_id(post_id) - topic_lookup[:url] - else - $& - end - end - - # [POST=]...[/POST] - # ==> [...](http://my.discourse.org/t///) - raw = raw.gsub(/\[post=(\d+)\](.+?)\[\/post\]/i) do - post_id, link = $1, $2 - if topic_lookup = topic_lookup_from_imported_post_id(post_id) - url = topic_lookup[:url] - "[#{link}](#{url})" - else - $& - end - end - - raw end - def close_topics - puts "", "Closing topics..." - - sql = <<-SQL - WITH closed_topic_ids AS ( - SELECT t.id AS topic_id - FROM post_custom_fields pcf - JOIN posts p ON p.id = pcf.post_id - JOIN topics t ON t.id = p.topic_id - WHERE pcf.name = 'import_id' - AND pcf.value IN (?) - ) - UPDATE topics - SET closed = true - WHERE id IN (SELECT topic_id FROM closed_topic_ids) - SQL - - Topic.exec_sql(sql, @closed_topic_ids) + # [THREAD=]...[/THREAD] + # ==> [...](http://my.discourse.org/t/slug/) + raw = raw.gsub(/\[thread=(\d+)\](.+?)\[\/thread\]/i) do + thread_id, link = $1, $2 + if topic_lookup = topic_lookup_from_imported_post_id("thread-#{thread_id}") + url = topic_lookup[:url] + "[#{link}](#{url})" + else + $& + end end - ############################################################################ - # OPTIONS # - ############################################################################ - - class Options - - attr_accessor :user_group, :user, :forum, :forum_permission, :thread, :post - attr_accessor :group_mapping, :forum_mapping - - def parse!(args = ARGV) - parser.parse!(args) - - [:user_group, :user, :forum, :forum_permission, :thread, :post].each do |option_name| - option = self.send(option_name) - raise OptionParser::MissingArgument.new(option_name) if option.nil? - raise OptionParser::InvalidArgument.new("#{option} file does not exist") if !File.exists?(option) - end + # [POST][/POST] + # ==> http://my.discourse.org/t/slug// + raw = raw.gsub(/\[post\](\d+)\[\/post\]/i) do + post_id = $1 + if topic_lookup = topic_lookup_from_imported_post_id(post_id) + topic_lookup[:url] + else + $& end - - def usage - parser.to_s - end - - private - - def parser - @parser ||= OptionParser.new(nil, 50) do |opts| - opts.banner = "Usage:\truby #{File.basename($0)} [options]" - opts.on("--user-group USER-GROUP.csv", "list of usergroups") { |s| self.user_group = s } - opts.on("--user USER.csv", "list of users") { |s| self.user = s } - opts.on("--forum FORUM.csv", "list of forums") { |s| self.forum = s } - opts.on("--forum-permission FORUM-PERMISSION.csv", "list of forum permissions") { |s| self.forum_permission = s } - opts.on("--thread THREAD.csv", "list of threads") { |s| self.thread = s } - opts.on("--post POST.csv", "list of posts") { |s| self.post = s } - opts.on("--group-mapping GROUP-MAPPING.csv", "list of group mappings") { |s| self.group_mapping = s } - opts.on("--forum-mapping FORUM-MAPPING.csv", "list of forum mappings") { |s| self.forum_mapping = s } - end - end end + # [POST=]...[/POST] + # ==> [...](http://my.discourse.org/t///) + raw = raw.gsub(/\[post=(\d+)\](.+?)\[\/post\]/i) do + post_id, link = $1, $2 + if topic_lookup = topic_lookup_from_imported_post_id(post_id) + url = topic_lookup[:url] + "[#{link}](#{url})" + else + $& + end + end + + raw + end + + def parse_timestamp(timestamp) + Time.zone.at(@tz.utc_to_local(timestamp)) + end + + def fake_email + SecureRandom.hex << "@domain.com" + end + + def mysql_query(sql) + @client.query(sql, cache_rows: false) + end + end -ImportScripts::VBulletin.run +ImportScripts::VBulletin.new.perform diff --git a/script/import_scripts/vbulletin_old.rb b/script/import_scripts/vbulletin_old.rb new file mode 100644 index 0000000000..6e27f034b2 --- /dev/null +++ b/script/import_scripts/vbulletin_old.rb @@ -0,0 +1,680 @@ +require File.expand_path(File.dirname(__FILE__) + "/base.rb") +require "optparse" +require "csv" + +class ImportScripts::VBulletinOld < ImportScripts::Base + + attr_reader :options + + def self.run + options = Options.new + + begin + options.parse! + rescue OptionParser::MissingArgument, OptionParser::InvalidArgument => e + $stderr.puts e.to_s.capitalize + $stderr.puts options.usage + exit 1 + end + + new(options).perform + end + + def initialize(options) + super() + + @options = options + end + + def execute + load_groups_mapping + load_groups + load_users + + load_categories_mappings + load_categories + load_categories_permissions + + load_topics + load_posts + + import_groups + import_users + create_groups_membership + + import_categories + # import_category_groups + + preprocess_posts + + import_topics + import_posts + + postprocess_posts + + close_topics + + puts "", "Done" + end + + private + + ############################################################################ + # LOAD # + ############################################################################ + + def default_csv_options + @@default_csv_options ||= { headers: true, header_converters: :symbol } + end + + def load_groups_mapping + return if @options.group_mapping.blank? + + puts "", "Loading groups mappings from '#{@options.group_mapping}'..." + + data = File.read(@options.group_mapping) + @groups_mappings = CSV.parse(data, default_csv_options).map { |row| row.to_hash } + + @mapped_groups = {} + @new_group_ids = {} + + @groups_mappings.each do |gm| + @mapped_groups[gm[:old_id]] = gm + @new_group_ids[gm[:new_id]] ||= true + end + + puts "Loaded #{@groups_mappings.count} groups mappings for #{@new_group_ids.count} groups!" + end + + def load_groups + puts "", "Loading groups from '#{@options.user_group}'..." + + data = File.read(@options.user_group) + @all_groups = CSV.parse(data, default_csv_options).map { |row| row.to_hash } + + # reject unmapped groups + @groups = @all_groups.reject { |group| !@new_group_ids.has_key?(group[:usergroupid]) } + + puts "Loaded #{@groups.count} out of #{@all_groups.count} groups!" + end + + def load_users + puts "", "Loading users from '#{@options.user}'..." + + data = File.read(@options.user) + csv_options = default_csv_options.merge({ col_sep: "\t", quote_char: "\u200B" }) + @users = CSV.parse(data, csv_options).map { |row| row.to_hash } + original_count = @users.count + + if @mapped_groups.try(:size) > 0 + # show some stats + group_ids = Set.new(@users.map { |user| user[:usergroupid].to_i }) + group_ids.sort.each do |group_id| + count = @users.select { |user| user[:usergroupid].to_i == group_id }.count + group = @all_groups.select { |group| group[:usergroupid].to_i == group_id }.first.try(:[], :title) + puts "\t- #{count} users in usergroup ##{group_id} (#{group})" + end + # reject users from unmapped groups + @users.reject! { |user| !@mapped_groups.has_key?(user[:usergroupid]) } + # change mapped groups + @users.each { |user| user[:usergroupid] = @mapped_groups[user[:usergroupid]][:new_id] } + end + + puts "Loaded #{@users.count} out of #{original_count} users!" + end + + def load_categories_mappings + return if @options.forum_mapping.blank? + + puts "", "Loading categories mappings from '#{@options.forum_mapping}'..." + + data = File.read(@options.forum_mapping) + @categories_mappings = CSV.parse(data, default_csv_options).map { |row| row.to_hash } + + @mapped_categories = {} + @new_category_ids = {} + + @categories_mappings.each do |cm| + @mapped_categories[cm[:old_id]] = cm + @new_category_ids[cm[:new_id]] ||= true + end + + puts "Loaded #{@categories_mappings.count} categories mappings for #{@new_category_ids.count} categories!" + end + + def load_categories + puts "", "Loading categories from '#{@options.forum}'..." + + data = File.read(@options.forum) + @categories = CSV.parse(data, default_csv_options).map { |row| row.to_hash } + original_count = @categories.count + + if @new_category_ids.try(:size) > 0 + # reject unmapped categories + @categories.reject! { |category| !@new_category_ids.has_key?(category[:forumid]) } + # update mapped categories' title + @categories.each { |category| category[:title] = @mapped_categories[category[:forumid]][:new_name] } + end + + puts "Loaded #{@categories.count} out of #{original_count} categories!" + end + + # extracted from the "bitfield_vbulletin.xml" file + VB_FORUM_PERMISSIONS_CAN_VIEW = 1 + VB_FORUM_PERMISSIONS_CAN_VIEW_THREADS = 524288 + VB_FORUM_PERMISSIONS_CAN_REPLY_OWN = 32 + VB_FORUM_PERMISSIONS_CAN_REPLY_OTHERS = 64 + VB_FORUM_PERMISSIONS_CAN_POST_NEW = 16 + + def translate_forum_permissions(permissions) + can_see = ((permissions & VB_FORUM_PERMISSIONS_CAN_VIEW) | (permissions & VB_FORUM_PERMISSIONS_CAN_VIEW_THREADS)) > 0 + can_reply = ((permissions & VB_FORUM_PERMISSIONS_CAN_REPLY_OWN) | (permissions & VB_FORUM_PERMISSIONS_CAN_REPLY_OTHERS)) > 0 + can_create = (permissions & VB_FORUM_PERMISSIONS_CAN_POST_NEW) > 0 + return CategoryGroup.permission_types[:full] if can_create + return CategoryGroup.permission_types[:create_post] if can_reply + return CategoryGroup.permission_types[:readonly] if can_see + nil + end + + def load_categories_permissions + puts "", "Loading categories permissions from '#{@options.forum_permission}'..." + + data = File.read(@options.forum_permission) + @categories_permissions = CSV.parse(data, default_csv_options).map { |row| row.to_hash } + original_count = @categories_permissions.count + + # reject unmapped groups + if @mapped_groups.try(:size) > 0 + @categories_permissions.reject! { |cp| !@mapped_groups.has_key?(cp[:usergroupid]) } + end + + # reject unmapped categories + if @mapped_categories.try(:size) > 0 + @categories_permissions.reject! { |cp| !@mapped_categories.has_key?(cp[:forumid]) } + end + + # translate permissions + @categories_permissions.each do |cp| + cp[:permission] = translate_forum_permissions(cp[:forumpermissions].to_i) + cp[:usergroupid] = @mapped_groups[cp[:usergroupid]][:new_id] + cp[:forumid] = @mapped_categories[cp[:forumid]][:new_id] + end + + # clean permissions up + @categories_permissions.reject! { |cp| cp[:permission].nil? } + + puts "Loaded #{@categories_permissions.count} out of #{original_count} categories permissions!" + end + + def load_topics + puts "", "Loading topics from '#{@options.thread}'..." + + data = File.read(@options.thread) + csv_options = default_csv_options.merge({ col_sep: "\t", quote_char: "\u200B" }) + @topics = CSV.parse(data, csv_options).map { |row| row.to_hash } + original_count = @topics.count + + if @mapped_categories.try(:size) > 0 + # reject topics from unmapped categories + @topics.reject! { |topic| !@mapped_categories.has_key?(topic[:forumid]) } + # change mapped categories + @topics.each do |topic| + topic[:old_forumid] = topic[:forumid] + topic[:forumid] = @mapped_categories[topic[:forumid]][:new_id] + end + end + + puts "Loaded #{@topics.count} out of #{original_count} topics!" + end + + def load_posts + puts "", "Loading posts from '#{@options.post}'..." + + data = File.read(@options.post) + csv_options = default_csv_options.merge({ col_sep: "\t", quote_char: "\u200B" }) + @posts = CSV.parse(data, csv_options).map { |row| row.to_hash } + original_count = @posts.count + + # reject posts without topics + topic_ids = Set.new(@topics.map { |t| t[:threadid] }) + @posts.reject! { |post| !topic_ids.include?(post[:threadid]) } + + puts "Loaded #{@posts.count} out of #{original_count} posts!" + end + + ############################################################################ + # IMPORT # + ############################################################################ + + def import_groups + puts "", "Importing groups..." + + # sort the groups + @groups.sort_by! { |group| group[:usergroupid].to_i } + + create_groups(@groups) do |group| + { + id: group[:usergroupid], + name: group[:title], + } + end + + end + + def import_users + puts "", "Importing users..." + + # sort the users + @users.sort_by! { |user| user[:userid].to_i } + + @old_username_to_new_usernames = {} + + create_users(@users) do |user| + @old_username_to_new_usernames[user[:username]] = UserNameSuggester.fix_username(user[:username]) + + { + id: user[:userid], + username: @old_username_to_new_usernames[user[:username]], + email: user[:email], + website: user[:homepage], + title: user[:usertitle], + primary_group_id: group_id_from_imported_group_id(user[:usergroupid]), + merge: true, + } + end + + end + + def create_groups_membership + puts "", "Creating groups membership..." + + Group.find_each do |group| + begin + next if group.automatic + + puts "\t#{group.name}" + + next if GroupUser.where(group_id: group.id).count > 0 + + user_ids_in_group = User.where(primary_group_id: group.id).pluck(:id).to_a + next if user_ids_in_group.size == 0 + + values = user_ids_in_group.map { |user_id| "(#{group.id}, #{user_id}, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)" }.join(",") + + User.exec_sql <<-SQL + BEGIN; + INSERT INTO group_users (group_id, user_id, created_at, updated_at) VALUES #{values}; + COMMIT; + SQL + + Group.reset_counters(group.id, :group_users) + rescue Exception => e + puts e.message + puts e.backtrace.join("\n") + end + end + end + + def import_categories + puts "", "Importing categories..." + + # sort categories + @categories.sort_by! { |category| category[:forumid].to_i } + + create_categories(@categories) do |category| + { + id: category[:forumid], + name: category[:title].strip[0...50], + position: category[:displayorder].to_i, + description: category[:description], + } + end + + end + + def import_category_groups + puts "", "Importing category groups..." + + # TODO + end + + def preprocess_posts + puts "", "Preprocessing posts..." + + current = 0 + max = @posts.size + + @posts.each do |post| + post[:raw] = preprocess_post_raw(post[:pagetext]) + current += 1 + print_status(current, max) + end + end + + def preprocess_post_raw(raw) + return "" if raw.blank? + + raw = raw.gsub(/(\\r)?\\n/, "\n") + .gsub("\\t", "\t") + + # remove attachments + raw = raw.gsub(/\[attach[^\]]*\]\d+\[\/attach\]/i, "") + + # [HTML]...[/HTML] + raw = raw.gsub(/\[html\]/i, "\n```html\n") + .gsub(/\[\/html\]/i, "\n```\n") + + # [PHP]...[/PHP] + raw = raw.gsub(/\[php\]/i, "\n```php\n") + .gsub(/\[\/php\]/i, "\n```\n") + + # [HIGHLIGHT="..."] + raw = raw.gsub(/\[highlight="?(\w+)"?\]/i) { "\n```#{$1.downcase}\n" } + + # [CODE]...[/CODE] + # [HIGHLIGHT]...[/HIGHLIGHT] + raw = raw.gsub(/\[\/?code\]/i, "\n```\n") + .gsub(/\[\/?highlight\]/i, "\n```\n") + + # [SAMP]...[/SAMP] + raw = raw.gsub(/\[\/?samp\]/i, "`") + + # replace all chevrons with HTML entities + # NOTE: must be done + # - AFTER all the "code" processing + # - BEFORE the "quote" processing + raw = raw.gsub(/`([^`]+)`/im) { "`" + $1.gsub("<", "\u2603") + "`" } + .gsub("<", "<") + .gsub("\u2603", "<") + + raw = raw.gsub(/`([^`]+)`/im) { "`" + $1.gsub(">", "\u2603") + "`" } + .gsub(">", ">") + .gsub("\u2603", ">") + + # [URL=...]...[/URL] + raw = raw.gsub(/\[url="?(.+?)"?\](.+)\[\/url\]/i) { "[#{$2}](#{$1})" } + + # [URL]...[/URL] + # [MP3]...[/MP3] + raw = raw.gsub(/\[\/?url\]/i, "") + .gsub(/\[\/?mp3\]/i, "") + + # [MENTION][/MENTION] + raw = raw.gsub(/\[mention\](.+?)\[\/mention\]/i) do + old_username = $1 + if @old_username_to_new_usernames.has_key?(old_username) + old_username = @old_username_to_new_usernames[old_username] + end + "@#{old_username}" + end + + # [MENTION=][/MENTION] + raw = raw.gsub(/\[mention="?(\d+)"?\](.+?)\[\/mention\]/i) do + user_id, old_username = $1, $2 + if user = @users.select { |u| u[:userid] == user_id }.first + old_username = @old_username_to_new_usernames[user[:username]] || user[:username] + end + "@#{old_username}" + end + + # [QUOTE]...[/QUOTE] + raw = raw.gsub(/\[quote\](.+?)\[\/quote\]/im) { "\n> #{$1}\n" } + + # [QUOTE=]...[/QUOTE] + raw = raw.gsub(/\[quote=([^;\]]+)\](.+?)\[\/quote\]/im) do + old_username, quote = $1, $2 + if @old_username_to_new_usernames.has_key?(old_username) + old_username = @old_username_to_new_usernames[old_username] + end + "\n[quote=\"#{old_username}\"]\n#{quote}\n[/quote]\n" + end + + # [YOUTUBE][/YOUTUBE] + raw = raw.gsub(/\[youtube\](.+?)\[\/youtube\]/i) { "\n//youtu.be/#{$1}\n" } + + # [VIDEO=youtube;]...[/VIDEO] + raw = raw.gsub(/\[video=youtube;([^\]]+)\].*?\[\/video\]/i) { "\n//youtu.be/#{$1}\n" } + + raw + end + + def import_topics + puts "", "Importing topics..." + + # keep track of closed topics + @closed_topic_ids = [] + + # sort the topics + @topics.sort_by! { |topic| topic[:threadid].to_i } + + create_posts(@topics) do |topic| + id = "thread#" + topic[:threadid] + + # store the list of closed topics + @closed_topic_ids << id if topic[:open] == "0" + + next if post_id_from_imported_post_id(id) + next unless post = @posts.select { |p| p[:postid] == topic[:firstpostid] }.first + + t = { + id: id, + user_id: user_id_from_imported_user_id(topic[:postuserid]) || Discourse::SYSTEM_USER_ID, + title: CGI.unescapeHTML(topic[:title]).strip[0...255], + category: category_from_imported_category_id(topic[:forumid]).try(:name), + raw: post[:raw], + created_at: Time.at(topic[:dateline].to_i), + visible: topic[:visible].to_i == 1, + views: topic[:views].to_i, + } + + if topic[:sticky].to_i == 1 + t[:pinned_at] = t[:created_at] + end + + # tag + if (tag = @mapped_categories[topic[:old_forumid]][:tag] || "").present? + t[:custom_fields] ||= {} + t[:custom_fields]['tag'] = tag + end + + t + end + end + + def import_posts + puts "", "Importing posts..." + + # reject all first posts + first_post_ids = Set.new(@topics.map { |t| t[:firstpostid] }) + posts_to_import = @posts.reject { |post| first_post_ids.include?(post[:postid]) } + + # sort the posts + @posts.sort_by! { |post| post[:postid].to_i } + + create_posts(posts_to_import) do |post| + next unless t = topic_lookup_from_imported_post_id("thread#" + post[:threadid]) + + p = { + id: post[:postid], + user_id: user_id_from_imported_user_id(post[:userid]) || Discourse::SYSTEM_USER_ID, + topic_id: t[:topic_id], + raw: post[:raw], + created_at: Time.at(post[:dateline].to_i), + hidden: post[:visible].to_i == 0, + } + + if (edit_reason = (post[:editreason] || "").gsub("NULL", "")).present? + p[:edit_reason] = edit_reason + end + + if parent = topic_lookup_from_imported_post_id(post[:parentid]) + p[:reply_to_post_number] = parent[:post_number] + end + + p + end + end + + def postprocess_posts + puts "", "Postprocessing posts..." + + current = 0 + max = @posts.size + + @posts.each do |post| + begin + new_raw = postprocess_post_raw(post[:raw]) + + if new_raw != post[:raw] + new_id = post_id_from_imported_post_id(post[:postid]) + p = Post.find_by(id: new_id) + if p.nil? + puts "Could not save the post-processed raw of the post ##{new_id} (previous id: ##{post[:postid]})" + next + end + p.raw = new_raw + p.save + end + rescue Exception => e + puts "", "-" * 100 + puts e.message + puts e.backtrace.join("\n") + puts "-" * 100, "" + next + ensure + current += 1 + print_status(current, max) + end + end + end + + def postprocess_post_raw(raw) + # [QUOTE=;]...[/QUOTE] + raw = raw.gsub(/\[quote=([^;]+);(\d+)\](.+?)\[\/quote\]/im) do + old_username, post_id, quote = $1, $2, $3 + + if @old_username_to_new_usernames.has_key?(old_username) + old_username = @old_username_to_new_usernames[old_username] + end + + if topic_lookup = topic_lookup_from_imported_post_id(post_id) + post_number = topic_lookup[:post_number] + topic_id = topic_lookup[:topic_id] + "\n[quote=\"#{old_username},post:#{post_number},topic:#{topic_id}\"]\n#{quote}\n[/quote]\n" + else + "\n[quote=\"#{old_username}\"]\n#{quote}\n[/quote]\n" + end + end + + # [THREAD][/THREAD] + # ==> http://my.discourse.org/t/slug/ + raw = raw.gsub(/\[thread\](\d+)\[\/thread\]/i) do + thread_id = $1 + if topic_lookup = topic_lookup_from_imported_post_id("thread#" + thread_id) + topic_lookup[:url] + else + $& + end + end + + # [THREAD=]...[/THREAD] + # ==> [...](http://my.discourse.org/t/slug/) + raw = raw.gsub(/\[thread=(\d+)\](.+?)\[\/thread\]/i) do + thread_id, link = $1, $2 + if topic_lookup = topic_lookup_from_imported_post_id("thread#" + thread_id) + url = topic_lookup[:url] + "[#{link}](#{url})" + else + $& + end + end + + # [POST][/POST] + # ==> http://my.discourse.org/t/slug// + raw = raw.gsub(/\[post\](\d+)\[\/post\]/i) do + post_id = $1 + if topic_lookup = topic_lookup_from_imported_post_id(post_id) + topic_lookup[:url] + else + $& + end + end + + # [POST=]...[/POST] + # ==> [...](http://my.discourse.org/t///) + raw = raw.gsub(/\[post=(\d+)\](.+?)\[\/post\]/i) do + post_id, link = $1, $2 + if topic_lookup = topic_lookup_from_imported_post_id(post_id) + url = topic_lookup[:url] + "[#{link}](#{url})" + else + $& + end + end + + raw + end + + def close_topics + puts "", "Closing topics..." + + sql = <<-SQL + WITH closed_topic_ids AS ( + SELECT t.id AS topic_id + FROM post_custom_fields pcf + JOIN posts p ON p.id = pcf.post_id + JOIN topics t ON t.id = p.topic_id + WHERE pcf.name = 'import_id' + AND pcf.value IN (?) + ) + UPDATE topics + SET closed = true + WHERE id IN (SELECT topic_id FROM closed_topic_ids) + SQL + + Topic.exec_sql(sql, @closed_topic_ids) + end + + ############################################################################ + # OPTIONS # + ############################################################################ + + class Options + + attr_accessor :user_group, :user, :forum, :forum_permission, :thread, :post + attr_accessor :group_mapping, :forum_mapping + + def parse!(args = ARGV) + parser.parse!(args) + + [:user_group, :user, :forum, :forum_permission, :thread, :post].each do |option_name| + option = self.send(option_name) + raise OptionParser::MissingArgument.new(option_name) if option.nil? + raise OptionParser::InvalidArgument.new("#{option} file does not exist") if !File.exists?(option) + end + end + + def usage + parser.to_s + end + + private + + def parser + @parser ||= OptionParser.new(nil, 50) do |opts| + opts.banner = "Usage:\truby #{File.basename($0)} [options]" + opts.on("--user-group USER-GROUP.csv", "list of usergroups") { |s| self.user_group = s } + opts.on("--user USER.csv", "list of users") { |s| self.user = s } + opts.on("--forum FORUM.csv", "list of forums") { |s| self.forum = s } + opts.on("--forum-permission FORUM-PERMISSION.csv", "list of forum permissions") { |s| self.forum_permission = s } + opts.on("--thread THREAD.csv", "list of threads") { |s| self.thread = s } + opts.on("--post POST.csv", "list of posts") { |s| self.post = s } + opts.on("--group-mapping GROUP-MAPPING.csv", "list of group mappings") { |s| self.group_mapping = s } + opts.on("--forum-mapping FORUM-MAPPING.csv", "list of forum mappings") { |s| self.forum_mapping = s } + end + end + end + +end + +ImportScripts::VBulletinOld.run diff --git a/script/measure.rb b/script/measure.rb index d643625fb6..bc49614acd 100644 --- a/script/measure.rb +++ b/script/measure.rb @@ -1,4 +1,4 @@ -# using this script to try figure out why Rails 2 is slower that 1.9 +# using this script to try figure out why Ruby 2 is slower that 1.9 # require 'flamegraph' diff --git a/script/micro_bench.rb b/script/micro_bench.rb new file mode 100644 index 0000000000..b7caadf01e --- /dev/null +++ b/script/micro_bench.rb @@ -0,0 +1,27 @@ +require 'benchmark/ips' +require File.expand_path("../../config/environment", __FILE__) + +conn = ActiveRecord::Base.connection.raw_connection + +Benchmark.ips do |b| + b.report("simple") do + User.first.name + end + + b.report("simple with select") do + User.select("name").first.name + end + + b.report("pluck with first") do + User.pluck(:name).first + end + + b.report("pluck with limit") do + User.limit(1).pluck(:name).first + end + + b.report("raw") do + conn.exec("SELECT name FROM users LIMIT 1").getvalue(0,0) + end +end + diff --git a/spec/components/archetype_spec.rb b/spec/components/archetype_spec.rb index b7124d24d3..a4cffe853e 100644 --- a/spec/components/archetype_spec.rb +++ b/spec/components/archetype_spec.rb @@ -8,11 +8,11 @@ describe Archetype do context 'default archetype' do it 'has an Archetype by default' do - Archetype.list.should be_present + expect(Archetype.list).to be_present end it 'has an id of default' do - Archetype.list.first.id.should == Archetype.default + expect(Archetype.list.first.id).to eq(Archetype.default) end context 'duplicate' do @@ -23,7 +23,7 @@ describe Archetype do end it 'does not add the same archetype twice' do - Archetype.list.size.should == @old_size + expect(Archetype.list.size).to eq(@old_size) end end @@ -35,8 +35,8 @@ describe Archetype do it 'has one more element' do @list = Archetype.list.dup Archetype.register('glados') - Archetype.list.size.should == @list.size + 1 - Archetype.list.find {|a| a.id == 'glados'}.should be_present + expect(Archetype.list.size).to eq(@list.size + 1) + expect(Archetype.list.find {|a| a.id == 'glados'}).to be_present end end diff --git a/spec/components/auth/default_current_user_provider_spec.rb b/spec/components/auth/default_current_user_provider_spec.rb index ccad63bbed..b8cbd5fe9a 100644 --- a/spec/components/auth/default_current_user_provider_spec.rb +++ b/spec/components/auth/default_current_user_provider_spec.rb @@ -18,7 +18,7 @@ describe Auth::DefaultCurrentUserProvider do it "finds a user for a correct per-user api key" do user = Fabricate(:user) ApiKey.create!(key: "hello", user_id: user.id, created_by_id: -1) - provider("/?api_key=hello").current_user.id.should == user.id + expect(provider("/?api_key=hello").current_user.id).to eq(user.id) end it "raises for a user pretending" do @@ -31,20 +31,46 @@ describe Auth::DefaultCurrentUserProvider do }.to raise_error(Discourse::InvalidAccess) end + it "raises for a user with a mismatching ip" do + user = Fabricate(:user) + ApiKey.create!(key: "hello", user_id: user.id, created_by_id: -1, allowed_ips: ['10.0.0.0/24']) + + expect{ + provider("/?api_key=hello&api_username=#{user.username.downcase}", "REMOTE_ADDR" => "10.1.0.1").current_user + }.to raise_error(Discourse::InvalidAccess) + + end + + it "allows a user with a matching ip" do + user = Fabricate(:user) + ApiKey.create!(key: "hello", user_id: user.id, created_by_id: -1, allowed_ips: ['100.0.0.0/24']) + + found_user = provider("/?api_key=hello&api_username=#{user.username.downcase}", + "REMOTE_ADDR" => "100.0.0.22").current_user + + expect(found_user.id).to eq(user.id) + + + found_user = provider("/?api_key=hello&api_username=#{user.username.downcase}", + "HTTP_X_FORWARDED_FOR" => "10.1.1.1, 100.0.0.22").current_user + expect(found_user.id).to eq(user.id) + + end + it "finds a user for a correct system api key" do user = Fabricate(:user) ApiKey.create!(key: "hello", created_by_id: -1) - provider("/?api_key=hello&api_username=#{user.username.downcase}").current_user.id.should == user.id + expect(provider("/?api_key=hello&api_username=#{user.username.downcase}").current_user.id).to eq(user.id) end it "should not update last seen for message bus" do - provider("/message-bus/anything/goes", method: "POST").should_update_last_seen?.should == false - provider("/message-bus/anything/goes", method: "GET").should_update_last_seen?.should == false + expect(provider("/message-bus/anything/goes", method: "POST").should_update_last_seen?).to eq(false) + expect(provider("/message-bus/anything/goes", method: "GET").should_update_last_seen?).to eq(false) end it "should update last seen for others" do - provider("/topic/anything/goes", method: "POST").should_update_last_seen?.should == true - provider("/topic/anything/goes", method: "GET").should_update_last_seen?.should == true + expect(provider("/topic/anything/goes", method: "POST").should_update_last_seen?).to eq(true) + expect(provider("/topic/anything/goes", method: "GET").should_update_last_seen?).to eq(true) end end diff --git a/spec/components/auth/facebook_authenticator_spec.rb b/spec/components/auth/facebook_authenticator_spec.rb index 9ee4d303c4..70c14491b7 100644 --- a/spec/components/auth/facebook_authenticator_spec.rb +++ b/spec/components/auth/facebook_authenticator_spec.rb @@ -27,7 +27,7 @@ describe Auth::FacebookAuthenticator do result = authenticator.after_authenticate(hash) - result.user.id.should == user.id + expect(result.user.id).to eq(user.id) end it 'can create a proper result for non existing users' do @@ -49,8 +49,8 @@ describe Auth::FacebookAuthenticator do result = authenticator.after_authenticate(hash) - result.user.should == nil - result.extra_data[:name].should == "bob bob" + expect(result.user).to eq(nil) + expect(result.extra_data[:name]).to eq("bob bob") end end diff --git a/spec/components/auth/google_oauth2_authenticator_spec.rb b/spec/components/auth/google_oauth2_authenticator_spec.rb index bb481658c2..f14996e13c 100644 --- a/spec/components/auth/google_oauth2_authenticator_spec.rb +++ b/spec/components/auth/google_oauth2_authenticator_spec.rb @@ -28,7 +28,7 @@ describe Auth::GoogleOAuth2Authenticator do result = authenticator.after_authenticate(hash) - result.user.id.should == user.id + expect(result.user.id).to eq(user.id) end it 'can create a proper result for non existing users' do @@ -50,8 +50,8 @@ describe Auth::GoogleOAuth2Authenticator do authenticator = described_class.new result = authenticator.after_authenticate(hash) - result.user.should == nil - result.extra_data[:name].should == "Jane Doe" + expect(result.user).to eq(nil) + expect(result.extra_data[:name]).to eq("Jane Doe") end end diff --git a/spec/components/auth/open_id_authenticator_spec.rb b/spec/components/auth/open_id_authenticator_spec.rb index df89c0748f..78fdc00ad2 100644 --- a/spec/components/auth/open_id_authenticator_spec.rb +++ b/spec/components/auth/open_id_authenticator_spec.rb @@ -14,12 +14,12 @@ describe Auth::OpenIdAuthenticator do user = Fabricate(:user) response = OpenStruct.new(identity_url: 'abc') result = auth.after_authenticate(info: {email: user.email}, extra: {response: response}) - result.user.should == user + expect(result.user).to eq(user) end it "raises an exception when email is missing" do auth = Auth::OpenIdAuthenticator.new("test", "id", trusted: true) response = OpenStruct.new(identity_url: 'abc') - -> { auth.after_authenticate(info: {}, extra: { response: response }) }.should raise_error(Discourse::InvalidParameters) + expect { auth.after_authenticate(info: {}, extra: { response: response }) }.to raise_error(Discourse::InvalidParameters) end end diff --git a/spec/components/avatar_lookup_spec.rb b/spec/components/avatar_lookup_spec.rb index 163e4a7124..9285360061 100644 --- a/spec/components/avatar_lookup_spec.rb +++ b/spec/components/avatar_lookup_spec.rb @@ -12,15 +12,15 @@ describe AvatarLookup do end it 'returns nil if user_id does not exists' do - @avatar_lookup[0].should == nil + expect(@avatar_lookup[0]).to eq(nil) end it 'returns nil if user_id is nil' do - @avatar_lookup[nil].should == nil + expect(@avatar_lookup[nil]).to eq(nil) end it 'returns user if user_id exists' do - @avatar_lookup[user.id].should eq(user) + expect(@avatar_lookup[user.id]).to eq(user) end end end diff --git a/spec/components/avatar_upload_service_spec.rb b/spec/components/avatar_upload_service_spec.rb index a003d26303..2be4ccc0ad 100644 --- a/spec/components/avatar_upload_service_spec.rb +++ b/spec/components/avatar_upload_service_spec.rb @@ -16,19 +16,19 @@ describe AvatarUploadService do let(:avatar_file) { AvatarUploadService.new(file, :image) } it "should have a filesize" do - avatar_file.filesize.should > 0 + expect(avatar_file.filesize).to be > 0 end it "should have a filename" do - avatar_file.filename.should == "logo.png" + expect(avatar_file.filename).to eq("logo.png") end it "should have a file" do - avatar_file.file.should == file.tempfile + expect(avatar_file.file).to eq(file.tempfile) end it "should have a source as 'image'" do - avatar_file.source.should == :image + expect(avatar_file.source).to eq(:image) end end @@ -38,19 +38,19 @@ describe AvatarUploadService do before { FileHelper.stubs(:download).returns(logo) } it "should have a filesize" do - avatar_file.filesize.should > 0 + expect(avatar_file.filesize).to be > 0 end it "should have a filename" do - avatar_file.filename.should == "logo.png" + expect(avatar_file.filename).to eq("logo.png") end it "should have a file" do - avatar_file.file.should == logo + expect(avatar_file.file).to eq(logo) end it "should have a source as 'url'" do - avatar_file.source.should == :url + expect(avatar_file.source).to eq(:url) end end end diff --git a/spec/components/cache_spec.rb b/spec/components/cache_spec.rb index bbc1e84267..385ab58428 100644 --- a/spec/components/cache_spec.rb +++ b/spec/components/cache_spec.rb @@ -9,32 +9,23 @@ describe Cache do it "supports fixnum" do cache.write("num", 1) - cache.read("num").should == 1 + expect(cache.read("num")).to eq(1) end it "supports hash" do hash = {a: 1, b: [1,2,3]} cache.write("hash", hash) - cache.read("hash").should == hash + expect(cache.read("hash")).to eq(hash) end it "can be cleared" do + $redis.set("boo", "boo") cache.write("hello0", "world") cache.write("hello1", "world") cache.clear - cache.read("hello0").should == nil - end - - it "can delete by family" do - cache.write("key2", "test", family: "my_family") - cache.write("key", "test", expires_in: 1.minute, family: "my_family") - - cache.delete_by_family("my_family") - - cache.fetch("key").should == nil - cache.fetch("key2").should == nil - + expect($redis.get("boo")).to eq("boo") + expect(cache.read("hello0")).to eq(nil) end it "can delete correctly" do @@ -43,19 +34,26 @@ describe Cache do end cache.delete("key") - cache.fetch("key").should == nil + expect(cache.fetch("key")).to eq(nil) end - #TODO yuck on this mock it "calls setex in redis" do cache.delete("key") + cache.delete("bla") key = cache.namespaced_key("key") - $redis.expects(:setex).with(key, 60 , Marshal.dump("bob")) cache.fetch("key", expires_in: 1.minute) do "bob" end + + expect($redis.ttl(key)).to be_within(2.seconds).of(1.minute) + + # we always expire withing a day + cache.fetch("bla"){ "hi" } + + key = cache.namespaced_key("bla") + expect($redis.ttl(key)).to be_within(2.seconds).of(1.day) end it "can store and fetch correctly" do @@ -64,7 +62,7 @@ describe Cache do r = cache.fetch "key" do "bob" end - r.should == "bob" + expect(r).to eq("bob") end it "can fetch existing correctly" do @@ -73,6 +71,6 @@ describe Cache do r = cache.fetch "key" do "bob" end - r.should == "bill" + expect(r).to eq("bill") end end diff --git a/spec/components/category_list_spec.rb b/spec/components/category_list_spec.rb index 9d3f6df14e..104173d3b9 100644 --- a/spec/components/category_list_spec.rb +++ b/spec/components/category_list_spec.rb @@ -15,9 +15,9 @@ describe CategoryList do cat.save # uncategorized + this - CategoryList.new(Guardian.new admin).categories.count.should == 2 - CategoryList.new(Guardian.new user).categories.count.should == 0 - CategoryList.new(Guardian.new nil).categories.count.should == 0 + expect(CategoryList.new(Guardian.new admin).categories.count).to eq(2) + expect(CategoryList.new(Guardian.new user).categories.count).to eq(0) + expect(CategoryList.new(Guardian.new nil).categories.count).to eq(0) end it "doesn't show topics that you can't view" do @@ -36,14 +36,14 @@ describe CategoryList do CategoryFeaturedTopic.feature_topics - CategoryList.new(Guardian.new(admin)).categories.find { |x| x.name == public_cat.name }.displayable_topics.count.should == 2 - CategoryList.new(Guardian.new(admin)).categories.find { |x| x.name == private_cat.name }.displayable_topics.count.should == 1 + expect(CategoryList.new(Guardian.new(admin)).categories.find { |x| x.name == public_cat.name }.displayable_topics.count).to eq(2) + expect(CategoryList.new(Guardian.new(admin)).categories.find { |x| x.name == private_cat.name }.displayable_topics.count).to eq(1) - CategoryList.new(Guardian.new(user)).categories.find { |x| x.name == public_cat.name }.displayable_topics.count.should == 1 - CategoryList.new(Guardian.new(user)).categories.find { |x| x.name == private_cat.name }.should == nil + expect(CategoryList.new(Guardian.new(user)).categories.find { |x| x.name == public_cat.name }.displayable_topics.count).to eq(1) + expect(CategoryList.new(Guardian.new(user)).categories.find { |x| x.name == private_cat.name }).to eq(nil) - CategoryList.new(Guardian.new(nil)).categories.find { |x| x.name == public_cat.name }.displayable_topics.count.should == 1 - CategoryList.new(Guardian.new(nil)).categories.find { |x| x.name == private_cat.name }.should == nil + expect(CategoryList.new(Guardian.new(nil)).categories.find { |x| x.name == public_cat.name }.displayable_topics.count).to eq(1) + expect(CategoryList.new(Guardian.new(nil)).categories.find { |x| x.name == private_cat.name }).to eq(nil) end end @@ -54,33 +54,33 @@ describe CategoryList do context "without a featured topic" do it "should not return empty categories" do - category_list.categories.should be_blank + expect(category_list.categories).to be_blank end it "returns empty categories for those who can create them" do SiteSetting.stubs(:allow_uncategorized_topics).returns(true) Guardian.any_instance.expects(:can_create?).with(Category).returns(true) - category_list.categories.should_not be_blank + expect(category_list.categories).not_to be_blank end it "returns empty categories with descriptions" do Fabricate(:category, description: 'The category description.') Guardian.any_instance.expects(:can_create?).with(Category).returns(false) - category_list.categories.should_not be_blank + expect(category_list.categories).not_to be_blank end it 'returns the empty category and a non-empty category for those who can create them' do SiteSetting.stubs(:allow_uncategorized_topics).returns(true) Fabricate(:topic, category: Fabricate(:category)) Guardian.any_instance.expects(:can_create?).with(Category).returns(true) - category_list.categories.size.should == 3 - category_list.categories.should include(topic_category) + expect(category_list.categories.size).to eq(3) + expect(category_list.categories).to include(topic_category) end it "doesn't return empty uncategorized category to admins if allow_uncategorized_topics is false" do SiteSetting.stubs(:allow_uncategorized_topics).returns(false) - CategoryList.new(Guardian.new(user)).categories.should be_empty - CategoryList.new(Guardian.new(admin)).categories.map(&:id).should_not include(SiteSetting.uncategorized_category_id) + expect(CategoryList.new(Guardian.new(user)).categories).to be_empty + expect(CategoryList.new(Guardian.new(admin)).categories.map(&:id)).not_to include(SiteSetting.uncategorized_category_id) end end @@ -90,15 +90,15 @@ describe CategoryList do let(:category) { category_list.categories.first } it "should return the category" do - category.should be_present + expect(category).to be_present end it "returns the correct category" do - category.id.should == topic_category.id + expect(category.id).to eq(topic_category.id) end it "should contain our topic" do - category.featured_topics.include?(topic).should == true + expect(category.featured_topics.include?(topic)).to eq(true) end end @@ -120,16 +120,16 @@ describe CategoryList do it "returns categories in specified order" do cat1, cat2 = Fabricate(:category, position: 1), Fabricate(:category, position: 0) - category_ids.should == [cat2.id, cat1.id] + expect(category_ids).to eq([cat2.id, cat1.id]) end it "handles duplicate position values" do cat1, cat2, cat3, cat4 = Fabricate(:category, position: 0), Fabricate(:category, position: 0), Fabricate(:category, position: nil), Fabricate(:category, position: 0) first_three = category_ids[0,3] # The order is not deterministic - first_three.should include(cat1.id) - first_three.should include(cat2.id) - first_three.should include(cat4.id) - category_ids[-1].should == cat3.id + expect(first_three).to include(cat1.id) + expect(first_three).to include(cat2.id) + expect(first_three).to include(cat4.id) + expect(category_ids[-1]).to eq(cat3.id) end end @@ -141,12 +141,12 @@ describe CategoryList do it "returns categories in order of activity" do cat1 = Fabricate(:category, position: 0, posts_week: 1, posts_month: 1, posts_year: 1) cat2 = Fabricate(:category, position: 1, posts_week: 2, posts_month: 1, posts_year: 1) - category_ids.should == [cat2.id, cat1.id] + expect(category_ids).to eq([cat2.id, cat1.id]) end it "returns categories in order of id when there's no activity" do cat1, cat2 = Fabricate(:category, position: 1), Fabricate(:category, position: 0) - category_ids.should == [cat1.id, cat2.id] + expect(category_ids).to eq([cat1.id, cat2.id]) end end end diff --git a/spec/components/common_passwords/common_passwords_spec.rb b/spec/components/common_passwords/common_passwords_spec.rb index a100200580..f121343281 100644 --- a/spec/components/common_passwords/common_passwords_spec.rb +++ b/spec/components/common_passwords/common_passwords_spec.rb @@ -4,7 +4,7 @@ require_dependency "common_passwords/common_passwords" describe CommonPasswords do it "the passwords file should exist" do - File.exists?(described_class::PASSWORD_FILE).should eq(true) + expect(File.exists?(described_class::PASSWORD_FILE)).to eq(true) end describe "#common_password?" do @@ -15,25 +15,25 @@ describe CommonPasswords do it "returns false if password isn't in the common passwords list" do described_class.stubs(:password_list).returns(stub_everything(:include? => false)) @password = 'uncommonPassword' - subject.should eq(false) + expect(subject).to eq(false) end it "returns false if password is nil" do described_class.expects(:password_list).never @password = nil - subject.should eq(false) + expect(subject).to eq(false) end it "returns false if password is blank" do described_class.expects(:password_list).never @password = "" - subject.should eq(false) + expect(subject).to eq(false) end it "returns true if password is in the common passwords list" do described_class.stubs(:password_list).returns(stub_everything(:include? => true)) @password = "password" - subject.should eq(true) + expect(subject).to eq(true) end end @@ -45,7 +45,7 @@ describe CommonPasswords do described_class.stubs(:redis).returns(mock_redis) described_class.expects(:load_passwords).returns(['password']) list = described_class.password_list - list.should respond_to(:include?) + expect(list).to respond_to(:include?) end it "doesn't load the passwords file if redis has it" do @@ -55,7 +55,7 @@ describe CommonPasswords do described_class.stubs(:redis).returns(mock_redis) described_class.expects(:load_passwords).never list = described_class.password_list - list.should respond_to(:include?) + expect(list).to respond_to(:include?) end it "loads the passwords file if redis has an empty list" do @@ -65,7 +65,7 @@ describe CommonPasswords do described_class.stubs(:redis).returns(mock_redis) described_class.expects(:load_passwords).returns(['password']) list = described_class.password_list - list.should respond_to(:include?) + expect(list).to respond_to(:include?) end end @@ -73,7 +73,7 @@ describe CommonPasswords do it "tolerates it" do described_class.stubs(:redis).returns(stub_everything(sismember: false, exists: false, scard: 0)) File.stubs(:readlines).with(described_class::PASSWORD_FILE).raises(Errno::ENOENT) - described_class.common_password?("password").should eq(false) + expect(described_class.common_password?("password")).to eq(false) end end end diff --git a/spec/components/composer_messages_finder_spec.rb b/spec/components/composer_messages_finder_spec.rb index f119bf4daf..2a48b11a54 100644 --- a/spec/components/composer_messages_finder_spec.rb +++ b/spec/components/composer_messages_finder_spec.rb @@ -32,12 +32,12 @@ describe ComposerMessagesFinder do it "returns a message for a user who has not posted any topics" do user.expects(:created_topic_count).returns(9) - finder.check_education_message.should be_present + expect(finder.check_education_message).to be_present end it "returns no message when the user has posted enough topics" do user.expects(:created_topic_count).returns(10) - finder.check_education_message.should be_blank + expect(finder.check_education_message).to be_blank end end @@ -50,12 +50,12 @@ describe ComposerMessagesFinder do it "returns a message for a user who has not posted any topics" do user.expects(:post_count).returns(9) - finder.check_education_message.should be_present + expect(finder.check_education_message).to be_present end it "returns no message when the user has posted enough topics" do user.expects(:post_count).returns(10) - finder.check_education_message.should be_blank + expect(finder.check_education_message).to be_blank end end end @@ -68,12 +68,12 @@ describe ComposerMessagesFinder do it "has no message when `posted_too_much_in_topic?` is false" do user.expects(:posted_too_much_in_topic?).returns(false) - finder.check_new_user_many_replies.should be_blank + expect(finder.check_new_user_many_replies).to be_blank end it "has a message when a user has posted too much" do user.expects(:posted_too_much_in_topic?).returns(true) - finder.check_new_user_many_replies.should be_present + expect(finder.check_new_user_many_replies).to be_present end end @@ -83,44 +83,31 @@ describe ComposerMessagesFinder do let(:finder) { ComposerMessagesFinder.new(user, composerAction: 'createTopic') } let(:user) { Fabricate(:user) } - context "a user who we haven't checked for an avatar yet" do - it "returns no avatar message" do - finder.check_avatar_notification.should be_blank + context "success" do + let!(:message) { finder.check_avatar_notification } + + it "returns an avatar upgrade message" do + expect(message).to be_present + end + + it "creates a notified_about_avatar log" do + expect(UserHistory.exists_for_user?(user, :notified_about_avatar)).to eq(true) end end - context "a user who has been checked for a custom avatar" do - before do - UserHistory.create!(action: UserHistory.actions[:checked_for_custom_avatar], target_user_id: user.id ) - end + it "doesn't return notifications for new users" do + user.trust_level = TrustLevel[0] + expect(finder.check_avatar_notification).to be_blank + end - context "success" do - let!(:message) { finder.check_avatar_notification } - - it "returns an avatar upgrade message" do - message.should be_present - end - - it "creates a notified_about_avatar log" do - UserHistory.exists_for_user?(user, :notified_about_avatar).should == true - end - end - - it "doesn't return notifications for new users" do - user.trust_level = TrustLevel[0] - finder.check_avatar_notification.should be_blank - end - - it "doesn't return notifications for users who have custom avatars" do - user.uploaded_avatar_id = 1 - finder.check_avatar_notification.should be_blank - end - - it "doesn't notify users who have been notified already" do - UserHistory.create!(action: UserHistory.actions[:notified_about_avatar], target_user_id: user.id ) - finder.check_avatar_notification.should be_blank - end + it "doesn't return notifications for users who have custom avatars" do + user.uploaded_avatar_id = 1 + expect(finder.check_avatar_notification).to be_blank + end + it "doesn't notify users who have been notified already" do + UserHistory.create!(action: UserHistory.actions[:notified_about_avatar], target_user_id: user.id ) + expect(finder.check_avatar_notification).to be_blank end end @@ -140,11 +127,11 @@ describe ComposerMessagesFinder do it "does not give a message for new topics" do finder = ComposerMessagesFinder.new(user, composerAction: 'createTopic') - finder.check_sequential_replies.should be_blank + expect(finder.check_sequential_replies).to be_blank end it "does not give a message without a topic id" do - ComposerMessagesFinder.new(user, composerAction: 'reply').check_sequential_replies.should be_blank + expect(ComposerMessagesFinder.new(user, composerAction: 'reply').check_sequential_replies).to be_blank end context "reply" do @@ -153,39 +140,39 @@ describe ComposerMessagesFinder do it "does not give a message to users who are still in the 'education' phase" do user.stubs(:post_count).returns(9) - finder.check_sequential_replies.should be_blank + expect(finder.check_sequential_replies).to be_blank end it "doesn't notify a user it has already notified about sequential replies" do UserHistory.create!(action: UserHistory.actions[:notified_about_sequential_replies], target_user_id: user.id, topic_id: topic.id ) - finder.check_sequential_replies.should be_blank + expect(finder.check_sequential_replies).to be_blank end it "will notify you if it hasn't in the current topic" do UserHistory.create!(action: UserHistory.actions[:notified_about_sequential_replies], target_user_id: user.id, topic_id: topic.id+1 ) - finder.check_sequential_replies.should be_present + expect(finder.check_sequential_replies).to be_present end it "doesn't notify a user who has less than the `sequential_replies_threshold` threshold posts" do SiteSetting.stubs(:sequential_replies_threshold).returns(5) - finder.check_sequential_replies.should be_blank + expect(finder.check_sequential_replies).to be_blank end it "doesn't notify a user if another user posted" do Fabricate(:post, topic: topic, user: Fabricate(:user)) - finder.check_sequential_replies.should be_blank + expect(finder.check_sequential_replies).to be_blank end context "success" do let!(:message) { finder.check_sequential_replies } it "returns a message" do - message.should be_present + expect(message).to be_present end it "creates a notified_about_sequential_replies log" do - UserHistory.exists_for_user?(user, :notified_about_sequential_replies).should == true + expect(UserHistory.exists_for_user?(user, :notified_about_sequential_replies)).to eq(true) end end @@ -212,11 +199,11 @@ describe ComposerMessagesFinder do it "does not give a message for new topics" do finder = ComposerMessagesFinder.new(user, composerAction: 'createTopic') - finder.check_dominating_topic.should be_blank + expect(finder.check_dominating_topic).to be_blank end it "does not give a message without a topic id" do - ComposerMessagesFinder.new(user, composerAction: 'reply').check_dominating_topic.should be_blank + expect(ComposerMessagesFinder.new(user, composerAction: 'reply').check_dominating_topic).to be_blank end context "reply" do @@ -224,53 +211,53 @@ describe ComposerMessagesFinder do it "does not give a message to users who are still in the 'education' phase" do user.stubs(:post_count).returns(9) - finder.check_dominating_topic.should be_blank + expect(finder.check_dominating_topic).to be_blank end it "does not notify if the `summary_posts_required` has not been reached" do SiteSetting.stubs(:summary_posts_required).returns(100) - finder.check_dominating_topic.should be_blank + expect(finder.check_dominating_topic).to be_blank end it "doesn't notify a user it has already notified in this topic" do UserHistory.create!(action: UserHistory.actions[:notified_about_dominating_topic], topic_id: topic.id, target_user_id: user.id ) - finder.check_dominating_topic.should be_blank + expect(finder.check_dominating_topic).to be_blank end it "notifies a user if the topic is different" do UserHistory.create!(action: UserHistory.actions[:notified_about_dominating_topic], topic_id: topic.id+1, target_user_id: user.id ) - finder.check_dominating_topic.should be_present + expect(finder.check_dominating_topic).to be_present end it "doesn't notify a user if the topic has less than `summary_posts_required` posts" do SiteSetting.stubs(:summary_posts_required).returns(5) - finder.check_dominating_topic.should be_blank + expect(finder.check_dominating_topic).to be_blank end it "doesn't notify a user if they've posted less than the percentage" do SiteSetting.stubs(:dominating_topic_minimum_percent).returns(100) - finder.check_dominating_topic.should be_blank + expect(finder.check_dominating_topic).to be_blank end it "doesn't notify you if it's your own topic" do topic.update_column(:user_id, user.id) - finder.check_dominating_topic.should be_blank + expect(finder.check_dominating_topic).to be_blank end it "doesn't notify you in a private message" do topic.update_columns(category_id: nil, archetype: Archetype.private_message) - finder.check_dominating_topic.should be_blank + expect(finder.check_dominating_topic).to be_blank end context "success" do let!(:message) { finder.check_dominating_topic } it "returns a message" do - message.should be_present + expect(message).to be_present end it "creates a notified_about_dominating_topic log" do - UserHistory.exists_for_user?(user, :notified_about_dominating_topic).should == true + expect(UserHistory.exists_for_user?(user, :notified_about_dominating_topic)).to eq(true) end end @@ -283,8 +270,8 @@ describe ComposerMessagesFinder do let(:topic) { Fabricate(:topic) } it "does not give a message without a topic id" do - described_class.new(user, composerAction: 'createTopic').check_reviving_old_topic.should be_blank - described_class.new(user, composerAction: 'reply').check_reviving_old_topic.should be_blank + expect(described_class.new(user, composerAction: 'createTopic').check_reviving_old_topic).to be_blank + expect(described_class.new(user, composerAction: 'reply').check_reviving_old_topic).to be_blank end context "a reply" do @@ -295,12 +282,12 @@ describe ComposerMessagesFinder do it "does not notify if last post is recent" do topic = Fabricate(:topic, last_posted_at: 1.hour.ago) - described_class.new(user, composerAction: 'reply', topic_id: topic.id).check_reviving_old_topic.should be_blank + expect(described_class.new(user, composerAction: 'reply', topic_id: topic.id).check_reviving_old_topic).to be_blank end it "notifies if last post is old" do topic = Fabricate(:topic, last_posted_at: 181.days.ago) - described_class.new(user, composerAction: 'reply', topic_id: topic.id).check_reviving_old_topic.should_not be_blank + expect(described_class.new(user, composerAction: 'reply', topic_id: topic.id).check_reviving_old_topic).not_to be_blank end end @@ -311,12 +298,12 @@ describe ComposerMessagesFinder do it "does not notify if last post is new" do topic = Fabricate(:topic, last_posted_at: 1.hour.ago) - described_class.new(user, composerAction: 'reply', topic_id: topic.id).check_reviving_old_topic.should be_blank + expect(described_class.new(user, composerAction: 'reply', topic_id: topic.id).check_reviving_old_topic).to be_blank end it "does not notify if last post is old" do topic = Fabricate(:topic, last_posted_at: 365.days.ago) - described_class.new(user, composerAction: 'reply', topic_id: topic.id).check_reviving_old_topic.should be_blank + expect(described_class.new(user, composerAction: 'reply', topic_id: topic.id).check_reviving_old_topic).to be_blank end end end diff --git a/spec/components/concern/has_custom_fields_spec.rb b/spec/components/concern/has_custom_fields_spec.rb index c41f3ebc6d..aa36db5109 100644 --- a/spec/components/concern/has_custom_fields_spec.rb +++ b/spec/components/concern/has_custom_fields_spec.rb @@ -31,7 +31,7 @@ describe HasCustomFields do it "simple modification of custom fields" do test_item = CustomFieldsTestItem.new - test_item.custom_fields["a"].should == nil + expect(test_item.custom_fields["a"]).to eq(nil) test_item.custom_fields["bob"] = "marley" test_item.custom_fields["jack"] = "black" @@ -40,8 +40,8 @@ describe HasCustomFields do test_item = CustomFieldsTestItem.find(test_item.id) - test_item.custom_fields["bob"].should == "marley" - test_item.custom_fields["jack"].should == "black" + expect(test_item.custom_fields["bob"]).to eq("marley") + expect(test_item.custom_fields["jack"]).to eq("black") test_item.custom_fields.delete("bob") test_item.custom_fields["jack"] = "jill" @@ -49,42 +49,42 @@ describe HasCustomFields do test_item.save test_item = CustomFieldsTestItem.find(test_item.id) - test_item.custom_fields.should == {"jack" => "jill"} + expect(test_item.custom_fields).to eq({"jack" => "jill"}) end it "casts integers to string without error" do test_item = CustomFieldsTestItem.new - test_item.custom_fields["a"].should == nil + expect(test_item.custom_fields["a"]).to eq(nil) test_item.custom_fields["a"] = 0 - test_item.custom_fields["a"].should == 0 + expect(test_item.custom_fields["a"]).to eq(0) test_item.save # should be casted right after saving - test_item.custom_fields["a"].should == "0" + expect(test_item.custom_fields["a"]).to eq("0") test_item = CustomFieldsTestItem.find(test_item.id) - test_item.custom_fields["a"].should == "0" + expect(test_item.custom_fields["a"]).to eq("0") end it "reload loads from database" do test_item = CustomFieldsTestItem.new test_item.custom_fields["a"] = 0 - test_item.custom_fields["a"].should == 0 + expect(test_item.custom_fields["a"]).to eq(0) test_item.save # should be casted right after saving - test_item.custom_fields["a"].should == "0" + expect(test_item.custom_fields["a"]).to eq("0") CustomFieldsTestItem.exec_sql("UPDATE custom_fields_test_item_custom_fields SET value='1' WHERE custom_fields_test_item_id=? AND name='a'", test_item.id) # still the same, did not load - test_item.custom_fields["a"].should == "0" + expect(test_item.custom_fields["a"]).to eq("0") # refresh loads from database - test_item.reload.custom_fields["a"].should == "1" - test_item.custom_fields["a"].should == "1" + expect(test_item.reload.custom_fields["a"]).to eq("1") + expect(test_item.custom_fields["a"]).to eq("1") end it "double save actually saves" do @@ -97,7 +97,7 @@ describe HasCustomFields do test_item.save db_item = CustomFieldsTestItem.find(test_item.id) - db_item.custom_fields.should == {"a" => "b", "c" => "d"} + expect(db_item.custom_fields).to eq({"a" => "b", "c" => "d"}) end @@ -109,11 +109,22 @@ describe HasCustomFields do test_item.save db_item = CustomFieldsTestItem.find(test_item.id) - db_item.custom_fields.should == {"a" => ["b", "c", "d"]} + expect(db_item.custom_fields).to eq({"a" => ["b", "c", "d"]}) - db_item.custom_fields["a"] = ["c", "d"] + db_item.custom_fields.update('a' => ['c', 'd']) db_item.save - db_item.custom_fields.should == {"a" => ["c", "d"]} + expect(db_item.custom_fields).to eq({"a" => ["c", "d"]}) + + # It can be updated to the exact same value + db_item.custom_fields.update('a' => ['c']) + db_item.save + expect(db_item.custom_fields).to eq({"a" => "c"}) + db_item.custom_fields.update('a' => ['c']) + db_item.save + expect(db_item.custom_fields).to eq({"a" => "c"}) + + db_item.custom_fields.delete('a') + expect(db_item.custom_fields).to eq({}) end @@ -122,10 +133,10 @@ describe HasCustomFields do test_item = CustomFieldsTestItem.new test_item.custom_fields = {"a" => ["b", 10, "d"]} test_item.save - test_item.custom_fields.should == {"a" => ["b", "10", "d"]} + expect(test_item.custom_fields).to eq({"a" => ["b", "10", "d"]}) db_item = CustomFieldsTestItem.find(test_item.id) - db_item.custom_fields.should == {"a" => ["b", "10", "d"]} + expect(db_item.custom_fields).to eq({"a" => ["b", "10", "d"]}) end @@ -138,13 +149,13 @@ describe HasCustomFields do test_item.save test_item.reload - test_item.custom_fields.should == {"bool" => true, "int" => 1} + expect(test_item.custom_fields).to eq({"bool" => true, "int" => 1}) end it "simple modifications don't interfere" do test_item = CustomFieldsTestItem.new - test_item.custom_fields["a"].should == nil + expect(test_item.custom_fields["a"]).to eq(nil) test_item.custom_fields["bob"] = "marley" test_item.custom_fields["jack"] = "black" @@ -152,7 +163,7 @@ describe HasCustomFields do test_item2 = CustomFieldsTestItem.new - test_item2.custom_fields["x"].should == nil + expect(test_item2.custom_fields["x"]).to eq(nil) test_item2.custom_fields["sixto"] = "rodriguez" test_item2.custom_fields["de"] = "la playa" @@ -161,8 +172,8 @@ describe HasCustomFields do test_item = CustomFieldsTestItem.find(test_item.id) test_item2 = CustomFieldsTestItem.find(test_item2.id) - test_item.custom_fields.should == {"jack" => "black", "bob" => "marley"} - test_item2.custom_fields.should == {"sixto" => "rodriguez", "de" => "la playa"} + expect(test_item.custom_fields).to eq({"jack" => "black", "bob" => "marley"}) + expect(test_item2.custom_fields).to eq({"sixto" => "rodriguez", "de" => "la playa"}) end it "supports bulk retrieval with a list of ids" do @@ -175,10 +186,10 @@ describe HasCustomFields do item2.save fields = CustomFieldsTestItem.custom_fields_for_ids([item1.id, item2.id], ['a', 'e']) - fields.should be_present - fields[item1.id]['a'].should =~ ['b', 'c', 'd'] - fields[item1.id]['not_whitelisted'].should be_blank - fields[item2.id]['e'].should == 'hallo' + expect(fields).to be_present + expect(fields[item1.id]['a']).to match_array(['b', 'c', 'd']) + expect(fields[item1.id]['not_whitelisted']).to be_blank + expect(fields[item2.id]['e']).to eq('hallo') end end end diff --git a/spec/components/concern/positionable_spec.rb b/spec/components/concern/positionable_spec.rb index da5ae19a6c..4e4b6d490a 100644 --- a/spec/components/concern/positionable_spec.rb +++ b/spec/components/concern/positionable_spec.rb @@ -28,24 +28,24 @@ describe Positionable do Topic.exec_sql("insert into test_items(id,position) values(#{i}, #{i})") end - positions.should == [0,1,2,3,4] + expect(positions).to eq([0,1,2,3,4]) TestItem.find(3).move_to(0) - positions.should == [3,0,1,2,4] - TestItem.pluck(:position).sort.should == [0,1,2,3,4] + expect(positions).to eq([3,0,1,2,4]) + expect(TestItem.pluck(:position).sort).to eq([0,1,2,3,4]) TestItem.find(3).move_to(1) - positions.should == [0,3,1,2,4] + expect(positions).to eq([0,3,1,2,4]) # this is somewhat odd, but when there is no such position, not much we can do TestItem.find(1).move_to(5) - positions.should == [0,3,2,4,1] + expect(positions).to eq([0,3,2,4,1]) - TestItem.pluck(:position).sort.should == [0,1,2,3,4] + expect(TestItem.pluck(:position).sort).to eq([0,1,2,3,4]) item = TestItem.new item.id = 7 item.save - item.position.should == 5 + expect(item.position).to eq(5) end end end diff --git a/spec/components/content_buffer_spec.rb b/spec/components/content_buffer_spec.rb index 43c265b2ad..8e6299896b 100644 --- a/spec/components/content_buffer_spec.rb +++ b/spec/components/content_buffer_spec.rb @@ -6,24 +6,24 @@ describe ContentBuffer do it "handles deletion across lines properly" do c = ContentBuffer.new("a\nbc\nc") c.apply_transform!(start: {row: 0, col: 0}, finish: {col: 1, row: 1}, operation: :delete) - c.to_s.should == "c\nc" + expect(c.to_s).to eq("c\nc") end it "handles deletion inside lines properly" do c = ContentBuffer.new("hello world") c.apply_transform!(start: {row: 0, col: 1}, finish: {col: 4, row: 0}, operation: :delete) - c.to_s.should == "ho world" + expect(c.to_s).to eq("ho world") end it "handles inserts inside lines properly" do c = ContentBuffer.new("hello!") c.apply_transform!(start: {row: 0, col: 5}, operation: :insert, text: " world") - c.to_s.should == "hello world!" + expect(c.to_s).to eq("hello world!") end it "handles multiline inserts" do c = ContentBuffer.new("hello!") c.apply_transform!(start: {row: 0, col: 5}, operation: :insert, text: "\nworld") - c.to_s.should == "hello\nworld!" + expect(c.to_s).to eq("hello\nworld!") end end diff --git a/spec/components/cooked_post_processor_spec.rb b/spec/components/cooked_post_processor_spec.rb index ae90bf3569..6b5ff79f41 100644 --- a/spec/components/cooked_post_processor_spec.rb +++ b/spec/components/cooked_post_processor_spec.rb @@ -49,10 +49,10 @@ describe CookedPostProcessor do it "works" do # adds the width from the image sizes provided when no dimension is provided - cpp.html.should =~ /src="http:\/\/foo.bar\/image.png" width="111" height="222"/ + expect(cpp.html).to match(/src="http:\/\/foo.bar\/image.png" width="111" height="222"/) # adds the width from the image sizes provided - cpp.html.should =~ /src="http:\/\/domain.com\/picture.jpg" width="50" height="42"/ - cpp.should be_dirty + expect(cpp.html).to match(/src="http:\/\/domain.com\/picture.jpg" width="50" height="42"/) + expect(cpp).to be_dirty end end @@ -65,8 +65,8 @@ describe CookedPostProcessor do it "adds the width and height to images that don't have them" do FastImage.expects(:size).returns([123, 456]) cpp.post_process_images - cpp.html.should =~ /width="123" height="456"/ - cpp.should be_dirty + expect(cpp.html).to match(/width="123" height="456"/) + expect(cpp).to be_dirty end end @@ -90,10 +90,10 @@ describe CookedPostProcessor do it "generates overlay information" do cpp.post_process_images - cpp.html.should match_html '
- + -
{{{option}}}
+
{{{po.option}}}
{{#if controller.showResults}} -
{{i18n poll.voteCount count=votes}}
+
{{i18n 'poll.voteCount' count=po.votes}}
{{/if}}
" + expect(DiscourseDiff.new("", "").side_by_side_markdown).to eq("
") end it "properly escape html tags" do before = "" after = "\"" - DiscourseDiff.new(before, after).side_by_side_markdown.should == "
<img src="//domain.com/image.png>"
" + expect(DiscourseDiff.new(before, after).side_by_side_markdown).to eq("
<img src="//domain.com/image.png>"
") end it "returns the diffed content on both columns when there is no difference" do before = after = "this is a paragraph" - DiscourseDiff.new(before, after).side_by_side_markdown.should == "
this is a paragraphthis is a paragraph
" + expect(DiscourseDiff.new(before, after).side_by_side_markdown).to eq("
this is a paragraphthis is a paragraph
") end it "adds tags around added text on the second column" do before = "this is a paragraph" after = "this is a great paragraph" - DiscourseDiff.new(before, after).side_by_side_markdown.should == "
this is a paragraphthis is a great paragraph
" + expect(DiscourseDiff.new(before, after).side_by_side_markdown).to eq("
this is a paragraphthis is a great paragraph
") end it "adds tags around removed text on the first column" do before = "this is a great paragraph" after = "this is a paragraph" - DiscourseDiff.new(before, after).side_by_side_markdown.should == "
this is a great paragraphthis is a paragraph
" + expect(DiscourseDiff.new(before, after).side_by_side_markdown).to eq("
this is a great paragraphthis is a paragraph
") end it "adds .diff-ins class when a paragraph is added" do before = "this is the first paragraph" after = "this is the first paragraph\nthis is the second paragraph" - DiscourseDiff.new(before, after).side_by_side_markdown.should == "
this is the first paragraphthis is the first paragraph\nthis is the second paragraph
" + expect(DiscourseDiff.new(before, after).side_by_side_markdown).to eq("
this is the first paragraphthis is the first paragraph\nthis is the second paragraph
") end it "adds .diff-del class when a paragraph is removed" do before = "this is the first paragraph\nthis is the second paragraph" after = "this is the second paragraph" - DiscourseDiff.new(before, after).side_by_side_markdown.should == "
this is the first paragraph\n
this is the second paragraphthis is the second paragraph
" + expect(DiscourseDiff.new(before, after).side_by_side_markdown).to eq("
this is the first paragraph\n
this is the second paragraphthis is the second paragraph
") end end diff --git a/spec/components/discourse_event_spec.rb b/spec/components/discourse_event_spec.rb index 0491f0577a..281cb7e09d 100644 --- a/spec/components/discourse_event_spec.rb +++ b/spec/components/discourse_event_spec.rb @@ -6,12 +6,12 @@ describe DiscourseEvent do describe "#events" do it "defaults to {}" do DiscourseEvent.instance_variable_set(:@events, nil) - DiscourseEvent.events.should == {} + expect(DiscourseEvent.events).to eq({}) end describe "key value" do it "defaults to an empty set" do - DiscourseEvent.events["event42"].should == Set.new + expect(DiscourseEvent.events["event42"]).to eq(Set.new) end end end @@ -20,7 +20,7 @@ describe DiscourseEvent do it "clears out events" do DiscourseEvent.events["event42"] << "test event" DiscourseEvent.clear - DiscourseEvent.events.should be_empty + expect(DiscourseEvent.events).to be_empty end end @@ -55,7 +55,7 @@ describe DiscourseEvent do it "changes the name" do DiscourseEvent.trigger(:acid_face, harvey) - harvey.name.should == 'Two Face' + expect(harvey.name).to eq('Two Face') end end @@ -71,8 +71,8 @@ describe DiscourseEvent do end it 'triggers both events' do - harvey.job.should == 'Supervillian' - harvey.name.should == 'Two Face' + expect(harvey.job).to eq('Supervillian') + expect(harvey.name).to eq('Two Face') end end diff --git a/spec/components/discourse_hub_spec.rb b/spec/components/discourse_hub_spec.rb index ea579d0e83..95d911a66b 100644 --- a/spec/components/discourse_hub_spec.rb +++ b/spec/components/discourse_hub_spec.rb @@ -6,7 +6,7 @@ describe DiscourseHub do it 'should return just return the json that the hub returns' do hub_response = {'success' => 'OK', 'latest_version' => '0.8.1', 'critical_updates' => false} RestClient.stubs(:get).returns( hub_response.to_json ) - DiscourseHub.discourse_version_check.should == hub_response + expect(DiscourseHub.discourse_version_check).to eq(hub_response) end end end diff --git a/spec/components/discourse_plugin_registry_spec.rb b/spec/components/discourse_plugin_registry_spec.rb index f3942e9122..1171292676 100644 --- a/spec/components/discourse_plugin_registry_spec.rb +++ b/spec/components/discourse_plugin_registry_spec.rb @@ -11,35 +11,35 @@ describe DiscoursePluginRegistry do context '#stylesheets' do it 'defaults to an empty Set' do registry.stylesheets = nil - registry.stylesheets.should == Set.new + expect(registry.stylesheets).to eq(Set.new) end end context '#mobile_stylesheets' do it 'defaults to an empty Set' do registry.mobile_stylesheets = nil - registry.mobile_stylesheets.should == Set.new + expect(registry.mobile_stylesheets).to eq(Set.new) end end context '#javascripts' do it 'defaults to an empty Set' do registry.javascripts = nil - registry.javascripts.should == Set.new + expect(registry.javascripts).to eq(Set.new) end end context '#server_side_javascripts' do it 'defaults to an empty Set' do registry.server_side_javascripts = nil - registry.server_side_javascripts.should == Set.new + expect(registry.server_side_javascripts).to eq(Set.new) end end context '#admin_javascripts' do it 'defaults to an empty Set' do registry.admin_javascripts = nil - registry.admin_javascripts.should == Set.new + expect(registry.admin_javascripts).to eq(Set.new) end end @@ -49,15 +49,15 @@ describe DiscoursePluginRegistry do end it 'is not leaking' do - DiscoursePluginRegistry.new.stylesheets.should be_blank + expect(DiscoursePluginRegistry.new.stylesheets).to be_blank end it 'is returned by DiscoursePluginRegistry.stylesheets' do - registry_instance.stylesheets.include?('hello.css').should == true + expect(registry_instance.stylesheets.include?('hello.css')).to eq(true) end it "won't add the same file twice" do - lambda { registry_instance.register_css('hello.css') }.should_not change(registry.stylesheets, :size) + expect { registry_instance.register_css('hello.css') }.not_to change(registry.stylesheets, :size) end end @@ -67,11 +67,11 @@ describe DiscoursePluginRegistry do end it 'is returned by DiscoursePluginRegistry.javascripts' do - registry_instance.javascripts.include?('hello.js').should == true + expect(registry_instance.javascripts.include?('hello.js')).to eq(true) end it "won't add the same file twice" do - lambda { registry_instance.register_js('hello.js') }.should_not change(registry.javascripts, :size) + expect { registry_instance.register_js('hello.js') }.not_to change(registry.javascripts, :size) end end @@ -82,4 +82,65 @@ describe DiscoursePluginRegistry do end end + context '#register_asset' do + let(:registry) { DiscoursePluginRegistry } + + after do + registry.reset! + end + + it "does register general css properly" do + registry.register_asset("test.css") + registry.register_asset("test2.css") + + expect(registry.mobile_stylesheets.count).to eq(0) + expect(registry.stylesheets.count).to eq(2) + end + + it "registers desktop css properly" do + registry.register_asset("test.css", :desktop) + + expect(registry.mobile_stylesheets.count).to eq(0) + expect(registry.desktop_stylesheets.count).to eq(1) + expect(registry.stylesheets.count).to eq(0) + end + + it "registers mobile css properly" do + registry.register_asset("test.css", :mobile) + + expect(registry.mobile_stylesheets.count).to eq(1) + expect(registry.stylesheets.count).to eq(0) + end + + it "registers desktop css properly" do + registry.register_asset("test.css", :desktop) + + expect(registry.desktop_stylesheets.count).to eq(1) + expect(registry.stylesheets.count).to eq(0) + end + + it "registers sass variable properly" do + registry.register_asset("test.css", :variables) + + expect(registry.sass_variables.count).to eq(1) + expect(registry.stylesheets.count).to eq(0) + end + + it "registers admin javascript properly" do + registry.register_asset("my_admin.js", :admin) + + expect(registry.admin_javascripts.count).to eq(1) + expect(registry.javascripts.count).to eq(0) + expect(registry.server_side_javascripts.count).to eq(0) + end + + it "registers server side javascript properly" do + registry.register_asset("my_admin.js", :server_side) + + expect(registry.server_side_javascripts.count).to eq(1) + expect(registry.javascripts.count).to eq(1) + expect(registry.admin_javascripts.count).to eq(0) + end + end + end diff --git a/spec/components/discourse_plugin_spec.rb b/spec/components/discourse_plugin_spec.rb index 1fcbf20572..8910b0f2ad 100644 --- a/spec/components/discourse_plugin_spec.rb +++ b/spec/components/discourse_plugin_spec.rb @@ -14,9 +14,13 @@ describe DiscoursePlugin do let(:registry) { mock } let(:plugin) { TestPlugin.new(registry) } + after do + DiscourseEvent.clear + end + describe ".mixins" do it "finds its mixins" do - TestPlugin.mixins.should == [TestPlugin::TestMixin] + expect(TestPlugin.mixins).to eq([TestPlugin::TestMixin]) end end diff --git a/spec/components/discourse_sass_compiler_spec.rb b/spec/components/discourse_sass_compiler_spec.rb index 8f92eaf23d..1bd144f11e 100644 --- a/spec/components/discourse_sass_compiler_spec.rb +++ b/spec/components/discourse_sass_compiler_spec.rb @@ -9,8 +9,8 @@ describe DiscourseSassCompiler do it "compiles scss" do DiscoursePluginRegistry.stubs(:stylesheets).returns(["#{Rails.root}/spec/fixtures/scss/my_plugin.scss"]) css = described_class.compile(test_scss, "test") - css.should include("color") - css.should include('my-plugin-thing') + expect(css).to include("color") + expect(css).to include('my-plugin-thing') end it "raises error for invalid scss" do @@ -23,7 +23,7 @@ describe DiscourseSassCompiler do ColorScheme.expects(:enabled).never DiscoursePluginRegistry.stubs(:stylesheets).returns(["#{Rails.root}/spec/fixtures/scss/my_plugin.scss"]) css = described_class.compile(test_scss, "test", safe: true) - css.should_not include('my-plugin-thing') + expect(css).not_to include('my-plugin-thing') end end diff --git a/spec/components/discourse_spec.rb b/spec/components/discourse_spec.rb index a34fae3220..ef8f86884b 100644 --- a/spec/components/discourse_spec.rb +++ b/spec/components/discourse_spec.rb @@ -10,7 +10,7 @@ describe Discourse do context 'current_hostname' do it 'returns the hostname from the current db connection' do - Discourse.current_hostname.should == 'foo.com' + expect(Discourse.current_hostname).to eq('foo.com') end end @@ -22,7 +22,7 @@ describe Discourse do end it 'has a non https base url' do - Discourse.base_url.should == "http://foo.com" + expect(Discourse.base_url).to eq("http://foo.com") end end @@ -32,7 +32,7 @@ describe Discourse do end it 'has a non-ssl base url' do - Discourse.base_url.should == "https://foo.com" + expect(Discourse.base_url).to eq("https://foo.com") end end @@ -42,7 +42,7 @@ describe Discourse do end it "returns the non standart port in the base url" do - Discourse.base_url.should == "http://foo.com:3000" + expect(Discourse.base_url).to eq("http://foo.com:3000") end end end @@ -54,17 +54,17 @@ describe Discourse do it 'returns the user specified by the site setting site_contact_username' do SiteSetting.stubs(:site_contact_username).returns(another_admin.username) - Discourse.site_contact_user.should == another_admin + expect(Discourse.site_contact_user).to eq(another_admin) end it 'returns the user specified by the site setting site_contact_username regardless of its case' do SiteSetting.stubs(:site_contact_username).returns(another_admin.username.upcase) - Discourse.site_contact_user.should == another_admin + expect(Discourse.site_contact_user).to eq(another_admin) end it 'returns the first admin user otherwise' do SiteSetting.stubs(:site_contact_username).returns(nil) - Discourse.site_contact_user.should == admin + expect(Discourse.site_contact_user).to eq(admin) end end @@ -72,7 +72,7 @@ describe Discourse do context "#store" do it "returns LocalStore by default" do - Discourse.store.should be_a(FileStore::LocalStore) + expect(Discourse.store).to be_a(FileStore::LocalStore) end it "returns S3Store when S3 is enabled" do @@ -80,7 +80,7 @@ describe Discourse do SiteSetting.stubs(:s3_upload_bucket).returns("s3_bucket") SiteSetting.stubs(:s3_access_key_id).returns("s3_access_key_id") SiteSetting.stubs(:s3_secret_access_key).returns("s3_secret_access_key") - Discourse.store.should be_a(FileStore::S3Store) + expect(Discourse.store).to be_a(FileStore::S3Store) end end @@ -109,12 +109,12 @@ describe Discourse do it "returns true when the key is present in redis" do $redis.expects(:get).with(Discourse.readonly_mode_key).returns("1") - Discourse.readonly_mode?.should == true + expect(Discourse.readonly_mode?).to eq(true) end it "returns false when the key is not present in redis" do $redis.expects(:get).with(Discourse.readonly_mode_key).returns(nil) - Discourse.readonly_mode?.should == false + expect(Discourse.readonly_mode?).to eq(false) end end @@ -140,16 +140,16 @@ describe Discourse do exception = StandardError.new Discourse.handle_exception(exception, nil, nil) - logger.exception.should == exception - logger.context.keys.should == [:current_db, :current_hostname] + expect(logger.exception).to eq(exception) + expect(logger.context.keys).to eq([:current_db, :current_hostname]) end it "correctly passes extra context" do exception = StandardError.new Discourse.handle_exception(exception, {message: "Doing a test", post_id: 31}, nil) - logger.exception.should == exception - logger.context.keys.sort.should == [:current_db, :current_hostname, :message, :post_id].sort + expect(logger.exception).to eq(exception) + expect(logger.context.keys.sort).to eq([:current_db, :current_hostname, :message, :post_id].sort) end end diff --git a/spec/components/discourse_stylesheets_spec.rb b/spec/components/discourse_stylesheets_spec.rb index 2889716e8c..79c3aa477d 100644 --- a/spec/components/discourse_stylesheets_spec.rb +++ b/spec/components/discourse_stylesheets_spec.rb @@ -7,14 +7,14 @@ describe DiscourseStylesheets do it "can compile desktop bundle" do DiscoursePluginRegistry.stubs(:stylesheets).returns(["#{Rails.root}/spec/fixtures/scss/my_plugin.scss"]) builder = described_class.new(:desktop) - builder.compile.should include('my-plugin-thing') + expect(builder.compile).to include('my-plugin-thing') FileUtils.rm builder.stylesheet_fullpath end it "can compile mobile bundle" do DiscoursePluginRegistry.stubs(:mobile_stylesheets).returns(["#{Rails.root}/spec/fixtures/scss/my_plugin.scss"]) builder = described_class.new(:mobile) - builder.compile.should include('my-plugin-thing') + expect(builder.compile).to include('my-plugin-thing') FileUtils.rm builder.stylesheet_fullpath end @@ -24,7 +24,7 @@ describe DiscourseStylesheets do "#{Rails.root}/spec/fixtures/scss/broken.scss" ]) builder = described_class.new(:desktop) - builder.compile.should_not include('my-plugin-thing') + expect(builder.compile).not_to include('my-plugin-thing') FileUtils.rm builder.stylesheet_fullpath end end diff --git a/spec/components/discourse_updates_spec.rb b/spec/components/discourse_updates_spec.rb index 9f4f2b8b7f..185a163649 100644 --- a/spec/components/discourse_updates_spec.rb +++ b/spec/components/discourse_updates_spec.rb @@ -26,14 +26,14 @@ describe DiscourseUpdates do before { stub_data(Discourse::VERSION::STRING, 0, false, 12.hours.ago) } it 'returns all the version fields' do - subject['latest_version'].should == Discourse::VERSION::STRING - subject['missing_versions_count'].should == 0 - subject['critical_updates'].should == false - subject['installed_version'].should == Discourse::VERSION::STRING + expect(subject['latest_version']).to eq(Discourse::VERSION::STRING) + expect(subject['missing_versions_count']).to eq(0) + expect(subject['critical_updates']).to eq(false) + expect(subject['installed_version']).to eq(Discourse::VERSION::STRING) end it 'returns the timestamp of the last version check' do - subject['updated_at'].should be_within_one_second_of(12.hours.ago) + expect(subject['updated_at']).to be_within_one_second_of(12.hours.ago) end end @@ -41,14 +41,14 @@ describe DiscourseUpdates do before { stub_data('0.9.0', 2, false, 12.hours.ago) } it 'returns all the version fields' do - subject['latest_version'].should == '0.9.0' - subject['missing_versions_count'].should == 2 - subject['critical_updates'].should == false - subject['installed_version'].should == Discourse::VERSION::STRING + expect(subject['latest_version']).to eq('0.9.0') + expect(subject['missing_versions_count']).to eq(2) + expect(subject['critical_updates']).to eq(false) + expect(subject['installed_version']).to eq(Discourse::VERSION::STRING) end it 'returns the timestamp of the last version check' do - subject['updated_at'].should be_within_one_second_of(12.hours.ago) + expect(subject['updated_at']).to be_within_one_second_of(12.hours.ago) end end end @@ -57,18 +57,18 @@ describe DiscourseUpdates do before { stub_data(nil, nil, false, nil) } it 'returns the installed version' do - subject['installed_version'].should == Discourse::VERSION::STRING + expect(subject['installed_version']).to eq(Discourse::VERSION::STRING) end it 'indicates that version check has not been performed' do - subject.should have_key('updated_at') - subject['updated_at'].should == nil + expect(subject).to have_key('updated_at') + expect(subject['updated_at']).to eq(nil) end it 'does not return latest version info' do - subject.should_not have_key('latest_version') - subject.should_not have_key('missing_versions_count') - subject.should_not have_key('critical_updates') + expect(subject).not_to have_key('latest_version') + expect(subject).not_to have_key('missing_versions_count') + expect(subject).not_to have_key('critical_updates') end it 'queues a version check' do @@ -87,11 +87,11 @@ describe DiscourseUpdates do end it 'reports 0 missing versions' do - subject['missing_versions_count'].should == 0 + expect(subject['missing_versions_count']).to eq(0) end it 'reports that a version check will be run soon' do - subject['version_check_pending'].should == true + expect(subject['version_check_pending']).to eq(true) end end @@ -119,11 +119,11 @@ describe DiscourseUpdates do end it 'reports 0 missing versions' do - subject['missing_versions_count'].should == 0 + expect(subject['missing_versions_count']).to eq(0) end it 'reports that a version check will be run soon' do - subject['version_check_pending'].should == true + expect(subject['version_check_pending']).to eq(true) end end diff --git a/spec/components/distributed_cache_spec.rb b/spec/components/distributed_cache_spec.rb index eeedfac497..68a0c259a4 100644 --- a/spec/components/distributed_cache_spec.rb +++ b/spec/components/distributed_cache_spec.rb @@ -20,18 +20,18 @@ describe DistributedCache do end Thread.pass - cache1["hi"].should == nil + expect(cache1["hi"]).to eq(nil) end it 'allows coerces symbol keys to strings' do cache1[:key] = "test" - cache1["key"].should == "test" + expect(cache1["key"]).to eq("test") wait_for do cache2[:key] == "test" end - cache2["key"].should == "test" + expect(cache2["key"]).to eq("test") end it 'sets other caches' do @@ -49,7 +49,7 @@ describe DistributedCache do end cache1.delete("foo") - cache1["foo"].should == nil + expect(cache1["foo"]).to eq(nil) wait_for do cache2["foo"] == nil @@ -64,7 +64,7 @@ describe DistributedCache do end cache1.clear - cache1["foo"].should == nil + expect(cache1["foo"]).to eq(nil) wait_for do cache2["boom"] == nil end diff --git a/spec/components/distributed_memoizer_spec.rb b/spec/components/distributed_memoizer_spec.rb index 16c364feb5..097797f803 100644 --- a/spec/components/distributed_memoizer_spec.rb +++ b/spec/components/distributed_memoizer_spec.rb @@ -17,9 +17,9 @@ describe DistributedMemoizer do end it "returns the value of a block" do - memoize do + expect(memoize do "abc" - end.should == "abc" + end).to eq("abc") end it "return the old value once memoized" do @@ -28,9 +28,9 @@ describe DistributedMemoizer do "abc" end - memoize do + expect(memoize do "world" - end.should == "abc" + end).to eq("abc") end it "memoizes correctly when used concurrently" do @@ -47,8 +47,8 @@ describe DistributedMemoizer do end threads.each(&:join) - results.uniq.length.should == 1 - results.count.should == 5 + expect(results.uniq.length).to eq(1) + expect(results.count).to eq(5) end diff --git a/spec/components/distributed_mutex_spec.rb b/spec/components/distributed_mutex_spec.rb index 37cbb4e1f9..d8a42ffbce 100644 --- a/spec/components/distributed_mutex_spec.rb +++ b/spec/components/distributed_mutex_spec.rb @@ -18,7 +18,7 @@ describe DistributedMutex do end end.map(&:join) - x.should == 10 + expect(x).to eq(10) end it "handles auto cleanup correctly" do @@ -33,17 +33,17 @@ describe DistributedMutex do end # no longer than a second - Time.now.to_i.should <= start + 1 + expect(Time.now.to_i).to be <= start + 1 end it "maintains mutex semantics" do m = DistributedMutex.new("test_mutex_key") - lambda { + expect { m.synchronize do m.synchronize{} end - }.should raise_error(ThreadError) + }.to raise_error(ThreadError) end end diff --git a/spec/components/email/email_spec.rb b/spec/components/email/email_spec.rb index 3773e7df51..4b278aa505 100644 --- a/spec/components/email/email_spec.rb +++ b/spec/components/email/email_spec.rb @@ -6,19 +6,19 @@ describe Email do describe "is_valid?" do it 'treats a good email as valid' do - Email.is_valid?('sam@sam.com').should == true + expect(Email.is_valid?('sam@sam.com')).to eq(true) end it 'treats a bad email as invalid' do - Email.is_valid?('sam@sam').should == false + expect(Email.is_valid?('sam@sam')).to eq(false) end it 'allows museum tld' do - Email.is_valid?('sam@nic.museum').should == true + expect(Email.is_valid?('sam@nic.museum')).to eq(true) end it 'does not think a word is an email' do - Email.is_valid?('sam').should == false + expect(Email.is_valid?('sam')).to eq(false) end end @@ -26,14 +26,14 @@ describe Email do describe "downcase" do it 'downcases local and host part' do - Email.downcase('SAM@GMAIL.COM').should == 'sam@gmail.com' - Email.downcase('sam@GMAIL.COM').should == 'sam@gmail.com' + expect(Email.downcase('SAM@GMAIL.COM')).to eq('sam@gmail.com') + expect(Email.downcase('sam@GMAIL.COM')).to eq('sam@gmail.com') end it 'leaves invalid emails untouched' do - Email.downcase('SAM@GMAILCOM').should == 'SAM@GMAILCOM' - Email.downcase('samGMAIL.COM').should == 'samGMAIL.COM' - Email.downcase('sam@GM@AIL.COM').should == 'sam@GM@AIL.COM' + expect(Email.downcase('SAM@GMAILCOM')).to eq('SAM@GMAILCOM') + expect(Email.downcase('samGMAIL.COM')).to eq('samGMAIL.COM') + expect(Email.downcase('sam@GM@AIL.COM')).to eq('sam@GM@AIL.COM') end end diff --git a/spec/components/email/message_builder_spec.rb b/spec/components/email/message_builder_spec.rb index 8586416f41..eb03bf53c0 100644 --- a/spec/components/email/message_builder_spec.rb +++ b/spec/components/email/message_builder_spec.rb @@ -58,7 +58,7 @@ describe Email::MessageBuilder do end it "cleans up the site title" do - SiteSetting.stubs(:title).returns(">>>Obnoxious Title: Deal With It<<<") + SiteSetting.stubs(:title).returns(">>>Obnoxious Title: Deal, With It<<<") expect(reply_by_email_builder.header_args['Reply-To']).to eq("Obnoxious Title Deal With It ") end end @@ -253,12 +253,12 @@ describe Email::MessageBuilder do end it "cleans up aliases in the from_alias arg" do - builder = Email::MessageBuilder.new(to_address, from_alias: "Finn: the Dog <3", from: finn_email) - builder.build_args[:from].should == "Finn the Dog 3 <#{finn_email}>" + builder = Email::MessageBuilder.new(to_address, from_alias: "Finn: the Dog, <3", from: finn_email) + expect(builder.build_args[:from]).to eq("Finn the Dog 3 <#{finn_email}>") end it "cleans up the email_site_title" do - SiteSetting.stubs(:email_site_title).returns("::>>>Best Forum EU: Award Winning<<<") + SiteSetting.stubs(:email_site_title).returns("::>>>Best Forum, EU: Award Winning<<<") expect(build_args[:from]).to eq("Best Forum EU Award Winning <#{SiteSetting.notification_email}>") end diff --git a/spec/components/email/receiver_spec.rb b/spec/components/email/receiver_spec.rb index 55a7df2027..dedb2a8635 100644 --- a/spec/components/email/receiver_spec.rb +++ b/spec/components/email/receiver_spec.rb @@ -33,45 +33,45 @@ describe Email::Receiver do end it "can parse the html section" do - test_parse_body(fixture_file("emails/html_only.eml")).should == "The EC2 instance - I've seen that there tends to be odd and " + - "unrecommended settings on the Bitnami installs that I've checked out." + expect(test_parse_body(fixture_file("emails/html_only.eml"))).to eq("The EC2 instance - I've seen that there tends to be odd and " + + "unrecommended settings on the Bitnami installs that I've checked out.") end it "supports a Dutch reply" do - test_parse_body(fixture_file("emails/dutch.eml")).should == "Dit is een antwoord in het Nederlands." + expect(test_parse_body(fixture_file("emails/dutch.eml"))).to eq("Dit is een antwoord in het Nederlands.") end it "supports a Hebrew reply" do I18n.expects(:t).with('user_notifications.previous_discussion').returns('כלטוב') # The force_encoding call is only needed for the test - it is passed on fine to the cooked post - test_parse_body(fixture_file("emails/hebrew.eml")).should == "שלום" + expect(test_parse_body(fixture_file("emails/hebrew.eml"))).to eq("שלום") end it "supports a BIG5-encoded reply" do I18n.expects(:t).with('user_notifications.previous_discussion').returns('媽!我上電視了!') # The force_encoding call is only needed for the test - it is passed on fine to the cooked post - test_parse_body(fixture_file("emails/big5.eml")).should == "媽!我上電視了!" + expect(test_parse_body(fixture_file("emails/big5.eml"))).to eq("媽!我上電視了!") end it "removes 'via' lines if they match the site title" do SiteSetting.title = "Discourse" - test_parse_body(fixture_file("emails/via_line.eml")).should == "Hello this email has content!" + expect(test_parse_body(fixture_file("emails/via_line.eml"))).to eq("Hello this email has content!") end it "removes an 'on date wrote' quoting line" do - test_parse_body(fixture_file("emails/on_wrote.eml")).should == "Sure, all you need to do is frobnicate the foobar and you'll be all set!" + expect(test_parse_body(fixture_file("emails/on_wrote.eml"))).to eq("Sure, all you need to do is frobnicate the foobar and you'll be all set!") end it "removes the 'Previous Discussion' marker" do - test_parse_body(fixture_file("emails/previous.eml")).should == "This will not include the previous discussion that is present in this email." + expect(test_parse_body(fixture_file("emails/previous.eml"))).to eq("This will not include the previous discussion that is present in this email.") end it "handles multiple paragraphs" do - test_parse_body(fixture_file("emails/paragraphs.eml")). - should == ( + expect(test_parse_body(fixture_file("emails/paragraphs.eml"))). + to eq( "Is there any reason the *old* candy can't be be kept in silos while the new candy is imported into *new* silos? @@ -83,8 +83,8 @@ Thanks for listening." end it "handles multiple paragraphs when parsing html" do - test_parse_body(fixture_file("emails/html_paragraphs.eml")). - should == ( + expect(test_parse_body(fixture_file("emails/html_paragraphs.eml"))). + to eq( "Awesome! Pleasure to have you here! @@ -93,13 +93,139 @@ Pleasure to have you here! ) end + it "handles newlines" do + expect(test_parse_body(fixture_file("emails/newlines.eml"))). + to eq( +"This is my reply. +It is my best reply. +It will also be my *only* reply." + ) + end + + it "handles inline reply" do + expect(test_parse_body(fixture_file("emails/inline_reply.eml"))). + to eq( +"On Wed, Oct 8, 2014 at 11:12 AM, techAPJ wrote: + +> techAPJ +> November 28 +> +> Test reply. +> +> First paragraph. +> +> Second paragraph. +> +> To respond, reply to this email or visit +> https://meta.discourse.org/t/testing-default-email-replies/22638/3 in +> your browser. +> ------------------------------ +> Previous Replies codinghorror +> +> November 28 +> +> We're testing the latest GitHub email processing library which we are +> integrating now. +> +> https://github.com/github/email_reply_parser +> +> Go ahead and reply to this topic and I'll reply from various email clients +> for testing. +> ------------------------------ +> +> To respond, reply to this email or visit +> https://meta.discourse.org/t/testing-default-email-replies/22638/3 in +> your browser. +> +> To unsubscribe from these emails, visit your user preferences +> . +> + +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over +the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown +fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over +the lazy dog. The quick brown fox jumps over the lazy dog." + ) + end + + it "should not include previous replies" do + expect(test_parse_body(fixture_file("emails/previous_replies.eml"))).not_to match /Previous Replies/ + end + + it "strips iPhone signature" do + expect(test_parse_body(fixture_file("emails/iphone_signature.eml"))).not_to match /Sent from my iPhone/ + end + + it "properly renders email reply from gmail web client" do + expect(test_parse_body(fixture_file("emails/gmail_web.eml"))). + to eq( +"### This is a reply from standard GMail in Google Chrome. + +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over +the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown +fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over +the lazy dog. The quick brown fox jumps over the lazy dog. + +Here's some **bold** text in Markdown. + +Here's a link http://example.com" + ) + end + + it "properly renders email reply from iOS default mail client" do + expect(test_parse_body(fixture_file("emails/ios_default.eml"))). + to eq( +"### this is a reply from iOS default mail + +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. + +Here's some **bold** markdown text. + +Here's a link http://example.com" + ) + end + + it "properly renders email reply from Android 5 gmail client" do + expect(test_parse_body(fixture_file("emails/android_gmail.eml"))). + to eq( +"### this is a reply from Android 5 gmail + +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over +the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown +fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. +The quick brown fox jumps over the lazy dog. + +This is **bold** in Markdown. + +This is a link to http://example.com" + ) + end + + it "properly renders email reply from Windows 8.1 Metro default mail client" do + expect(test_parse_body(fixture_file("emails/windows_8_metro.eml"))). + to eq( +"### reply from default mail client in Windows 8.1 Metro + + +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. + + +This is a **bold** word in Markdown + + +This is a link http://example.com" + ) + end + it "properly renders email reply from MS Outlook client" do - test_parse_body(fixture_file("emails/outlook.eml")).should == "Microsoft Outlook 2010" + expect(test_parse_body(fixture_file("emails/outlook.eml"))).to eq("Microsoft Outlook 2010") end it "converts back to UTF-8 at the end" do result = test_parse_body(fixture_file("emails/big5.eml")) - result.encoding.should == Encoding::UTF_8 + expect(result.encoding).to eq(Encoding::UTF_8) # should not throw TextCleaner.normalize_whitespaces( @@ -143,11 +269,11 @@ Pleasure to have you here! receiver.process - topic.posts.count.should == (start_count + 1) + expect(topic.posts.count).to eq(start_count + 1) created_post = topic.posts.last - created_post.via_email.should == true - created_post.raw_email.should == fixture_file("emails/valid_reply.eml") - created_post.cooked.strip.should == fixture_file("emails/valid_reply.cooked").strip + expect(created_post.via_email).to eq(true) + expect(created_post.raw_email).to eq(fixture_file("emails/valid_reply.eml")) + expect(created_post.cooked.strip).to eq(fixture_file("emails/valid_reply.cooked").strip) end end @@ -160,9 +286,9 @@ Pleasure to have you here! receiver.process - topic.posts.count.should == (start_count + 1) - topic.posts.last.cooked.strip.should == fixture_file("emails/paragraphs.cooked").strip - topic.posts.last.cooked.should_not match /
/ - Upload.find_by(sha1: upload_sha).should_not == nil + expect(topic.posts.count).to eq(start_count + 1) + expect(topic.posts.last.cooked).to match // + expect(Upload.find_by(sha1: upload_sha)).not_to eq(nil) end end @@ -220,6 +346,14 @@ Pleasure to have you here! end end + describe "auto response email replies should not be accepted" do + let!(:reply_key) { '636ca428858779856c226bb145ef4fad' } + let!(:email_raw) { fixture_file("emails/auto_reply.eml") } + it "raises a AutoGeneratedEmailError" do + expect { receiver.process }.to raise_error(Email::Receiver::AutoGeneratedEmailError) + end + end + end describe "posting reply to a closed topic" do @@ -314,7 +448,7 @@ Pleasure to have you here! expect { receiver.process }.to raise_error(Email::Receiver::InvalidPost) - Topic.count.should == before_topic_count + expect(Topic.count).to eq(before_topic_count) end end @@ -415,7 +549,7 @@ greatest show ever created. Everyone should watch it. user.save process_email(from: user.email, to: to) - user.posts.count.should == 1 + expect(user.posts.count).to eq(1) # email too short message = nil @@ -425,7 +559,7 @@ greatest show ever created. Everyone should watch it. message = e.message end - e.message.should include("too short") + expect(e.message).to include("too short") end end @@ -447,7 +581,7 @@ greatest show ever created. Everyone should watch it. process_email(from: "test@test.com", to: "bob@bob.com") # This is the current implementation but it is wrong, it should register an account - Discourse.system_user.posts.order("id desc").limit(1).pluck(:raw).first.should include("Hey folks") + expect(Discourse.system_user.posts.order("id desc").limit(1).pluck(:raw).first).to include("Hey folks") end diff --git a/spec/components/email/renderer_spec.rb b/spec/components/email/renderer_spec.rb index 946f858521..7800ae865a 100644 --- a/spec/components/email/renderer_spec.rb +++ b/spec/components/email/renderer_spec.rb @@ -20,7 +20,7 @@ describe Email::Renderer do it "escapes HTML entities from text" do renderer = Email::Renderer.new(message) - renderer.text.should == "Key & Peele" + expect(renderer.text).to eq("Key & Peele") end end diff --git a/spec/components/email/sender_spec.rb b/spec/components/email/sender_spec.rb index f3b027d8ef..55f56e4c35 100644 --- a/spec/components/email/sender_spec.rb +++ b/spec/components/email/sender_spec.rb @@ -29,19 +29,19 @@ describe Email::Sender do context "host_for" do it "defaults to localhost" do - Email::Sender.host_for(nil).should == "localhost" + expect(Email::Sender.host_for(nil)).to eq("localhost") end it "returns localhost for a weird host" do - Email::Sender.host_for("this is not a real host").should == "localhost" + expect(Email::Sender.host_for("this is not a real host")).to eq("localhost") end it "parses hosts from urls" do - Email::Sender.host_for("http://meta.discourse.org").should == "meta.discourse.org" + expect(Email::Sender.host_for("http://meta.discourse.org")).to eq("meta.discourse.org") end it "downcases hosts" do - Email::Sender.host_for("http://ForumSite.com").should == "forumsite.com" + expect(Email::Sender.host_for("http://ForumSite.com")).to eq("forumsite.com") end end @@ -73,6 +73,11 @@ describe Email::Sender do Then { expect(message.header['List-ID']).to be_present } end + context "adds a Message-ID header even when topic id is not present" do + When { email_sender.send } + Then { expect(message.header['Message-ID']).to be_present } + end + context "adds Precedence header" do before do message.header['X-Discourse-Topic-Id'] = 5577 @@ -82,6 +87,24 @@ describe Email::Sender do Then { expect(message.header['Precedence']).to be_present } end + context "removes custom Discourse headers from topic notification mails" do + before do + message.header['X-Discourse-Topic-Id'] = 5577 + end + + When { email_sender.send } + Then { expect(message.header['X-Discourse-Topic-Id']).not_to be_present } + Then { expect(message.header['X-Discourse-Post-Id']).not_to be_present } + Then { expect(message.header['X-Discourse-Reply-Key']).not_to be_present } + end + + context "removes custom Discourse headers from digest/registration/other mails" do + When { email_sender.send } + Then { expect(message.header['X-Discourse-Topic-Id']).not_to be_present } + Then { expect(message.header['X-Discourse-Post-Id']).not_to be_present } + Then { expect(message.header['X-Discourse-Reply-Key']).not_to be_present } + end + context 'email logs' do let(:email_log) { EmailLog.last } @@ -144,7 +167,7 @@ describe Email::Sender do end it 'should have the current user_id' do - @email_log.user_id.should == user.id + expect(@email_log.user_id).to eq(user.id) end diff --git a/spec/components/email/styles_spec.rb b/spec/components/email/styles_spec.rb index 846944a51b..e062d654bc 100644 --- a/spec/components/email/styles_spec.rb +++ b/spec/components/email/styles_spec.rb @@ -31,7 +31,7 @@ describe Email::Styles do end it "adds a width and height to images with an emoji path" do - frag = basic_fragment("") + frag = basic_fragment("") expect(frag.at("img")["width"]).to eq("20") expect(frag.at("img")["height"]).to eq("20") end @@ -100,7 +100,7 @@ describe Email::Styles do context "rewriting protocol relative URLs to the forum" do it "doesn't rewrite a url to another site" do frag = html_fragment('hello') - frag.at('a')['href'].should == "//youtube.com/discourse" + expect(frag.at('a')['href']).to eq("//youtube.com/discourse") end context "without https" do @@ -110,17 +110,17 @@ describe Email::Styles do it "rewrites the href to have http" do frag = html_fragment('hello') - frag.at('a')['href'].should == "http://test.localhost/discourse" + expect(frag.at('a')['href']).to eq("http://test.localhost/discourse") end it "rewrites the href for attachment files to have http" do frag = html_fragment('attachment_file.txt') - frag.at('a')['href'].should == "http://try-discourse.global.ssl.fastly.net/uploads/default/368/40b610b0aa90cfcf.txt" + expect(frag.at('a')['href']).to eq("http://try-discourse.global.ssl.fastly.net/uploads/default/368/40b610b0aa90cfcf.txt") end it "rewrites the src to have http" do frag = html_fragment('') - frag.at('img')['src'].should == "http://test.localhost/blah.jpg" + expect(frag.at('img')['src']).to eq("http://test.localhost/blah.jpg") end end @@ -131,17 +131,17 @@ describe Email::Styles do it "rewrites the forum URL to have https" do frag = html_fragment('hello') - frag.at('a')['href'].should == "https://test.localhost/discourse" + expect(frag.at('a')['href']).to eq("https://test.localhost/discourse") end it "rewrites the href for attachment files to have https" do frag = html_fragment('attachment_file.txt') - frag.at('a')['href'].should == "https://try-discourse.global.ssl.fastly.net/uploads/default/368/40b610b0aa90cfcf.txt" + expect(frag.at('a')['href']).to eq("https://try-discourse.global.ssl.fastly.net/uploads/default/368/40b610b0aa90cfcf.txt") end it "rewrites the src to have https" do frag = html_fragment('') - frag.at('img')['src'].should == "https://test.localhost/blah.jpg" + expect(frag.at('img')['src']).to eq("https://test.localhost/blah.jpg") end end diff --git a/spec/components/enum_spec.rb b/spec/components/enum_spec.rb index f0c6ca95b9..58ab150ed1 100644 --- a/spec/components/enum_spec.rb +++ b/spec/components/enum_spec.rb @@ -6,33 +6,33 @@ describe Enum do describe ".[]" do it "looks up a number by symbol" do - enum[:princess_bubblegum].should == 3 + expect(enum[:princess_bubblegum]).to eq(3) end it "looks up a symbol by number" do - enum[2].should == :finn + expect(enum[2]).to eq(:finn) end end describe ".valid?" do it "returns true if a key exists" do - enum.valid?(:finn).should == true + expect(enum.valid?(:finn)).to eq(true) end it "returns false if a key does not exist" do - enum.valid?(:obama).should == false + expect(enum.valid?(:obama)).to eq(false) end end describe ".only" do it "returns only the values we ask for" do - enum.only(:jake, :princess_bubblegum).should == { jake: 1, princess_bubblegum: 3 } + expect(enum.only(:jake, :princess_bubblegum)).to eq({ jake: 1, princess_bubblegum: 3 }) end end describe ".except" do it "returns everything but the values we ask to delete" do - enum.except(:jake, :princess_bubblegum).should == { finn: 2, peppermint_butler: 4 } + expect(enum.except(:jake, :princess_bubblegum)).to eq({ finn: 2, peppermint_butler: 4 }) end end end diff --git a/spec/components/file_store/local_store_spec.rb b/spec/components/file_store/local_store_spec.rb index 097976dcf0..8cc2d2ea5c 100644 --- a/spec/components/file_store/local_store_spec.rb +++ b/spec/components/file_store/local_store_spec.rb @@ -18,7 +18,7 @@ describe FileStore::LocalStore do Time.stubs(:now).returns(Time.utc(2013, 2, 17, 12, 0, 0, 0)) upload.stubs(:id).returns(42) store.expects(:copy_file) - store.store_upload(uploaded_file, upload).should == "/uploads/default/42/253dc8edf9d4ada1.png" + expect(store.store_upload(uploaded_file, upload)).to eq("/uploads/default/42/253dc8edf9d4ada1.png") end end @@ -27,7 +27,7 @@ describe FileStore::LocalStore do it "returns a relative url" do store.expects(:copy_file) - store.store_optimized_image({}, optimized_image).should == "/uploads/default/_optimized/86f/7e4/37faa5a7fc_100x200.png" + expect(store.store_optimized_image({}, optimized_image)).to eq("/uploads/default/_optimized/86f/7e4/37faa5a7fc_100x200.png") end end @@ -66,24 +66,24 @@ describe FileStore::LocalStore do describe ".has_been_uploaded?" do it "identifies relatives urls" do - store.has_been_uploaded?("/uploads/default/42/0123456789ABCDEF.jpg").should == true + expect(store.has_been_uploaded?("/uploads/default/42/0123456789ABCDEF.jpg")).to eq(true) end it "identifies local urls" do Discourse.stubs(:base_url_no_prefix).returns("http://discuss.site.com") - store.has_been_uploaded?("http://discuss.site.com/uploads/default/42/0123456789ABCDEF.jpg").should == true - store.has_been_uploaded?("//discuss.site.com/uploads/default/42/0123456789ABCDEF.jpg").should == true + expect(store.has_been_uploaded?("http://discuss.site.com/uploads/default/42/0123456789ABCDEF.jpg")).to eq(true) + expect(store.has_been_uploaded?("//discuss.site.com/uploads/default/42/0123456789ABCDEF.jpg")).to eq(true) end it "identifies local urls when using a CDN" do Rails.configuration.action_controller.stubs(:asset_host).returns("http://my.cdn.com") - store.has_been_uploaded?("http://my.cdn.com/uploads/default/42/0123456789ABCDEF.jpg").should == true - store.has_been_uploaded?("//my.cdn.com/uploads/default/42/0123456789ABCDEF.jpg").should == true + expect(store.has_been_uploaded?("http://my.cdn.com/uploads/default/42/0123456789ABCDEF.jpg")).to eq(true) + expect(store.has_been_uploaded?("//my.cdn.com/uploads/default/42/0123456789ABCDEF.jpg")).to eq(true) end it "does not match dummy urls" do - store.has_been_uploaded?("http://domain.com/uploads/default/42/0123456789ABCDEF.jpg").should == false - store.has_been_uploaded?("//domain.com/uploads/default/42/0123456789ABCDEF.jpg").should == false + expect(store.has_been_uploaded?("http://domain.com/uploads/default/42/0123456789ABCDEF.jpg")).to eq(false) + expect(store.has_been_uploaded?("//domain.com/uploads/default/42/0123456789ABCDEF.jpg")).to eq(false) end end @@ -91,7 +91,7 @@ describe FileStore::LocalStore do describe ".absolute_base_url" do it "is present" do - store.absolute_base_url.should == "http://test.localhost/uploads/default" + expect(store.absolute_base_url).to eq("http://test.localhost/uploads/default") end end @@ -99,20 +99,20 @@ describe FileStore::LocalStore do describe ".relative_base_url" do it "is present" do - store.relative_base_url.should == "/uploads/default" + expect(store.relative_base_url).to eq("/uploads/default") end end it "is internal" do - store.internal?.should == true - store.external?.should == false + expect(store.internal?).to eq(true) + expect(store.external?).to eq(false) end describe ".avatar_template" do it "is present" do - store.avatar_template(avatar).should == "/uploads/default/avatars/e9d/71f/5ee7c92d6d/{size}.png" + expect(store.avatar_template(avatar)).to eq("/uploads/default/avatars/e9d/71f/5ee7c92d6d/{size}.png") end end diff --git a/spec/components/file_store/s3_store_spec.rb b/spec/components/file_store/s3_store_spec.rb index ad0b2fecca..8b78803d95 100644 --- a/spec/components/file_store/s3_store_spec.rb +++ b/spec/components/file_store/s3_store_spec.rb @@ -26,7 +26,7 @@ describe FileStore::S3Store do upload.stubs(:id).returns(42) upload.stubs(:extension).returns(".png") s3_helper.expects(:upload) - store.store_upload(uploaded_file, upload).should == "//s3_upload_bucket.s3.amazonaws.com/42e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98.png" + expect(store.store_upload(uploaded_file, upload)).to eq("//s3_upload_bucket.s3.amazonaws.com/42e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98.png") end end @@ -36,7 +36,7 @@ describe FileStore::S3Store do it "returns an absolute schemaless url" do optimized_image.stubs(:id).returns(42) s3_helper.expects(:upload) - store.store_optimized_image(optimized_image_file, optimized_image).should == "//s3_upload_bucket.s3.amazonaws.com/4286f7e437faa5a7fce15d1ddcb9eaeaea377667b8_100x200.png" + expect(store.store_optimized_image(optimized_image_file, optimized_image)).to eq("//s3_upload_bucket.s3.amazonaws.com/4286f7e437faa5a7fce15d1ddcb9eaeaea377667b8_100x200.png") end end @@ -62,12 +62,12 @@ describe FileStore::S3Store do describe ".has_been_uploaded?" do it "identifies S3 uploads" do - store.has_been_uploaded?("//s3_upload_bucket.s3.amazonaws.com/1337.png").should == true + expect(store.has_been_uploaded?("//s3_upload_bucket.s3.amazonaws.com/1337.png")).to eq(true) end it "does not match other s3 urls" do - store.has_been_uploaded?("//s3.amazonaws.com/s3_upload_bucket/1337.png").should == false - store.has_been_uploaded?("//s4_upload_bucket.s3.amazonaws.com/1337.png").should == false + expect(store.has_been_uploaded?("//s3.amazonaws.com/s3_upload_bucket/1337.png")).to eq(false) + expect(store.has_been_uploaded?("//s4_upload_bucket.s3.amazonaws.com/1337.png")).to eq(false) end end @@ -75,14 +75,14 @@ describe FileStore::S3Store do describe ".absolute_base_url" do it "returns a lowercase schemaless absolute url" do - store.absolute_base_url.should == "//s3_upload_bucket.s3.amazonaws.com" + expect(store.absolute_base_url).to eq("//s3_upload_bucket.s3.amazonaws.com") end end it "is external" do - store.external?.should == true - store.internal?.should == false + expect(store.external?).to eq(true) + expect(store.internal?).to eq(false) end describe ".download" do @@ -105,7 +105,7 @@ describe FileStore::S3Store do describe ".avatar_template" do it "is present" do - store.avatar_template(avatar).should == "//s3_upload_bucket.s3.amazonaws.com/avatars/e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98/{size}.png" + expect(store.avatar_template(avatar)).to eq("//s3_upload_bucket.s3.amazonaws.com/avatars/e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98/{size}.png") end end diff --git a/spec/components/filter_best_posts_spec.rb b/spec/components/filter_best_posts_spec.rb index a435e5112d..ca04ee7834 100644 --- a/spec/components/filter_best_posts_spec.rb +++ b/spec/components/filter_best_posts_spec.rb @@ -21,12 +21,12 @@ describe FilterBestPosts do filtered_posts = TopicView.new(topic.id, coding_horror, best: 2).filtered_posts best2 = FilterBestPosts.new(topic, filtered_posts, 2) - best2.posts.count.should == 2 - best2.posts[0].id.should == p2.id - best2.posts[1].id.should == p3.id + expect(best2.posts.count).to eq(2) + expect(best2.posts[0].id).to eq(p2.id) + expect(best2.posts[1].id).to eq(p3.id) topic.update_status('closed', true, Fabricate(:admin)) - topic.posts.count.should == 4 + expect(topic.posts.count).to eq(4) end describe "processing options" do @@ -35,57 +35,57 @@ describe FilterBestPosts do it "should not get the status post" do best = FilterBestPosts.new(topic, @filtered_posts, 99) - best.filtered_posts.size.should == 3 - best.posts.map(&:id).should =~ [p2.id, p3.id] + expect(best.filtered_posts.size).to eq(3) + expect(best.posts.map(&:id)).to match_array([p2.id, p3.id]) end it "should get no results for trust level too low" do best = FilterBestPosts.new(topic, @filtered_posts, 99, min_trust_level: coding_horror.trust_level + 1) - best.posts.count.should == 0 + expect(best.posts.count).to eq(0) end it "should filter out the posts with a score that is too low" do best = FilterBestPosts.new(topic, @filtered_posts, 99, min_score: 99) - best.posts.count.should == 0 + expect(best.posts.count).to eq(0) end it "should filter out everything if min replies not met" do best = FilterBestPosts.new(topic, @filtered_posts, 99, min_replies: 99) - best.posts.count.should == 0 + expect(best.posts.count).to eq(0) end it "should punch through posts if the score is high enough" do p2.update_column(:score, 100) best = FilterBestPosts.new(topic, @filtered_posts, 99, bypass_trust_level_score: 100, min_trust_level: coding_horror.trust_level + 1) - best.posts.count.should == 1 + expect(best.posts.count).to eq(1) end it "should bypass trust level score" do best = FilterBestPosts.new(topic, @filtered_posts, 99, bypass_trust_level_score: 0, min_trust_level: coding_horror.trust_level + 1) - best.posts.count.should == 0 + expect(best.posts.count).to eq(0) end it "should return none if restricted to posts a moderator liked" do best = FilterBestPosts.new(topic, @filtered_posts, 99, only_moderator_liked: true) - best.posts.count.should == 0 + expect(best.posts.count).to eq(0) end it "doesn't count likes from admins" do PostAction.act(admin, p3, PostActionType.types[:like]) best = FilterBestPosts.new(topic, @filtered_posts, 99, only_moderator_liked: true) - best.posts.count.should == 0 + expect(best.posts.count).to eq(0) end it "should find the post liked by the moderator" do PostAction.act(moderator, p2, PostActionType.types[:like]) best = FilterBestPosts.new(topic, @filtered_posts, 99, only_moderator_liked: true) - best.posts.count.should == 1 + expect(best.posts.count).to eq(1) end end diff --git a/spec/components/flag_query_spec.rb b/spec/components/flag_query_spec.rb index a6a1ac3f9b..de6f0fb0e2 100644 --- a/spec/components/flag_query_spec.rb +++ b/spec/components/flag_query_spec.rb @@ -24,21 +24,21 @@ describe FlagQuery do PostAction.act(user2, post2, PostActionType.types[:spam]) posts, topics, users = FlagQuery.flagged_posts_report(admin, "") - posts.count.should == 2 + expect(posts.count).to eq(2) first = posts.first - users.count.should == 5 - first[:post_actions].count.should == 2 + expect(users.count).to eq(5) + expect(first[:post_actions].count).to eq(2) - topics.count.should == 2 + expect(topics.count).to eq(2) second = posts[1] - second[:post_actions].count.should == 3 - second[:post_actions].first[:permalink].should == mod_message.related_post.topic.relative_url + expect(second[:post_actions].count).to eq(3) + expect(second[:post_actions].first[:permalink]).to eq(mod_message.related_post.topic.relative_url) posts, users = FlagQuery.flagged_posts_report(admin, "", 1) - posts.count.should == 1 + expect(posts.count).to eq(1) # chuck post in category a mod can not see and make sure its missing category = Fabricate(:category) @@ -49,7 +49,7 @@ describe FlagQuery do posts, users = FlagQuery.flagged_posts_report(moderator, "") - posts.count.should == 1 + expect(posts.count).to eq(1) end end end diff --git a/spec/components/freedom_patches/pool_drainer_spec.rb b/spec/components/freedom_patches/pool_drainer_spec.rb index 9ebfd0e696..ad44e37af3 100644 --- a/spec/components/freedom_patches/pool_drainer_spec.rb +++ b/spec/components/freedom_patches/pool_drainer_spec.rb @@ -8,22 +8,22 @@ describe 'pool drainer' do it 'can correctly drain the connection pool' do pool.drain old = pool.connections.length - old.should == 1 + expect(old).to eq(1) Thread.new do conn = pool.checkout pool.checkin conn end.join - pool.connections.length.should == (old+1) + expect(pool.connections.length).to eq(old+1) pool.drain - pool.connections.length.should == old + expect(pool.connections.length).to eq(old) end it 'can drain with idle time setting' do pool.drain old = pool.connections.length - old.should == 1 + expect(old).to eq(1) Thread.new do @@ -31,9 +31,9 @@ describe 'pool drainer' do pool.checkin conn end.join - pool.connections.length.should == (old+1) + expect(pool.connections.length).to eq(old+1) pool.drain(1.minute) - pool.connections.length.should == (old+1) + expect(pool.connections.length).to eq(old+1) # make sure we don't corrupt internal state 20.times do diff --git a/spec/components/freedom_patches/safe_buffer_spec.rb b/spec/components/freedom_patches/safe_buffer_spec.rb index 34bf78f853..61bba0de9e 100644 --- a/spec/components/freedom_patches/safe_buffer_spec.rb +++ b/spec/components/freedom_patches/safe_buffer_spec.rb @@ -10,6 +10,6 @@ describe ActiveSupport::SafeBuffer do buffer << "hello#{254.chr}".force_encoding("ASCII-8BIT").freeze # we pay a cost for force encoding, the h gets dropped - buffer.should =~ /ello.*hello/ + expect(buffer).to match(/ello.*hello/) end end diff --git a/spec/components/gaps_spec.rb b/spec/components/gaps_spec.rb index 9941ce25db..780d297ef0 100644 --- a/spec/components/gaps_spec.rb +++ b/spec/components/gaps_spec.rb @@ -5,24 +5,24 @@ describe Gaps do it 'returns no gaps for empty data' do - Gaps.new(nil, nil).should be_blank + expect(Gaps.new(nil, nil)).to be_blank end it 'returns no gaps with one element' do - Gaps.new([1], [1]).should be_blank + expect(Gaps.new([1], [1])).to be_blank end it 'returns no gaps when all elements are present' do - Gaps.new([1,2,3], [1,2,3]).should be_blank + expect(Gaps.new([1,2,3], [1,2,3])).to be_blank end context "single element gap" do let(:gap) { Gaps.new([1,3], [1,2,3]) } it 'has a gap for post 3' do - gap.should_not be_blank - gap.before[3].should == [2] - gap.after.should be_blank + expect(gap).not_to be_blank + expect(gap.before[3]).to eq([2]) + expect(gap.after).to be_blank end end @@ -30,9 +30,9 @@ describe Gaps do let(:gap) { Gaps.new([1,2,3,6,7], [1,2,3,4,5,6,7]) } it 'has a gap for post 6' do - gap.should_not be_blank - gap.before[6].should == [4,5] - gap.after.should be_blank + expect(gap).not_to be_blank + expect(gap.before[6]).to eq([4,5]) + expect(gap.after).to be_blank end end @@ -40,10 +40,10 @@ describe Gaps do let(:gap) { Gaps.new([1,5,6,7,10], [1,2,3,4,5,6,7,8,9,10]) } it 'has both gaps' do - gap.should_not be_blank - gap.before[5].should == [2,3,4] - gap.before[10].should == [8,9] - gap.after.should be_blank + expect(gap).not_to be_blank + expect(gap.before[5]).to eq([2,3,4]) + expect(gap.before[10]).to eq([8,9]) + expect(gap.after).to be_blank end end @@ -51,9 +51,9 @@ describe Gaps do let(:gap) { Gaps.new([2,3,4], [1,2,3,4]) } it 'has the gap' do - gap.should_not be_blank - gap.before[2].should == [1] - gap.after.should be_blank + expect(gap).not_to be_blank + expect(gap.before[2]).to eq([1]) + expect(gap.after).to be_blank end end @@ -61,9 +61,9 @@ describe Gaps do let(:gap) { Gaps.new([1,2,3], [1,2,3,4]) } it 'has the gap' do - gap.should_not be_blank - gap.before.should be_blank - gap.after[3].should == [4] + expect(gap).not_to be_blank + expect(gap.before).to be_blank + expect(gap.after[3]).to eq([4]) end end @@ -71,9 +71,9 @@ describe Gaps do let(:gap) { Gaps.new([1,2,3], [1,2,3,4,5,6]) } it 'has the gap' do - gap.should_not be_blank - gap.before.should be_blank - gap.after[3].should == [4,5,6] + expect(gap).not_to be_blank + expect(gap.before).to be_blank + expect(gap.after[3]).to eq([4,5,6]) end end diff --git a/spec/components/guardian_spec.rb b/spec/components/guardian_spec.rb index d5ec6ccf45..e39f46b838 100644 --- a/spec/components/guardian_spec.rb +++ b/spec/components/guardian_spec.rb @@ -16,11 +16,11 @@ describe Guardian do let(:post) { build(:post, topic: topic, user: topic.user) } it 'can be created without a user (not logged in)' do - lambda { Guardian.new }.should_not raise_error + expect { Guardian.new }.not_to raise_error end - it 'can be instantiaed with a user instance' do - lambda { Guardian.new(user) }.should_not raise_error + it 'can be instantiated with a user instance' do + expect { Guardian.new(user) }.not_to raise_error end describe 'post_can_act?' do @@ -28,59 +28,72 @@ describe Guardian do let(:user) { build(:user) } it "returns false when the user is nil" do - Guardian.new(nil).post_can_act?(post, :like).should be_falsey + expect(Guardian.new(nil).post_can_act?(post, :like)).to be_falsey end it "returns false when the post is nil" do - Guardian.new(user).post_can_act?(nil, :like).should be_falsey + expect(Guardian.new(user).post_can_act?(nil, :like)).to be_falsey end it "returns false when the topic is archived" do post.topic.archived = true - Guardian.new(user).post_can_act?(post, :like).should be_falsey + expect(Guardian.new(user).post_can_act?(post, :like)).to be_falsey end it "returns false when the post is deleted" do post.deleted_at = Time.now - Guardian.new(user).post_can_act?(post, :like).should be_falsey + expect(Guardian.new(user).post_can_act?(post, :like)).to be_falsey end it "always allows flagging" do post.topic.archived = true - Guardian.new(user).post_can_act?(post, :spam).should be_truthy + expect(Guardian.new(user).post_can_act?(post, :spam)).to be_truthy end it "returns false when liking yourself" do - Guardian.new(post.user).post_can_act?(post, :like).should be_falsey + expect(Guardian.new(post.user).post_can_act?(post, :like)).to be_falsey end it "returns false when you've already done it" do - Guardian.new(user).post_can_act?(post, :like, taken_actions: {PostActionType.types[:like] => 1}).should be_falsey + expect(Guardian.new(user).post_can_act?(post, :like, taken_actions: {PostActionType.types[:like] => 1})).to be_falsey end it "returns false when you already flagged a post" do - Guardian.new(user).post_can_act?(post, :off_topic, taken_actions: {PostActionType.types[:spam] => 1}).should be_falsey + expect(Guardian.new(user).post_can_act?(post, :off_topic, taken_actions: {PostActionType.types[:spam] => 1})).to be_falsey + end + + it "returns false for notify_user if private messages are disabled" do + SiteSetting.stubs(:enable_private_messages).returns(false) + user.trust_level = TrustLevel[2] + expect(Guardian.new(user).post_can_act?(post, :notify_user)).to be_falsey + expect(Guardian.new(user).post_can_act?(post, :notify_moderators)).to be_falsey end describe "trust levels" do it "returns true for a new user liking something" do user.trust_level = TrustLevel[0] - Guardian.new(user).post_can_act?(post, :like).should be_truthy + expect(Guardian.new(user).post_can_act?(post, :like)).to be_truthy end - it "returns false for a new user flagging something as spam" do + it "returns false for a new user flagging a standard post as spam" do user.trust_level = TrustLevel[0] - Guardian.new(user).post_can_act?(post, :spam).should be_falsey + expect(Guardian.new(user).post_can_act?(post, :spam)).to be_falsey + end + + it "returns true for a new user flagging a private message as spam" do + post.topic.archetype = Archetype.private_message + user.trust_level = TrustLevel[0] + expect(Guardian.new(user).post_can_act?(post, :spam)).to be_truthy end it "returns false for a new user flagging something as off topic" do user.trust_level = TrustLevel[0] - Guardian.new(user).post_can_act?(post, :off_topic).should be_falsey + expect(Guardian.new(user).post_can_act?(post, :off_topic)).to be_falsey end it "returns false for a new user flagging with notify_user" do user.trust_level = TrustLevel[0] - Guardian.new(user).post_can_act?(post, :notify_user).should be_falsey # because new users can't send private messages + expect(Guardian.new(user).post_can_act?(post, :notify_user)).to be_falsey # because new users can't send private messages end end end @@ -92,19 +105,19 @@ describe Guardian do let(:moderator) { Fabricate(:moderator) } it "returns false when the user is nil" do - Guardian.new(nil).can_defer_flags?(post).should be_falsey + expect(Guardian.new(nil).can_defer_flags?(post)).to be_falsey end it "returns false when the post is nil" do - Guardian.new(moderator).can_defer_flags?(nil).should be_falsey + expect(Guardian.new(moderator).can_defer_flags?(nil)).to be_falsey end it "returns false when the user is not a moderator" do - Guardian.new(user).can_defer_flags?(post).should be_falsey + expect(Guardian.new(user).can_defer_flags?(post)).to be_falsey end it "returns true when the user is a moderator" do - Guardian.new(moderator).can_defer_flags?(post).should be_truthy + expect(Guardian.new(moderator).can_defer_flags?(post)).to be_truthy end end @@ -115,48 +128,48 @@ describe Guardian do let(:suspended_user) { Fabricate(:user, suspended_till: 1.week.from_now, suspended_at: 1.day.ago) } it "returns false when the user is nil" do - Guardian.new(nil).can_send_private_message?(user).should be_falsey + expect(Guardian.new(nil).can_send_private_message?(user)).to be_falsey end it "returns false when the target user is nil" do - Guardian.new(user).can_send_private_message?(nil).should be_falsey + expect(Guardian.new(user).can_send_private_message?(nil)).to be_falsey end it "returns false when the target is the same as the user" do - Guardian.new(user).can_send_private_message?(user).should be_falsey + expect(Guardian.new(user).can_send_private_message?(user)).to be_falsey end it "returns false when you are untrusted" do user.trust_level = TrustLevel[0] - Guardian.new(user).can_send_private_message?(another_user).should be_falsey + expect(Guardian.new(user).can_send_private_message?(another_user)).to be_falsey end it "returns true to another user" do - Guardian.new(user).can_send_private_message?(another_user).should be_truthy + expect(Guardian.new(user).can_send_private_message?(another_user)).to be_truthy end context "enable_private_messages is false" do before { SiteSetting.stubs(:enable_private_messages).returns(false) } it "returns false if user is not the contact user" do - Guardian.new(user).can_send_private_message?(another_user).should be_falsey + expect(Guardian.new(user).can_send_private_message?(another_user)).to be_falsey end it "returns true for the contact user and system user" do SiteSetting.stubs(:site_contact_username).returns(user.username) - Guardian.new(user).can_send_private_message?(another_user).should be_truthy - Guardian.new(Discourse.system_user).can_send_private_message?(another_user).should be_truthy + expect(Guardian.new(user).can_send_private_message?(another_user)).to be_truthy + expect(Guardian.new(Discourse.system_user).can_send_private_message?(another_user)).to be_truthy end end context "target user is suspended" do it "returns true for staff" do - Guardian.new(admin).can_send_private_message?(suspended_user).should be_truthy - Guardian.new(moderator).can_send_private_message?(suspended_user).should be_truthy + expect(Guardian.new(admin).can_send_private_message?(suspended_user)).to be_truthy + expect(Guardian.new(moderator).can_send_private_message?(suspended_user)).to be_truthy end it "returns false for regular users" do - Guardian.new(user).can_send_private_message?(suspended_user).should be_falsey + expect(Guardian.new(user).can_send_private_message?(suspended_user)).to be_falsey end end end @@ -166,20 +179,20 @@ describe Guardian do let(:topic) { Fabricate(:topic) } it "returns false for a non logged in user" do - Guardian.new(nil).can_reply_as_new_topic?(topic).should be_falsey + expect(Guardian.new(nil).can_reply_as_new_topic?(topic)).to be_falsey end it "returns false for a nil topic" do - Guardian.new(user).can_reply_as_new_topic?(nil).should be_falsey + expect(Guardian.new(user).can_reply_as_new_topic?(nil)).to be_falsey end it "returns false for an untrusted user" do user.trust_level = TrustLevel[0] - Guardian.new(user).can_reply_as_new_topic?(topic).should be_falsey + expect(Guardian.new(user).can_reply_as_new_topic?(topic)).to be_falsey end it "returns true for a trusted user" do - Guardian.new(user).can_reply_as_new_topic?(topic).should be_truthy + expect(Guardian.new(user).can_reply_as_new_topic?(topic)).to be_truthy end end @@ -189,33 +202,33 @@ describe Guardian do it 'displays visibility correctly' do guardian = Guardian.new(user) - guardian.can_see_post_actors?(nil, PostActionType.types[:like]).should be_falsey - guardian.can_see_post_actors?(topic, PostActionType.types[:like]).should be_truthy - guardian.can_see_post_actors?(topic, PostActionType.types[:bookmark]).should be_falsey - guardian.can_see_post_actors?(topic, PostActionType.types[:off_topic]).should be_falsey - guardian.can_see_post_actors?(topic, PostActionType.types[:spam]).should be_falsey - guardian.can_see_post_actors?(topic, PostActionType.types[:vote]).should be_truthy + expect(guardian.can_see_post_actors?(nil, PostActionType.types[:like])).to be_falsey + expect(guardian.can_see_post_actors?(topic, PostActionType.types[:like])).to be_truthy + expect(guardian.can_see_post_actors?(topic, PostActionType.types[:bookmark])).to be_falsey + expect(guardian.can_see_post_actors?(topic, PostActionType.types[:off_topic])).to be_falsey + expect(guardian.can_see_post_actors?(topic, PostActionType.types[:spam])).to be_falsey + expect(guardian.can_see_post_actors?(topic, PostActionType.types[:vote])).to be_truthy end it 'returns false for private votes' do topic.expects(:has_meta_data_boolean?).with(:private_poll).returns(true) - Guardian.new(user).can_see_post_actors?(topic, PostActionType.types[:vote]).should be_falsey + expect(Guardian.new(user).can_see_post_actors?(topic, PostActionType.types[:vote])).to be_falsey end end describe 'can_impersonate?' do it 'allows impersonation correctly' do - Guardian.new(admin).can_impersonate?(nil).should be_falsey - Guardian.new.can_impersonate?(user).should be_falsey - Guardian.new(coding_horror).can_impersonate?(user).should be_falsey - Guardian.new(admin).can_impersonate?(admin).should be_falsey - Guardian.new(admin).can_impersonate?(another_admin).should be_falsey - Guardian.new(admin).can_impersonate?(user).should be_truthy - Guardian.new(admin).can_impersonate?(moderator).should be_truthy + expect(Guardian.new(admin).can_impersonate?(nil)).to be_falsey + expect(Guardian.new.can_impersonate?(user)).to be_falsey + expect(Guardian.new(coding_horror).can_impersonate?(user)).to be_falsey + expect(Guardian.new(admin).can_impersonate?(admin)).to be_falsey + expect(Guardian.new(admin).can_impersonate?(another_admin)).to be_falsey + expect(Guardian.new(admin).can_impersonate?(user)).to be_truthy + expect(Guardian.new(admin).can_impersonate?(moderator)).to be_truthy Rails.configuration.stubs(:developer_emails).returns([admin.email]) - Guardian.new(admin).can_impersonate?(another_admin).should be_truthy + expect(Guardian.new(admin).can_impersonate?(another_admin)).to be_truthy end end @@ -224,23 +237,23 @@ describe Guardian do let(:moderator) { Fabricate.build(:moderator) } it "doesn't allow anonymous users to invite" do - Guardian.new.can_invite_to_forum?.should be_falsey + expect(Guardian.new.can_invite_to_forum?).to be_falsey end it 'returns true when the site requires approving users and is mod' do SiteSetting.expects(:must_approve_users?).returns(true) - Guardian.new(moderator).can_invite_to_forum?.should be_truthy + expect(Guardian.new(moderator).can_invite_to_forum?).to be_truthy end it 'returns false when the site requires approving users and is regular' do SiteSetting.expects(:must_approve_users?).returns(true) - Guardian.new(user).can_invite_to_forum?.should be_falsey + expect(Guardian.new(user).can_invite_to_forum?).to be_falsey end it 'returns false when the local logins are disabled' do SiteSetting.stubs(:enable_local_logins).returns(false) - Guardian.new(user).can_invite_to_forum?.should be_falsey - Guardian.new(moderator).can_invite_to_forum?.should be_falsey + expect(Guardian.new(user).can_invite_to_forum?).to be_falsey + expect(Guardian.new(moderator).can_invite_to_forum?).to be_falsey end end @@ -255,34 +268,34 @@ describe Guardian do let(:admin) { Fabricate(:admin) } it 'handles invitation correctly' do - Guardian.new(nil).can_invite_to?(topic).should be_falsey - Guardian.new(moderator).can_invite_to?(nil).should be_falsey - Guardian.new(moderator).can_invite_to?(topic).should be_truthy - Guardian.new(user).can_invite_to?(topic).should be_falsey + expect(Guardian.new(nil).can_invite_to?(topic)).to be_falsey + expect(Guardian.new(moderator).can_invite_to?(nil)).to be_falsey + expect(Guardian.new(moderator).can_invite_to?(topic)).to be_truthy + expect(Guardian.new(user).can_invite_to?(topic)).to be_falsey end it 'returns true when the site requires approving users and is mod' do SiteSetting.expects(:must_approve_users?).returns(true) - Guardian.new(moderator).can_invite_to?(topic).should be_truthy + expect(Guardian.new(moderator).can_invite_to?(topic)).to be_truthy end it 'returns false when the site requires approving users and is regular' do SiteSetting.expects(:must_approve_users?).returns(true) - Guardian.new(coding_horror).can_invite_to?(topic).should be_falsey + expect(Guardian.new(coding_horror).can_invite_to?(topic)).to be_falsey end it 'returns false when local logins are disabled' do SiteSetting.stubs(:enable_local_logins).returns(false) - Guardian.new(moderator).can_invite_to?(topic).should be_falsey - Guardian.new(user).can_invite_to?(topic).should be_falsey + expect(Guardian.new(moderator).can_invite_to?(topic)).to be_falsey + expect(Guardian.new(user).can_invite_to?(topic)).to be_falsey end it 'returns false for normal user on private topic' do - Guardian.new(user).can_invite_to?(private_topic).should be_falsey + expect(Guardian.new(user).can_invite_to?(private_topic)).to be_falsey end it 'returns true for admin on private topic' do - Guardian.new(admin).can_invite_to?(private_topic).should be_truthy + expect(Guardian.new(admin).can_invite_to?(private_topic)).to be_truthy end end @@ -290,7 +303,7 @@ describe Guardian do describe 'can_see?' do it 'returns false with a nil object' do - Guardian.new.can_see?(nil).should be_falsey + expect(Guardian.new.can_see?(nil)).to be_falsey end describe 'a Group' do @@ -298,22 +311,22 @@ describe Guardian do let(:invisible_group) { Group.new(visible: false) } it "returns true when the group is visible" do - Guardian.new.can_see?(group).should be_truthy + expect(Guardian.new.can_see?(group)).to be_truthy end it "returns true when the group is visible but the user is an admin" do admin = Fabricate.build(:admin) - Guardian.new(admin).can_see?(invisible_group).should be_truthy + expect(Guardian.new(admin).can_see?(invisible_group)).to be_truthy end it "returns false when the group is invisible" do - Guardian.new.can_see?(invisible_group).should be_falsey + expect(Guardian.new.can_see?(invisible_group)).to be_falsey end end describe 'a Topic' do it 'allows non logged in users to view topics' do - Guardian.new.can_see?(topic).should be_truthy + expect(Guardian.new.can_see?(topic)).to be_truthy end it 'correctly handles groups' do @@ -324,29 +337,29 @@ describe Guardian do topic = Fabricate(:topic, category: category) - Guardian.new(user).can_see?(topic).should be_falsey + expect(Guardian.new(user).can_see?(topic)).to be_falsey group.add(user) group.save - Guardian.new(user).can_see?(topic).should be_truthy + expect(Guardian.new(user).can_see?(topic)).to be_truthy end it "restricts deleted topics" do topic = Fabricate(:topic) topic.trash!(moderator) - Guardian.new(build(:user)).can_see?(topic).should be_falsey - Guardian.new(moderator).can_see?(topic).should be_truthy - Guardian.new(admin).can_see?(topic).should be_truthy + expect(Guardian.new(build(:user)).can_see?(topic)).to be_falsey + expect(Guardian.new(moderator).can_see?(topic)).to be_truthy + expect(Guardian.new(admin).can_see?(topic)).to be_truthy end it "restricts private topics" do user.save! private_topic = Fabricate(:private_message_topic, user: user) - Guardian.new(private_topic.user).can_see?(private_topic).should be_truthy - Guardian.new(build(:user)).can_see?(private_topic).should be_falsey - Guardian.new(moderator).can_see?(private_topic).should be_falsey - Guardian.new(admin).can_see?(private_topic).should be_truthy + expect(Guardian.new(private_topic.user).can_see?(private_topic)).to be_truthy + expect(Guardian.new(build(:user)).can_see?(private_topic)).to be_falsey + expect(Guardian.new(moderator).can_see?(private_topic)).to be_falsey + expect(Guardian.new(admin).can_see?(private_topic)).to be_truthy end it "restricts private deleted topics" do @@ -354,19 +367,32 @@ describe Guardian do private_topic = Fabricate(:private_message_topic, user: user) private_topic.trash!(admin) - Guardian.new(private_topic.user).can_see?(private_topic).should be_falsey - Guardian.new(build(:user)).can_see?(private_topic).should be_falsey - Guardian.new(moderator).can_see?(private_topic).should be_falsey - Guardian.new(admin).can_see?(private_topic).should be_truthy + expect(Guardian.new(private_topic.user).can_see?(private_topic)).to be_falsey + expect(Guardian.new(build(:user)).can_see?(private_topic)).to be_falsey + expect(Guardian.new(moderator).can_see?(private_topic)).to be_falsey + expect(Guardian.new(admin).can_see?(private_topic)).to be_truthy end it "restricts static doc topics" do tos_topic = Fabricate(:topic, user: Discourse.system_user) SiteSetting.stubs(:tos_topic_id).returns(tos_topic.id) - Guardian.new(build(:user)).can_edit?(tos_topic).should be_falsey - Guardian.new(moderator).can_edit?(tos_topic).should be_falsey - Guardian.new(admin).can_edit?(tos_topic).should be_truthy + expect(Guardian.new(build(:user)).can_edit?(tos_topic)).to be_falsey + expect(Guardian.new(moderator).can_edit?(tos_topic)).to be_falsey + expect(Guardian.new(admin).can_edit?(tos_topic)).to be_truthy + end + + it "allows moderators to see a flagged private message" do + moderator.save! + user.save! + + private_topic = Fabricate(:private_message_topic, user: user) + first_post = Fabricate(:post, topic: private_topic, user: user) + + expect(Guardian.new(moderator).can_see?(private_topic)).to be_falsey + + PostAction.act(user, first_post, PostActionType.types[:off_topic]) + expect(Guardian.new(moderator).can_see?(private_topic)).to be_truthy end end @@ -376,19 +402,19 @@ describe Guardian do post = Fabricate(:post) topic = post.topic - Guardian.new(user).can_see?(post).should be_truthy + expect(Guardian.new(user).can_see?(post)).to be_truthy post.trash!(another_admin) post.reload - Guardian.new(user).can_see?(post).should be_falsey - Guardian.new(admin).can_see?(post).should be_truthy + expect(Guardian.new(user).can_see?(post)).to be_falsey + expect(Guardian.new(admin).can_see?(post)).to be_truthy post.recover! post.reload topic.trash!(another_admin) topic.reload - Guardian.new(user).can_see?(post).should be_falsey - Guardian.new(admin).can_see?(post).should be_truthy + expect(Guardian.new(user).can_see?(post)).to be_falsey + expect(Guardian.new(admin).can_see?(post)).to be_truthy end end @@ -399,21 +425,21 @@ describe Guardian do before { SiteSetting.stubs(:edit_history_visible_to_public).returns(true) } it 'is false for nil' do - Guardian.new.can_see?(nil).should be_falsey + expect(Guardian.new.can_see?(nil)).to be_falsey end it 'is true if not logged in' do - Guardian.new.can_see?(post_revision).should be_truthy + expect(Guardian.new.can_see?(post_revision)).to be_truthy end it 'is true when logged in' do - Guardian.new(Fabricate(:user)).can_see?(post_revision).should be_truthy + expect(Guardian.new(Fabricate(:user)).can_see?(post_revision)).to be_truthy end it 'is true if the author has public edit history' do public_post_revision = Fabricate(:post_revision) public_post_revision.post.user.edit_history_public = true - Guardian.new.can_see?(public_post_revision).should be_truthy + expect(Guardian.new.can_see?(public_post_revision)).to be_truthy end end @@ -421,22 +447,22 @@ describe Guardian do before { SiteSetting.stubs(:edit_history_visible_to_public).returns(false) } it 'is true for staff' do - Guardian.new(Fabricate(:admin)).can_see?(post_revision).should be_truthy - Guardian.new(Fabricate(:moderator)).can_see?(post_revision).should be_truthy + expect(Guardian.new(Fabricate(:admin)).can_see?(post_revision)).to be_truthy + expect(Guardian.new(Fabricate(:moderator)).can_see?(post_revision)).to be_truthy end it 'is true for trust level 4' do - Guardian.new(trust_level_4).can_see?(post_revision).should be_truthy + expect(Guardian.new(trust_level_4).can_see?(post_revision)).to be_truthy end it 'is false for trust level lower than 4' do - Guardian.new(trust_level_3).can_see?(post_revision).should be_falsey + expect(Guardian.new(trust_level_3).can_see?(post_revision)).to be_falsey end it 'is true if the author has public edit history' do public_post_revision = Fabricate(:post_revision) public_post_revision.post.user.edit_history_public = true - Guardian.new.can_see?(public_post_revision).should be_truthy + expect(Guardian.new.can_see?(public_post_revision)).to be_truthy end end end @@ -447,19 +473,19 @@ describe Guardian do describe 'a Category' do it 'returns false when not logged in' do - Guardian.new.can_create?(Category).should be_falsey + expect(Guardian.new.can_create?(Category)).to be_falsey end it 'returns false when a regular user' do - Guardian.new(user).can_create?(Category).should be_falsey + expect(Guardian.new(user).can_create?(Category)).to be_falsey end it 'returns false when a moderator' do - Guardian.new(moderator).can_create?(Category).should be_falsey + expect(Guardian.new(moderator).can_create?(Category)).to be_falsey end it 'returns true when an admin' do - Guardian.new(admin).can_create?(Category).should be_truthy + expect(Guardian.new(admin).can_create?(Category)).to be_truthy end end @@ -468,24 +494,24 @@ describe Guardian do category = Fabricate(:category) category.set_permissions(:everyone => :create_post) category.save - Guardian.new(user).can_create?(Topic,category).should be_falsey + expect(Guardian.new(user).can_create?(Topic,category)).to be_falsey end it "is true for new users by default" do - Guardian.new(user).can_create?(Topic,Fabricate(:category)).should be_truthy + expect(Guardian.new(user).can_create?(Topic,Fabricate(:category))).to be_truthy end it "is false if user has not met minimum trust level" do SiteSetting.stubs(:min_trust_to_create_topic).returns(1) - Guardian.new(build(:user, trust_level: 0)).can_create?(Topic,Fabricate(:category)).should be_falsey + expect(Guardian.new(build(:user, trust_level: 0)).can_create?(Topic,Fabricate(:category))).to be_falsey end it "is true if user has met or exceeded the minimum trust level" do SiteSetting.stubs(:min_trust_to_create_topic).returns(1) - Guardian.new(build(:user, trust_level: 1)).can_create?(Topic,Fabricate(:category)).should be_truthy - Guardian.new(build(:user, trust_level: 2)).can_create?(Topic,Fabricate(:category)).should be_truthy - Guardian.new(build(:admin, trust_level: 0)).can_create?(Topic,Fabricate(:category)).should be_truthy - Guardian.new(build(:moderator, trust_level: 0)).can_create?(Topic,Fabricate(:category)).should be_truthy + expect(Guardian.new(build(:user, trust_level: 1)).can_create?(Topic,Fabricate(:category))).to be_truthy + expect(Guardian.new(build(:user, trust_level: 2)).can_create?(Topic,Fabricate(:category))).to be_truthy + expect(Guardian.new(build(:admin, trust_level: 0)).can_create?(Topic,Fabricate(:category))).to be_truthy + expect(Guardian.new(build(:moderator, trust_level: 0)).can_create?(Topic,Fabricate(:category))).to be_truthy end end @@ -497,21 +523,21 @@ describe Guardian do category.set_permissions(:everyone => :readonly) category.save - Guardian.new(topic.user).can_create?(Post, topic).should be_falsey + expect(Guardian.new(topic.user).can_create?(Post, topic)).to be_falsey end it "is false when not logged in" do - Guardian.new.can_create?(Post, topic).should be_falsey + expect(Guardian.new.can_create?(Post, topic)).to be_falsey end it 'is true for a regular user' do - Guardian.new(topic.user).can_create?(Post, topic).should be_truthy + expect(Guardian.new(topic.user).can_create?(Post, topic)).to be_truthy end it "is false when you can't see the topic" do Guardian.any_instance.expects(:can_see?).with(topic).returns(false) - Guardian.new(topic.user).can_create?(Post, topic).should be_falsey + expect(Guardian.new(topic.user).can_create?(Post, topic)).to be_falsey end context 'closed topic' do @@ -520,23 +546,23 @@ describe Guardian do end it "doesn't allow new posts from regular users" do - Guardian.new(topic.user).can_create?(Post, topic).should be_falsey + expect(Guardian.new(topic.user).can_create?(Post, topic)).to be_falsey end it 'allows editing of posts' do - Guardian.new(topic.user).can_edit?(post).should be_truthy + expect(Guardian.new(topic.user).can_edit?(post)).to be_truthy end it "allows new posts from moderators" do - Guardian.new(moderator).can_create?(Post, topic).should be_truthy + expect(Guardian.new(moderator).can_create?(Post, topic)).to be_truthy end it "allows new posts from admins" do - Guardian.new(admin).can_create?(Post, topic).should be_truthy + expect(Guardian.new(admin).can_create?(Post, topic)).to be_truthy end it "allows new posts from trust_level_4s" do - Guardian.new(trust_level_4).can_create?(Post, topic).should be_truthy + expect(Guardian.new(trust_level_4).can_create?(Post, topic)).to be_truthy end end @@ -547,20 +573,20 @@ describe Guardian do context 'regular users' do it "doesn't allow new posts from regular users" do - Guardian.new(coding_horror).can_create?(Post, topic).should be_falsey + expect(Guardian.new(coding_horror).can_create?(Post, topic)).to be_falsey end it 'does not allow editing of posts' do - Guardian.new(coding_horror).can_edit?(post).should be_falsey + expect(Guardian.new(coding_horror).can_edit?(post)).to be_falsey end end it "allows new posts from moderators" do - Guardian.new(moderator).can_create?(Post, topic).should be_truthy + expect(Guardian.new(moderator).can_create?(Post, topic)).to be_truthy end it "allows new posts from admins" do - Guardian.new(admin).can_create?(Post, topic).should be_truthy + expect(Guardian.new(admin).can_create?(Post, topic)).to be_truthy end end @@ -570,15 +596,15 @@ describe Guardian do end it "doesn't allow new posts from regular users" do - Guardian.new(coding_horror).can_create?(Post, topic).should be_falsey + expect(Guardian.new(coding_horror).can_create?(Post, topic)).to be_falsey end it "doesn't allow new posts from moderators users" do - Guardian.new(moderator).can_create?(Post, topic).should be_falsey + expect(Guardian.new(moderator).can_create?(Post, topic)).to be_falsey end it "doesn't allow new posts from admins" do - Guardian.new(admin).can_create?(Post, topic).should be_falsey + expect(Guardian.new(admin).can_create?(Post, topic)).to be_falsey end end @@ -591,7 +617,7 @@ describe Guardian do describe 'post_can_act?' do it "isn't allowed on nil" do - Guardian.new(user).post_can_act?(nil, nil).should be_falsey + expect(Guardian.new(user).post_can_act?(nil, nil)).to be_falsey end describe 'a Post' do @@ -602,24 +628,24 @@ describe Guardian do it "isn't allowed when not logged in" do - Guardian.new(nil).post_can_act?(post,:vote).should be_falsey + expect(Guardian.new(nil).post_can_act?(post,:vote)).to be_falsey end it "is allowed as a regular user" do - guardian.post_can_act?(post,:vote).should be_truthy + expect(guardian.post_can_act?(post,:vote)).to be_truthy end it "doesn't allow voting if the user has an action from voting already" do - guardian.post_can_act?(post,:vote,taken_actions: {PostActionType.types[:vote] => 1}).should be_falsey + expect(guardian.post_can_act?(post,:vote,taken_actions: {PostActionType.types[:vote] => 1})).to be_falsey end it "allows voting if the user has performed a different action" do - guardian.post_can_act?(post,:vote,taken_actions: {PostActionType.types[:like] => 1}).should be_truthy + expect(guardian.post_can_act?(post,:vote,taken_actions: {PostActionType.types[:like] => 1})).to be_truthy end it "isn't allowed on archived topics" do topic.archived = true - Guardian.new(user).post_can_act?(post,:like).should be_falsey + expect(Guardian.new(user).post_can_act?(post,:like)).to be_falsey end @@ -627,11 +653,11 @@ describe Guardian do it "isn't allowed if the user voted and the topic doesn't allow multiple votes" do Topic.any_instance.expects(:has_meta_data_boolean?).with(:single_vote).returns(true) - Guardian.new(user).can_vote?(post, voted_in_topic: true).should be_falsey + expect(Guardian.new(user).can_vote?(post, voted_in_topic: true)).to be_falsey end it "is allowed if the user voted and the topic doesn't allow multiple votes" do - Guardian.new(user).can_vote?(post, voted_in_topic: false).should be_truthy + expect(Guardian.new(user).can_vote?(post, voted_in_topic: false)).to be_truthy end end @@ -641,38 +667,38 @@ describe Guardian do describe "can_recover_topic?" do it "returns false for a nil user" do - Guardian.new(nil).can_recover_topic?(topic).should be_falsey + expect(Guardian.new(nil).can_recover_topic?(topic)).to be_falsey end it "returns false for a nil object" do - Guardian.new(user).can_recover_topic?(nil).should be_falsey + expect(Guardian.new(user).can_recover_topic?(nil)).to be_falsey end it "returns false for a regular user" do - Guardian.new(user).can_recover_topic?(topic).should be_falsey + expect(Guardian.new(user).can_recover_topic?(topic)).to be_falsey end it "returns true for a moderator" do - Guardian.new(moderator).can_recover_topic?(topic).should be_truthy + expect(Guardian.new(moderator).can_recover_topic?(topic)).to be_truthy end end describe "can_recover_post?" do it "returns false for a nil user" do - Guardian.new(nil).can_recover_post?(post).should be_falsey + expect(Guardian.new(nil).can_recover_post?(post)).to be_falsey end it "returns false for a nil object" do - Guardian.new(user).can_recover_post?(nil).should be_falsey + expect(Guardian.new(user).can_recover_post?(nil)).to be_falsey end it "returns false for a regular user" do - Guardian.new(user).can_recover_post?(post).should be_falsey + expect(Guardian.new(user).can_recover_post?(post)).to be_falsey end it "returns true for a moderator" do - Guardian.new(moderator).can_recover_post?(post).should be_truthy + expect(Guardian.new(moderator).can_recover_post?(post)).to be_truthy end end @@ -680,90 +706,90 @@ describe Guardian do describe 'can_edit?' do it 'returns false with a nil object' do - Guardian.new(user).can_edit?(nil).should be_falsey + expect(Guardian.new(user).can_edit?(nil)).to be_falsey end describe 'a Post' do it 'returns false when not logged in' do - Guardian.new.can_edit?(post).should be_falsey + expect(Guardian.new.can_edit?(post)).to be_falsey end it 'returns false when not logged in also for wiki post' do post.wiki = true - Guardian.new.can_edit?(post).should be_falsey + expect(Guardian.new.can_edit?(post)).to be_falsey end it 'returns true if you want to edit your own post' do - Guardian.new(post.user).can_edit?(post).should be_truthy + expect(Guardian.new(post.user).can_edit?(post)).to be_truthy end it "returns false if the post is hidden due to flagging and it's too soon" do post.hidden = true post.hidden_at = Time.now - Guardian.new(post.user).can_edit?(post).should be_falsey + expect(Guardian.new(post.user).can_edit?(post)).to be_falsey end it "returns true if the post is hidden due to flagging and it been enough time" do post.hidden = true post.hidden_at = (SiteSetting.cooldown_minutes_after_hiding_posts + 1).minutes.ago - Guardian.new(post.user).can_edit?(post).should be_truthy + expect(Guardian.new(post.user).can_edit?(post)).to be_truthy end it "returns true if the post is hidden, it's been enough time and the edit window has expired" do post.hidden = true post.hidden_at = (SiteSetting.cooldown_minutes_after_hiding_posts + 1).minutes.ago post.created_at = (SiteSetting.post_edit_time_limit + 1).minutes.ago - Guardian.new(post.user).can_edit?(post).should be_truthy + expect(Guardian.new(post.user).can_edit?(post)).to be_truthy end it "returns true if the post is hidden due to flagging and it's got a nil `hidden_at`" do post.hidden = true post.hidden_at = nil - Guardian.new(post.user).can_edit?(post).should be_truthy + expect(Guardian.new(post.user).can_edit?(post)).to be_truthy end it 'returns false if you are trying to edit a post you soft deleted' do post.user_deleted = true - Guardian.new(post.user).can_edit?(post).should be_falsey + expect(Guardian.new(post.user).can_edit?(post)).to be_falsey end it 'returns false if another regular user tries to edit a soft deleted wiki post' do post.wiki = true post.user_deleted = true - Guardian.new(coding_horror).can_edit?(post).should be_falsey + expect(Guardian.new(coding_horror).can_edit?(post)).to be_falsey end it 'returns false if you are trying to edit a deleted post' do post.deleted_at = 1.day.ago - Guardian.new(post.user).can_edit?(post).should be_falsey + expect(Guardian.new(post.user).can_edit?(post)).to be_falsey end it 'returns false if another regular user tries to edit a deleted wiki post' do post.wiki = true post.deleted_at = 1.day.ago - Guardian.new(coding_horror).can_edit?(post).should be_falsey + expect(Guardian.new(coding_horror).can_edit?(post)).to be_falsey end it 'returns false if another regular user tries to edit your post' do - Guardian.new(coding_horror).can_edit?(post).should be_falsey + expect(Guardian.new(coding_horror).can_edit?(post)).to be_falsey end it 'returns true if another regular user tries to edit wiki post' do post.wiki = true - Guardian.new(coding_horror).can_edit?(post).should be_truthy + expect(Guardian.new(coding_horror).can_edit?(post)).to be_truthy end it 'returns true as a moderator' do - Guardian.new(moderator).can_edit?(post).should be_truthy + expect(Guardian.new(moderator).can_edit?(post)).to be_truthy end it 'returns true as an admin' do - Guardian.new(admin).can_edit?(post).should be_truthy + expect(Guardian.new(admin).can_edit?(post)).to be_truthy end it 'returns true as a trust level 4 user' do - Guardian.new(trust_level_4).can_edit?(post).should be_truthy + expect(Guardian.new(trust_level_4).can_edit?(post)).to be_truthy end context 'post is older than post_edit_time_limit' do @@ -773,24 +799,24 @@ describe Guardian do end it 'returns false to the author of the post' do - Guardian.new(old_post.user).can_edit?(old_post).should be_falsey + expect(Guardian.new(old_post.user).can_edit?(old_post)).to be_falsey end it 'returns true as a moderator' do - Guardian.new(moderator).can_edit?(old_post).should eq(true) + expect(Guardian.new(moderator).can_edit?(old_post)).to eq(true) end it 'returns true as an admin' do - Guardian.new(admin).can_edit?(old_post).should eq(true) + expect(Guardian.new(admin).can_edit?(old_post)).to eq(true) end it 'returns false for another regular user trying to edit your post' do - Guardian.new(coding_horror).can_edit?(old_post).should be_falsey + expect(Guardian.new(coding_horror).can_edit?(old_post)).to be_falsey end it 'returns true for another regular user trying to edit a wiki post' do old_post.wiki = true - Guardian.new(coding_horror).can_edit?(old_post).should be_truthy + expect(Guardian.new(coding_horror).can_edit?(old_post)).to be_truthy end it 'returns false when another user has too low trust level to edit wiki post' do @@ -798,7 +824,7 @@ describe Guardian do post.wiki = true coding_horror.trust_level = 1 - Guardian.new(coding_horror).can_edit?(post).should be_falsey + expect(Guardian.new(coding_horror).can_edit?(post)).to be_falsey end it 'returns true when another user has adequate trust level to edit wiki post' do @@ -806,7 +832,7 @@ describe Guardian do post.wiki = true coding_horror.trust_level = 2 - Guardian.new(coding_horror).can_edit?(post).should be_truthy + expect(Guardian.new(coding_horror).can_edit?(post)).to be_truthy end it 'returns true for post author even when he has too low trust level to edit wiki post' do @@ -814,7 +840,7 @@ describe Guardian do post.wiki = true post.user.trust_level = 1 - Guardian.new(post.user).can_edit?(post).should be_truthy + expect(Guardian.new(post.user).can_edit?(post)).to be_truthy end end @@ -824,9 +850,9 @@ describe Guardian do before { SiteSetting.stubs(:tos_topic_id).returns(tos_topic.id) } it "restricts static doc posts" do - Guardian.new(build(:user)).can_edit?(tos_first_post).should be_falsey - Guardian.new(moderator).can_edit?(tos_first_post).should be_falsey - Guardian.new(admin).can_edit?(tos_first_post).should be_truthy + expect(Guardian.new(build(:user)).can_edit?(tos_first_post)).to be_falsey + expect(Guardian.new(moderator).can_edit?(tos_first_post)).to be_falsey + expect(Guardian.new(admin).can_edit?(tos_first_post)).to be_truthy end end end @@ -834,54 +860,54 @@ describe Guardian do describe 'a Topic' do it 'returns false when not logged in' do - Guardian.new.can_edit?(topic).should be_falsey + expect(Guardian.new.can_edit?(topic)).to be_falsey end it 'returns true for editing your own post' do - Guardian.new(topic.user).can_edit?(topic).should eq(true) + expect(Guardian.new(topic.user).can_edit?(topic)).to eq(true) end it 'returns false as a regular user' do - Guardian.new(coding_horror).can_edit?(topic).should be_falsey + expect(Guardian.new(coding_horror).can_edit?(topic)).to be_falsey end context 'not archived' do it 'returns true as a moderator' do - Guardian.new(moderator).can_edit?(topic).should eq(true) + expect(Guardian.new(moderator).can_edit?(topic)).to eq(true) end it 'returns true as an admin' do - Guardian.new(admin).can_edit?(topic).should eq(true) + expect(Guardian.new(admin).can_edit?(topic)).to eq(true) end it 'returns true at trust level 3' do - Guardian.new(trust_level_3).can_edit?(topic).should eq(true) + expect(Guardian.new(trust_level_3).can_edit?(topic)).to eq(true) end end context 'private message' do it 'returns false at trust level 3' do topic.archetype = 'private_message' - Guardian.new(trust_level_3).can_edit?(topic).should eq(false) + expect(Guardian.new(trust_level_3).can_edit?(topic)).to eq(false) end end context 'archived' do it 'returns true as a moderator' do - Guardian.new(moderator).can_edit?(build(:topic, user: user, archived: true)).should be_truthy + expect(Guardian.new(moderator).can_edit?(build(:topic, user: user, archived: true))).to be_truthy end it 'returns true as an admin' do - Guardian.new(admin).can_edit?(build(:topic, user: user, archived: true)).should be_truthy + expect(Guardian.new(admin).can_edit?(build(:topic, user: user, archived: true))).to be_truthy end it 'returns true at trust level 3' do - Guardian.new(trust_level_3).can_edit?(build(:topic, user: user, archived: true)).should be_truthy + expect(Guardian.new(trust_level_3).can_edit?(build(:topic, user: user, archived: true))).to be_truthy end it 'returns false as a topic creator' do - Guardian.new(user).can_edit?(build(:topic, user: user, archived: true)).should be_falsey + expect(Guardian.new(user).can_edit?(build(:topic, user: user, archived: true))).to be_falsey end end end @@ -891,42 +917,42 @@ describe Guardian do let(:category) { Fabricate(:category) } it 'returns false when not logged in' do - Guardian.new.can_edit?(category).should be_falsey + expect(Guardian.new.can_edit?(category)).to be_falsey end it 'returns false as a regular user' do - Guardian.new(category.user).can_edit?(category).should be_falsey + expect(Guardian.new(category.user).can_edit?(category)).to be_falsey end it 'returns false as a moderator' do - Guardian.new(moderator).can_edit?(category).should be_falsey + expect(Guardian.new(moderator).can_edit?(category)).to be_falsey end it 'returns true as an admin' do - Guardian.new(admin).can_edit?(category).should be_truthy + expect(Guardian.new(admin).can_edit?(category)).to be_truthy end end describe 'a User' do it 'returns false when not logged in' do - Guardian.new.can_edit?(user).should be_falsey + expect(Guardian.new.can_edit?(user)).to be_falsey end it 'returns false as a different user' do - Guardian.new(coding_horror).can_edit?(user).should be_falsey + expect(Guardian.new(coding_horror).can_edit?(user)).to be_falsey end it 'returns true when trying to edit yourself' do - Guardian.new(user).can_edit?(user).should be_truthy + expect(Guardian.new(user).can_edit?(user)).to be_truthy end it 'returns true as a moderator' do - Guardian.new(moderator).can_edit?(user).should be_truthy + expect(Guardian.new(moderator).can_edit?(user)).to be_truthy end it 'returns true as an admin' do - Guardian.new(admin).can_edit?(user).should be_truthy + expect(Guardian.new(admin).can_edit?(user)).to be_truthy end end @@ -935,29 +961,29 @@ describe Guardian do context 'can_moderate?' do it 'returns false with a nil object' do - Guardian.new(user).can_moderate?(nil).should be_falsey + expect(Guardian.new(user).can_moderate?(nil)).to be_falsey end context 'a Topic' do it 'returns false when not logged in' do - Guardian.new.can_moderate?(topic).should be_falsey + expect(Guardian.new.can_moderate?(topic)).to be_falsey end it 'returns false when not a moderator' do - Guardian.new(user).can_moderate?(topic).should be_falsey + expect(Guardian.new(user).can_moderate?(topic)).to be_falsey end it 'returns true when a moderator' do - Guardian.new(moderator).can_moderate?(topic).should be_truthy + expect(Guardian.new(moderator).can_moderate?(topic)).to be_truthy end it 'returns true when an admin' do - Guardian.new(admin).can_moderate?(topic).should be_truthy + expect(Guardian.new(admin).can_moderate?(topic)).to be_truthy end it 'returns true when trust level 4' do - Guardian.new(trust_level_4).can_moderate?(topic).should be_truthy + expect(Guardian.new(trust_level_4).can_moderate?(topic)).to be_truthy end end @@ -967,48 +993,48 @@ describe Guardian do context 'can_see_flags?' do it "returns false when there is no post" do - Guardian.new(moderator).can_see_flags?(nil).should be_falsey + expect(Guardian.new(moderator).can_see_flags?(nil)).to be_falsey end it "returns false when there is no user" do - Guardian.new(nil).can_see_flags?(post).should be_falsey + expect(Guardian.new(nil).can_see_flags?(post)).to be_falsey end it "allow regular users to see flags" do - Guardian.new(user).can_see_flags?(post).should be_falsey + expect(Guardian.new(user).can_see_flags?(post)).to be_falsey end it "allows moderators to see flags" do - Guardian.new(moderator).can_see_flags?(post).should be_truthy + expect(Guardian.new(moderator).can_see_flags?(post)).to be_truthy end it "allows moderators to see flags" do - Guardian.new(admin).can_see_flags?(post).should be_truthy + expect(Guardian.new(admin).can_see_flags?(post)).to be_truthy end end context 'can_move_posts?' do it 'returns false with a nil object' do - Guardian.new(user).can_move_posts?(nil).should be_falsey + expect(Guardian.new(user).can_move_posts?(nil)).to be_falsey end context 'a Topic' do it 'returns false when not logged in' do - Guardian.new.can_move_posts?(topic).should be_falsey + expect(Guardian.new.can_move_posts?(topic)).to be_falsey end it 'returns false when not a moderator' do - Guardian.new(user).can_move_posts?(topic).should be_falsey + expect(Guardian.new(user).can_move_posts?(topic)).to be_falsey end it 'returns true when a moderator' do - Guardian.new(moderator).can_move_posts?(topic).should be_truthy + expect(Guardian.new(moderator).can_move_posts?(topic)).to be_truthy end it 'returns true when an admin' do - Guardian.new(admin).can_move_posts?(topic).should be_truthy + expect(Guardian.new(admin).can_move_posts?(topic)).to be_truthy end end @@ -1018,7 +1044,7 @@ describe Guardian do context 'can_delete?' do it 'returns false with a nil object' do - Guardian.new(user).can_delete?(nil).should be_falsey + expect(Guardian.new(user).can_delete?(nil)).to be_falsey end context 'a Topic' do @@ -1028,25 +1054,25 @@ describe Guardian do end it 'returns false when not logged in' do - Guardian.new.can_delete?(topic).should be_falsey + expect(Guardian.new.can_delete?(topic)).to be_falsey end it 'returns false when not a moderator' do - Guardian.new(user).can_delete?(topic).should be_falsey + expect(Guardian.new(user).can_delete?(topic)).to be_falsey end it 'returns true when a moderator' do - Guardian.new(moderator).can_delete?(topic).should be_truthy + expect(Guardian.new(moderator).can_delete?(topic)).to be_truthy end it 'returns true when an admin' do - Guardian.new(admin).can_delete?(topic).should be_truthy + expect(Guardian.new(admin).can_delete?(topic)).to be_truthy end it 'returns false for static doc topics' do tos_topic = Fabricate(:topic, user: Discourse.system_user) SiteSetting.stubs(:tos_topic_id).returns(tos_topic.id) - Guardian.new(admin).can_delete?(tos_topic).should be_falsey + expect(Guardian.new(admin).can_delete?(tos_topic)).to be_falsey end end @@ -1057,35 +1083,35 @@ describe Guardian do end it 'returns false when not logged in' do - Guardian.new.can_delete?(post).should be_falsey + expect(Guardian.new.can_delete?(post)).to be_falsey end it "returns false when trying to delete your own post that has already been deleted" do post = Fabricate(:post) PostDestroyer.new(user, post).destroy post.reload - Guardian.new(user).can_delete?(post).should be_falsey + expect(Guardian.new(user).can_delete?(post)).to be_falsey end it 'returns true when trying to delete your own post' do - Guardian.new(user).can_delete?(post).should be_truthy + expect(Guardian.new(user).can_delete?(post)).to be_truthy end it "returns false when trying to delete another user's own post" do - Guardian.new(Fabricate(:user)).can_delete?(post).should be_falsey + expect(Guardian.new(Fabricate(:user)).can_delete?(post)).to be_falsey end it "returns false when it's the OP, even as a moderator" do post.update_attribute :post_number, 1 - Guardian.new(moderator).can_delete?(post).should be_falsey + expect(Guardian.new(moderator).can_delete?(post)).to be_falsey end it 'returns true when a moderator' do - Guardian.new(moderator).can_delete?(post).should be_truthy + expect(Guardian.new(moderator).can_delete?(post)).to be_truthy end it 'returns true when an admin' do - Guardian.new(admin).can_delete?(post).should be_truthy + expect(Guardian.new(admin).can_delete?(post)).to be_truthy end it 'returns false when post is first in a static doc topic' do @@ -1093,7 +1119,7 @@ describe Guardian do SiteSetting.stubs(:tos_topic_id).returns(tos_topic.id) post.update_attribute :post_number, 1 post.update_attribute :topic_id, tos_topic.id - Guardian.new(admin).can_delete?(post).should be_falsey + expect(Guardian.new(admin).can_delete?(post)).to be_falsey end context 'post is older than post_edit_time_limit' do @@ -1103,24 +1129,24 @@ describe Guardian do end it 'returns false to the author of the post' do - Guardian.new(old_post.user).can_delete?(old_post).should eq(false) + expect(Guardian.new(old_post.user).can_delete?(old_post)).to eq(false) end it 'returns true as a moderator' do - Guardian.new(moderator).can_delete?(old_post).should eq(true) + expect(Guardian.new(moderator).can_delete?(old_post)).to eq(true) end it 'returns true as an admin' do - Guardian.new(admin).can_delete?(old_post).should eq(true) + expect(Guardian.new(admin).can_delete?(old_post)).to eq(true) end it "returns false when it's the OP, even as a moderator" do old_post.post_number = 1 - Guardian.new(moderator).can_delete?(old_post).should eq(false) + expect(Guardian.new(moderator).can_delete?(old_post)).to eq(false) end it 'returns false for another regular user trying to delete your post' do - Guardian.new(coding_horror).can_delete?(old_post).should eq(false) + expect(Guardian.new(coding_horror).can_delete?(old_post)).to eq(false) end end @@ -1130,11 +1156,11 @@ describe Guardian do end it "allows a staff member to delete it" do - Guardian.new(moderator).can_delete?(post).should be_truthy + expect(Guardian.new(moderator).can_delete?(post)).to be_truthy end it "doesn't allow a regular user to delete it" do - Guardian.new(post.user).can_delete?(post).should be_falsey + expect(Guardian.new(post.user).can_delete?(post)).to be_falsey end end @@ -1146,54 +1172,54 @@ describe Guardian do let(:category) { build(:category, user: moderator) } it 'returns false when not logged in' do - Guardian.new.can_delete?(category).should be_falsey + expect(Guardian.new.can_delete?(category)).to be_falsey end it 'returns false when a regular user' do - Guardian.new(user).can_delete?(category).should be_falsey + expect(Guardian.new(user).can_delete?(category)).to be_falsey end it 'returns false when a moderator' do - Guardian.new(moderator).can_delete?(category).should be_falsey + expect(Guardian.new(moderator).can_delete?(category)).to be_falsey end it 'returns true when an admin' do - Guardian.new(admin).can_delete?(category).should be_truthy + expect(Guardian.new(admin).can_delete?(category)).to be_truthy end it "can't be deleted if it has a forum topic" do category.topic_count = 10 - Guardian.new(moderator).can_delete?(category).should be_falsey + expect(Guardian.new(moderator).can_delete?(category)).to be_falsey end it "can't be deleted if it is the Uncategorized Category" do uncategorized_cat_id = SiteSetting.uncategorized_category_id uncategorized_category = Category.find(uncategorized_cat_id) - Guardian.new(admin).can_delete?(uncategorized_category).should be_falsey + expect(Guardian.new(admin).can_delete?(uncategorized_category)).to be_falsey end it "can't be deleted if it has children" do category.expects(:has_children?).returns(true) - Guardian.new(admin).can_delete?(category).should be_falsey + expect(Guardian.new(admin).can_delete?(category)).to be_falsey end end context 'can_suspend?' do it 'returns false when a user tries to suspend another user' do - Guardian.new(user).can_suspend?(coding_horror).should be_falsey + expect(Guardian.new(user).can_suspend?(coding_horror)).to be_falsey end it 'returns true when an admin tries to suspend another user' do - Guardian.new(admin).can_suspend?(coding_horror).should be_truthy + expect(Guardian.new(admin).can_suspend?(coding_horror)).to be_truthy end it 'returns true when a moderator tries to suspend another user' do - Guardian.new(moderator).can_suspend?(coding_horror).should be_truthy + expect(Guardian.new(moderator).can_suspend?(coding_horror)).to be_truthy end it 'returns false when staff tries to suspend staff' do - Guardian.new(admin).can_suspend?(moderator).should be_falsey + expect(Guardian.new(admin).can_suspend?(moderator)).to be_falsey end end @@ -1208,21 +1234,21 @@ describe Guardian do } it 'returns false when not logged in' do - Guardian.new.can_delete?(post_action).should be_falsey + expect(Guardian.new.can_delete?(post_action)).to be_falsey end it 'returns false when not the user who created it' do - Guardian.new(coding_horror).can_delete?(post_action).should be_falsey + expect(Guardian.new(coding_horror).can_delete?(post_action)).to be_falsey end it "returns false if the window has expired" do post_action.created_at = 20.minutes.ago SiteSetting.expects(:post_undo_action_window_mins).returns(10) - Guardian.new(user).can_delete?(post_action).should be_falsey + expect(Guardian.new(user).can_delete?(post_action)).to be_falsey end it "returns true if it's yours" do - Guardian.new(user).can_delete?(post_action).should be_truthy + expect(Guardian.new(user).can_delete?(post_action)).to be_truthy end end @@ -1232,24 +1258,24 @@ describe Guardian do context 'can_approve?' do it "wont allow a non-logged in user to approve" do - Guardian.new.can_approve?(user).should be_falsey + expect(Guardian.new.can_approve?(user)).to be_falsey end it "wont allow a non-admin to approve a user" do - Guardian.new(coding_horror).can_approve?(user).should be_falsey + expect(Guardian.new(coding_horror).can_approve?(user)).to be_falsey end it "returns false when the user is already approved" do user.approved = true - Guardian.new(admin).can_approve?(user).should be_falsey + expect(Guardian.new(admin).can_approve?(user)).to be_falsey end it "allows an admin to approve a user" do - Guardian.new(admin).can_approve?(user).should be_truthy + expect(Guardian.new(admin).can_approve?(user)).to be_truthy end it "allows a moderator to approve a user" do - Guardian.new(moderator).can_approve?(user).should be_truthy + expect(Guardian.new(moderator).can_approve?(user)).to be_truthy end @@ -1257,107 +1283,107 @@ describe Guardian do context 'can_grant_admin?' do it "wont allow a non logged in user to grant an admin's access" do - Guardian.new.can_grant_admin?(another_admin).should be_falsey + expect(Guardian.new.can_grant_admin?(another_admin)).to be_falsey end it "wont allow a regular user to revoke an admin's access" do - Guardian.new(user).can_grant_admin?(another_admin).should be_falsey + expect(Guardian.new(user).can_grant_admin?(another_admin)).to be_falsey end it 'wont allow an admin to grant their own access' do - Guardian.new(admin).can_grant_admin?(admin).should be_falsey + expect(Guardian.new(admin).can_grant_admin?(admin)).to be_falsey end it "allows an admin to grant a regular user access" do admin.id = 1 user.id = 2 - Guardian.new(admin).can_grant_admin?(user).should be_truthy + expect(Guardian.new(admin).can_grant_admin?(user)).to be_truthy end end context 'can_revoke_admin?' do it "wont allow a non logged in user to revoke an admin's access" do - Guardian.new.can_revoke_admin?(another_admin).should be_falsey + expect(Guardian.new.can_revoke_admin?(another_admin)).to be_falsey end it "wont allow a regular user to revoke an admin's access" do - Guardian.new(user).can_revoke_admin?(another_admin).should be_falsey + expect(Guardian.new(user).can_revoke_admin?(another_admin)).to be_falsey end it 'wont allow an admin to revoke their own access' do - Guardian.new(admin).can_revoke_admin?(admin).should be_falsey + expect(Guardian.new(admin).can_revoke_admin?(admin)).to be_falsey end it "allows an admin to revoke another admin's access" do admin.id = 1 another_admin.id = 2 - Guardian.new(admin).can_revoke_admin?(another_admin).should be_truthy + expect(Guardian.new(admin).can_revoke_admin?(another_admin)).to be_truthy end end context 'can_grant_moderation?' do it "wont allow a non logged in user to grant an moderator's access" do - Guardian.new.can_grant_moderation?(user).should be_falsey + expect(Guardian.new.can_grant_moderation?(user)).to be_falsey end it "wont allow a regular user to revoke an moderator's access" do - Guardian.new(user).can_grant_moderation?(moderator).should be_falsey + expect(Guardian.new(user).can_grant_moderation?(moderator)).to be_falsey end it 'will allow an admin to grant their own moderator access' do - Guardian.new(admin).can_grant_moderation?(admin).should be_truthy + expect(Guardian.new(admin).can_grant_moderation?(admin)).to be_truthy end it 'wont allow an admin to grant it to an already moderator' do - Guardian.new(admin).can_grant_moderation?(moderator).should be_falsey + expect(Guardian.new(admin).can_grant_moderation?(moderator)).to be_falsey end it "allows an admin to grant a regular user access" do - Guardian.new(admin).can_grant_moderation?(user).should be_truthy + expect(Guardian.new(admin).can_grant_moderation?(user)).to be_truthy end end context 'can_revoke_moderation?' do it "wont allow a non logged in user to revoke an moderator's access" do - Guardian.new.can_revoke_moderation?(moderator).should be_falsey + expect(Guardian.new.can_revoke_moderation?(moderator)).to be_falsey end it "wont allow a regular user to revoke an moderator's access" do - Guardian.new(user).can_revoke_moderation?(moderator).should be_falsey + expect(Guardian.new(user).can_revoke_moderation?(moderator)).to be_falsey end it 'wont allow a moderator to revoke their own moderator' do - Guardian.new(moderator).can_revoke_moderation?(moderator).should be_falsey + expect(Guardian.new(moderator).can_revoke_moderation?(moderator)).to be_falsey end it "allows an admin to revoke a moderator's access" do - Guardian.new(admin).can_revoke_moderation?(moderator).should be_truthy + expect(Guardian.new(admin).can_revoke_moderation?(moderator)).to be_truthy end it "allows an admin to revoke a moderator's access from self" do admin.moderator = true - Guardian.new(admin).can_revoke_moderation?(admin).should be_truthy + expect(Guardian.new(admin).can_revoke_moderation?(admin)).to be_truthy end it "does not allow revoke from non moderators" do - Guardian.new(admin).can_revoke_moderation?(admin).should be_falsey + expect(Guardian.new(admin).can_revoke_moderation?(admin)).to be_falsey end end context "can_see_invite_details?" do it 'is false without a logged in user' do - Guardian.new(nil).can_see_invite_details?(user).should be_falsey + expect(Guardian.new(nil).can_see_invite_details?(user)).to be_falsey end it 'is false without a user to look at' do - Guardian.new(user).can_see_invite_details?(nil).should be_falsey + expect(Guardian.new(user).can_see_invite_details?(nil)).to be_falsey end it 'is true when looking at your own invites' do - Guardian.new(user).can_see_invite_details?(user).should be_truthy + expect(Guardian.new(user).can_see_invite_details?(user)).to be_truthy end end @@ -1371,11 +1397,11 @@ describe Guardian do end it "returns true for a nil user" do - Guardian.new(nil).can_access_forum?.should be_truthy + expect(Guardian.new(nil).can_access_forum?).to be_truthy end it "returns true for an unapproved user" do - Guardian.new(unapproved_user).can_access_forum?.should be_truthy + expect(Guardian.new(unapproved_user).can_access_forum?).to be_truthy end end @@ -1385,21 +1411,21 @@ describe Guardian do end it "returns false for a nil user" do - Guardian.new(nil).can_access_forum?.should be_falsey + expect(Guardian.new(nil).can_access_forum?).to be_falsey end it "returns false for an unapproved user" do - Guardian.new(unapproved_user).can_access_forum?.should be_falsey + expect(Guardian.new(unapproved_user).can_access_forum?).to be_falsey end it "returns true for an admin user" do unapproved_user.admin = true - Guardian.new(unapproved_user).can_access_forum?.should be_truthy + expect(Guardian.new(unapproved_user).can_access_forum?).to be_truthy end it "returns true for an approved user" do unapproved_user.approved = true - Guardian.new(unapproved_user).can_access_forum?.should be_truthy + expect(Guardian.new(unapproved_user).can_access_forum?).to be_truthy end end @@ -1408,15 +1434,15 @@ describe Guardian do describe "can_delete_user?" do it "is false without a logged in user" do - Guardian.new(nil).can_delete_user?(user).should be_falsey + expect(Guardian.new(nil).can_delete_user?(user)).to be_falsey end it "is false without a user to look at" do - Guardian.new(admin).can_delete_user?(nil).should be_falsey + expect(Guardian.new(admin).can_delete_user?(nil)).to be_falsey end it "is false for regular users" do - Guardian.new(user).can_delete_user?(coding_horror).should be_falsey + expect(Guardian.new(user).can_delete_user?(coding_horror)).to be_falsey end context "delete myself" do @@ -1424,41 +1450,41 @@ describe Guardian do subject { Guardian.new(myself).can_delete_user?(myself) } it "is true to delete myself and I have never made a post" do - subject.should be_truthy + expect(subject).to be_truthy end it "is true to delete myself and I have only made 1 post" do myself.stubs(:post_count).returns(1) - subject.should be_truthy + expect(subject).to be_truthy end it "is false to delete myself and I have made 2 posts" do myself.stubs(:post_count).returns(2) - subject.should be_falsey + expect(subject).to be_falsey end end shared_examples "can_delete_user examples" do it "is true if user is not an admin and has never posted" do - Guardian.new(actor).can_delete_user?(Fabricate.build(:user, created_at: 100.days.ago)).should be_truthy + expect(Guardian.new(actor).can_delete_user?(Fabricate.build(:user, created_at: 100.days.ago))).to be_truthy end it "is true if user is not an admin and first post is not too old" do user = Fabricate.build(:user, created_at: 100.days.ago) user.stubs(:first_post_created_at).returns(9.days.ago) SiteSetting.stubs(:delete_user_max_post_age).returns(10) - Guardian.new(actor).can_delete_user?(user).should be_truthy + expect(Guardian.new(actor).can_delete_user?(user)).to be_truthy end it "is false if user is an admin" do - Guardian.new(actor).can_delete_user?(another_admin).should be_falsey + expect(Guardian.new(actor).can_delete_user?(another_admin)).to be_falsey end it "is false if user's first post is too old" do user = Fabricate.build(:user, created_at: 100.days.ago) user.stubs(:first_post_created_at).returns(11.days.ago) SiteSetting.stubs(:delete_user_max_post_age).returns(10) - Guardian.new(actor).can_delete_user?(user).should be_falsey + expect(Guardian.new(actor).can_delete_user?(user)).to be_falsey end end @@ -1475,53 +1501,53 @@ describe Guardian do describe "can_delete_all_posts?" do it "is false without a logged in user" do - Guardian.new(nil).can_delete_all_posts?(user).should be_falsey + expect(Guardian.new(nil).can_delete_all_posts?(user)).to be_falsey end it "is false without a user to look at" do - Guardian.new(admin).can_delete_all_posts?(nil).should be_falsey + expect(Guardian.new(admin).can_delete_all_posts?(nil)).to be_falsey end it "is false for regular users" do - Guardian.new(user).can_delete_all_posts?(coding_horror).should be_falsey + expect(Guardian.new(user).can_delete_all_posts?(coding_horror)).to be_falsey end shared_examples "can_delete_all_posts examples" do it "is true if user has no posts" do SiteSetting.stubs(:delete_user_max_post_age).returns(10) - Guardian.new(actor).can_delete_all_posts?(Fabricate(:user, created_at: 100.days.ago)).should be_truthy + expect(Guardian.new(actor).can_delete_all_posts?(Fabricate(:user, created_at: 100.days.ago))).to be_truthy end it "is true if user's first post is newer than delete_user_max_post_age days old" do user = Fabricate(:user, created_at: 100.days.ago) user.stubs(:first_post_created_at).returns(9.days.ago) SiteSetting.stubs(:delete_user_max_post_age).returns(10) - Guardian.new(actor).can_delete_all_posts?(user).should be_truthy + expect(Guardian.new(actor).can_delete_all_posts?(user)).to be_truthy end it "is false if user's first post is older than delete_user_max_post_age days old" do user = Fabricate(:user, created_at: 100.days.ago) user.stubs(:first_post_created_at).returns(11.days.ago) SiteSetting.stubs(:delete_user_max_post_age).returns(10) - Guardian.new(actor).can_delete_all_posts?(user).should be_falsey + expect(Guardian.new(actor).can_delete_all_posts?(user)).to be_falsey end it "is false if user is an admin" do - Guardian.new(actor).can_delete_all_posts?(admin).should be_falsey + expect(Guardian.new(actor).can_delete_all_posts?(admin)).to be_falsey end it "is true if number of posts is small" do u = Fabricate(:user, created_at: 1.day.ago) u.stubs(:post_count).returns(1) SiteSetting.stubs(:delete_all_posts_max).returns(10) - Guardian.new(actor).can_delete_all_posts?(u).should be_truthy + expect(Guardian.new(actor).can_delete_all_posts?(u)).to be_truthy end it "is false if number of posts is not small" do u = Fabricate(:user, created_at: 1.day.ago) u.stubs(:post_count).returns(11) SiteSetting.stubs(:delete_all_posts_max).returns(10) - Guardian.new(actor).can_delete_all_posts?(u).should be_falsey + expect(Guardian.new(actor).can_delete_all_posts?(u)).to be_falsey end end @@ -1538,23 +1564,23 @@ describe Guardian do describe 'can_grant_title?' do it 'is false without a logged in user' do - Guardian.new(nil).can_grant_title?(user).should be_falsey + expect(Guardian.new(nil).can_grant_title?(user)).to be_falsey end it 'is false for regular users' do - Guardian.new(user).can_grant_title?(user).should be_falsey + expect(Guardian.new(user).can_grant_title?(user)).to be_falsey end it 'is true for moderators' do - Guardian.new(moderator).can_grant_title?(user).should be_truthy + expect(Guardian.new(moderator).can_grant_title?(user)).to be_truthy end it 'is true for admins' do - Guardian.new(admin).can_grant_title?(user).should be_truthy + expect(Guardian.new(admin).can_grant_title?(user)).to be_truthy end it 'is false without a user to look at' do - Guardian.new(admin).can_grant_title?(nil).should be_falsey + expect(Guardian.new(admin).can_grant_title?(nil)).to be_falsey end end @@ -1562,38 +1588,38 @@ describe Guardian do describe 'can_change_trust_level?' do it 'is false without a logged in user' do - Guardian.new(nil).can_change_trust_level?(user).should be_falsey + expect(Guardian.new(nil).can_change_trust_level?(user)).to be_falsey end it 'is false for regular users' do - Guardian.new(user).can_change_trust_level?(user).should be_falsey + expect(Guardian.new(user).can_change_trust_level?(user)).to be_falsey end it 'is true for moderators' do - Guardian.new(moderator).can_change_trust_level?(user).should be_truthy + expect(Guardian.new(moderator).can_change_trust_level?(user)).to be_truthy end it 'is true for admins' do - Guardian.new(admin).can_change_trust_level?(user).should be_truthy + expect(Guardian.new(admin).can_change_trust_level?(user)).to be_truthy end end describe "can_edit_username?" do it "is false without a logged in user" do - Guardian.new(nil).can_edit_username?(build(:user, created_at: 1.minute.ago)).should be_falsey + expect(Guardian.new(nil).can_edit_username?(build(:user, created_at: 1.minute.ago))).to be_falsey end it "is false for regular users to edit another user's username" do - Guardian.new(build(:user)).can_edit_username?(build(:user, created_at: 1.minute.ago)).should be_falsey + expect(Guardian.new(build(:user)).can_edit_username?(build(:user, created_at: 1.minute.ago))).to be_falsey end shared_examples "staff can always change usernames" do it "is true for moderators" do - Guardian.new(moderator).can_edit_username?(user).should be_truthy + expect(Guardian.new(moderator).can_edit_username?(user)).to be_truthy end it "is true for admins" do - Guardian.new(admin).can_edit_username?(user).should be_truthy + expect(Guardian.new(admin).can_edit_username?(user)).to be_truthy end end @@ -1602,7 +1628,7 @@ describe Guardian do include_examples "staff can always change usernames" it "is true for the user to change their own username" do - Guardian.new(target_user).can_edit_username?(target_user).should be_truthy + expect(Guardian.new(target_user).can_edit_username?(target_user)).to be_truthy end end @@ -1616,7 +1642,7 @@ describe Guardian do context 'with no posts' do include_examples "staff can always change usernames" it "is true for the user to change their own username" do - Guardian.new(target_user).can_edit_username?(target_user).should be_truthy + expect(Guardian.new(target_user).can_edit_username?(target_user)).to be_truthy end end @@ -1624,7 +1650,7 @@ describe Guardian do before { target_user.stubs(:post_count).returns(1) } include_examples "staff can always change usernames" it "is false for the user to change their own username" do - Guardian.new(target_user).can_edit_username?(target_user).should be_falsey + expect(Guardian.new(target_user).can_edit_username?(target_user)).to be_falsey end end end @@ -1637,7 +1663,7 @@ describe Guardian do include_examples "staff can always change usernames" it "is false for the user to change their own username" do - Guardian.new(user).can_edit_username?(user).should be_falsey + expect(Guardian.new(user).can_edit_username?(user)).to be_falsey end end @@ -1648,15 +1674,15 @@ describe Guardian do end it "is false for admins" do - Guardian.new(admin).can_edit_username?(admin).should be_falsey + expect(Guardian.new(admin).can_edit_username?(admin)).to be_falsey end it "is false for moderators" do - Guardian.new(moderator).can_edit_username?(moderator).should be_falsey + expect(Guardian.new(moderator).can_edit_username?(moderator)).to be_falsey end it "is false for users" do - Guardian.new(user).can_edit_username?(user).should be_falsey + expect(Guardian.new(user).can_edit_username?(user)).to be_falsey end end end @@ -1668,23 +1694,23 @@ describe Guardian do end it "is false when not logged in" do - Guardian.new(nil).can_edit_email?(build(:user, created_at: 1.minute.ago)).should be_falsey + expect(Guardian.new(nil).can_edit_email?(build(:user, created_at: 1.minute.ago))).to be_falsey end it "is false for regular users to edit another user's email" do - Guardian.new(build(:user)).can_edit_email?(build(:user, created_at: 1.minute.ago)).should be_falsey + expect(Guardian.new(build(:user)).can_edit_email?(build(:user, created_at: 1.minute.ago))).to be_falsey end it "is true for a regular user to edit their own email" do - Guardian.new(user).can_edit_email?(user).should be_truthy + expect(Guardian.new(user).can_edit_email?(user)).to be_truthy end it "is true for moderators" do - Guardian.new(moderator).can_edit_email?(user).should be_truthy + expect(Guardian.new(moderator).can_edit_email?(user)).to be_truthy end it "is true for admins" do - Guardian.new(admin).can_edit_email?(user).should be_truthy + expect(Guardian.new(admin).can_edit_email?(user)).to be_truthy end end @@ -1694,23 +1720,23 @@ describe Guardian do end it "is false when not logged in" do - Guardian.new(nil).can_edit_email?(build(:user, created_at: 1.minute.ago)).should be_falsey + expect(Guardian.new(nil).can_edit_email?(build(:user, created_at: 1.minute.ago))).to be_falsey end it "is false for regular users to edit another user's email" do - Guardian.new(build(:user)).can_edit_email?(build(:user, created_at: 1.minute.ago)).should be_falsey + expect(Guardian.new(build(:user)).can_edit_email?(build(:user, created_at: 1.minute.ago))).to be_falsey end it "is false for a regular user to edit their own email" do - Guardian.new(user).can_edit_email?(user).should be_falsey + expect(Guardian.new(user).can_edit_email?(user)).to be_falsey end it "is false for admins" do - Guardian.new(admin).can_edit_email?(user).should be_falsey + expect(Guardian.new(admin).can_edit_email?(user)).to be_falsey end it "is false for moderators" do - Guardian.new(moderator).can_edit_email?(user).should be_falsey + expect(Guardian.new(moderator).can_edit_email?(user)).to be_falsey end end @@ -1721,41 +1747,41 @@ describe Guardian do end it "is false for admins" do - Guardian.new(admin).can_edit_email?(admin).should be_falsey + expect(Guardian.new(admin).can_edit_email?(admin)).to be_falsey end it "is false for moderators" do - Guardian.new(moderator).can_edit_email?(moderator).should be_falsey + expect(Guardian.new(moderator).can_edit_email?(moderator)).to be_falsey end it "is false for users" do - Guardian.new(user).can_edit_email?(user).should be_falsey + expect(Guardian.new(user).can_edit_email?(user)).to be_falsey end end end describe 'can_edit_name?' do it 'is false without a logged in user' do - Guardian.new(nil).can_edit_name?(build(:user, created_at: 1.minute.ago)).should be_falsey + expect(Guardian.new(nil).can_edit_name?(build(:user, created_at: 1.minute.ago))).to be_falsey end it "is false for regular users to edit another user's name" do - Guardian.new(build(:user)).can_edit_name?(build(:user, created_at: 1.minute.ago)).should be_falsey + expect(Guardian.new(build(:user)).can_edit_name?(build(:user, created_at: 1.minute.ago))).to be_falsey end context 'for a new user' do let(:target_user) { build(:user, created_at: 1.minute.ago) } it 'is true for the user to change their own name' do - Guardian.new(target_user).can_edit_name?(target_user).should be_truthy + expect(Guardian.new(target_user).can_edit_name?(target_user)).to be_truthy end it 'is true for moderators' do - Guardian.new(moderator).can_edit_name?(user).should be_truthy + expect(Guardian.new(moderator).can_edit_name?(user)).to be_truthy end it 'is true for admins' do - Guardian.new(admin).can_edit_name?(user).should be_truthy + expect(Guardian.new(admin).can_edit_name?(user)).to be_truthy end end @@ -1765,15 +1791,15 @@ describe Guardian do end it 'is false for the user to change their own name' do - Guardian.new(user).can_edit_name?(user).should be_falsey + expect(Guardian.new(user).can_edit_name?(user)).to be_falsey end it 'is false for moderators' do - Guardian.new(moderator).can_edit_name?(user).should be_falsey + expect(Guardian.new(moderator).can_edit_name?(user)).to be_falsey end it 'is false for admins' do - Guardian.new(admin).can_edit_name?(user).should be_falsey + expect(Guardian.new(admin).can_edit_name?(user)).to be_falsey end end @@ -1789,15 +1815,15 @@ describe Guardian do end it 'is true for admins' do - Guardian.new(admin).can_edit_name?(admin).should be_truthy + expect(Guardian.new(admin).can_edit_name?(admin)).to be_truthy end it 'is true for moderators' do - Guardian.new(moderator).can_edit_name?(moderator).should be_truthy + expect(Guardian.new(moderator).can_edit_name?(moderator)).to be_truthy end it 'is true for users' do - Guardian.new(user).can_edit_name?(user).should be_truthy + expect(Guardian.new(user).can_edit_name?(user)).to be_truthy end end @@ -1812,15 +1838,15 @@ describe Guardian do end it 'is false for admins' do - Guardian.new(admin).can_edit_name?(admin).should be_falsey + expect(Guardian.new(admin).can_edit_name?(admin)).to be_falsey end it 'is false for moderators' do - Guardian.new(moderator).can_edit_name?(moderator).should be_falsey + expect(Guardian.new(moderator).can_edit_name?(moderator)).to be_falsey end it 'is false for users' do - Guardian.new(user).can_edit_name?(user).should be_falsey + expect(Guardian.new(user).can_edit_name?(user)).to be_falsey end end @@ -1830,15 +1856,15 @@ describe Guardian do end it 'is true for admins' do - Guardian.new(admin).can_edit_name?(admin).should be_truthy + expect(Guardian.new(admin).can_edit_name?(admin)).to be_truthy end it 'is true for moderators' do - Guardian.new(moderator).can_edit_name?(moderator).should be_truthy + expect(Guardian.new(moderator).can_edit_name?(moderator)).to be_truthy end it 'is true for users' do - Guardian.new(user).can_edit_name?(user).should be_truthy + expect(Guardian.new(user).can_edit_name?(user)).to be_truthy end end end @@ -1847,15 +1873,15 @@ describe Guardian do describe 'can_wiki?' do it 'returns false for regular user' do - Guardian.new(coding_horror).can_wiki?.should be_falsey + expect(Guardian.new(coding_horror).can_wiki?).to be_falsey end it 'returns true for admin user' do - Guardian.new(admin).can_wiki?.should be_truthy + expect(Guardian.new(admin).can_wiki?).to be_truthy end it 'returns true for trust_level_4 user' do - Guardian.new(trust_level_4).can_wiki?.should be_truthy + expect(Guardian.new(trust_level_4).can_wiki?).to be_truthy end end end diff --git a/spec/components/image_sizer_spec.rb b/spec/components/image_sizer_spec.rb index 53f9f902b9..e8e705862f 100644 --- a/spec/components/image_sizer_spec.rb +++ b/spec/components/image_sizer_spec.rb @@ -9,19 +9,19 @@ describe ImageSizer do end it 'returns the same dimensions when smaller than the maximums' do - ImageSizer.resize(400, 200).should == [400, 200] + expect(ImageSizer.resize(400, 200)).to eq([400, 200]) end it 'returns nil if the width is nil' do - ImageSizer.resize(nil, 100).should == nil + expect(ImageSizer.resize(nil, 100)).to eq(nil) end it 'returns nil if the height is nil' do - ImageSizer.resize(100, nil).should == nil + expect(ImageSizer.resize(100, nil)).to eq(nil) end it 'works with string parameters' do - ImageSizer.resize('100', '101').should == [100, 101] + expect(ImageSizer.resize('100', '101')).to eq([100, 101]) end describe 'when larger than the maximum width' do @@ -31,11 +31,11 @@ describe ImageSizer do end it 'returns the maxmimum width if larger than the maximum' do - @w.should == 500 + expect(@w).to eq(500) end it 'resizes the height retaining the aspect ratio' do - @h.should == 102 + expect(@h).to eq(102) end end @@ -47,11 +47,11 @@ describe ImageSizer do end it 'returns the maxmimum height if larger than the maximum' do - @h.should == 500 + expect(@h).to eq(500) end it 'resizes the width retaining the aspect ratio' do - @w.should == 102 + expect(@w).to eq(102) end end @@ -63,8 +63,8 @@ describe ImageSizer do end it 'resizes both dimensions retaining the aspect ratio' do - @h.should == 500 - @w.should == 333 + expect(@h).to eq(500) + expect(@w).to eq(333) end end diff --git a/spec/components/import/normalize_spec.rb b/spec/components/import/normalize_spec.rb index 209e2d76b7..8175801e11 100644 --- a/spec/components/import/normalize_spec.rb +++ b/spec/components/import/normalize_spec.rb @@ -14,7 +14,7 @@ describe Import::Normalize do
this is a ""
MD expected = "  \n \n```\n I am a te \"\n \n```\n\n test  \n \n```\nthis is a \"\"\n```\n\n" - Import::Normalize.normalize_code_blocks(markdown).should == expected + expect(Import::Normalize.normalize_code_blocks(markdown)).to eq(expected) end end end diff --git a/spec/components/js_locale_helper_spec.rb b/spec/components/js_locale_helper_spec.rb index 85206f44de..a6da4a3a63 100644 --- a/spec/components/js_locale_helper_spec.rb +++ b/spec/components/js_locale_helper_spec.rb @@ -3,7 +3,7 @@ require_dependency 'js_locale_helper' describe JsLocaleHelper do it 'should be able to generate translations' do - JsLocaleHelper.output_locale('en').length.should > 0 + expect(JsLocaleHelper.output_locale('en').length).to be > 0 end def setup_message_format(format) @@ -23,8 +23,8 @@ describe JsLocaleHelper do one {1 result} other {# results} }') - localize(NUM_RESULTS: 1).should == '1 result' - localize(NUM_RESULTS: 2).should == '2 results' + expect(localize(NUM_RESULTS: 1)).to eq('1 result') + expect(localize(NUM_RESULTS: 2)).to eq('2 results') end it 'handles double plurals' do @@ -37,21 +37,21 @@ describe JsLocaleHelper do }') - localize(NUM_RESULTS: 1, NUM_APPLES: 2).should == '1 result and 2 apples' - localize(NUM_RESULTS: 2, NUM_APPLES: 1).should == '2 results and 1 apple' + expect(localize(NUM_RESULTS: 1, NUM_APPLES: 2)).to eq('1 result and 2 apples') + expect(localize(NUM_RESULTS: 2, NUM_APPLES: 1)).to eq('2 results and 1 apple') end it 'handles select' do setup_message_format('{GENDER, select, male {He} female {She} other {They}} read a book') - localize(GENDER: 'male').should == 'He read a book' - localize(GENDER: 'female').should == 'She read a book' - localize(GENDER: 'none').should == 'They read a book' + expect(localize(GENDER: 'male')).to eq('He read a book') + expect(localize(GENDER: 'female')).to eq('She read a book') + expect(localize(GENDER: 'none')).to eq('They read a book') end it 'can strip out message formats' do hash = {"a" => "b", "c" => { "d" => {"f_MF" => "bob"} }} - JsLocaleHelper.strip_out_message_formats!(hash).should == {"c.d.f_MF" => "bob"} - hash["c"]["d"].should == {} + expect(JsLocaleHelper.strip_out_message_formats!(hash)).to eq({"c.d.f_MF" => "bob"}) + expect(hash["c"]["d"]).to eq({}) end it 'handles message format special keys' do @@ -70,18 +70,18 @@ describe JsLocaleHelper do } })) - ctx.eval('I18n.translations')["en"]["js"]["hello"].should == "world" - ctx.eval('I18n.translations')["en"]["js"]["test_MF"].should == nil + expect(ctx.eval('I18n.translations')["en"]["js"]["hello"]).to eq("world") + expect(ctx.eval('I18n.translations')["en"]["js"]["test_MF"]).to eq(nil) - ctx.eval('I18n.messageFormat("test_MF", { HELLO: "hi", COUNT: 3 })').should == "hi 3 ducks" - ctx.eval('I18n.messageFormat("error_MF", { HELLO: "hi", COUNT: 3 })').should =~ /Invalid Format/ - ctx.eval('I18n.messageFormat("missing", {})').should =~ /missing/ - ctx.eval('I18n.messageFormat("simple_MF", {})').should =~ /COUNT/ # error + expect(ctx.eval('I18n.messageFormat("test_MF", { HELLO: "hi", COUNT: 3 })')).to eq("hi 3 ducks") + expect(ctx.eval('I18n.messageFormat("error_MF", { HELLO: "hi", COUNT: 3 })')).to match(/Invalid Format/) + expect(ctx.eval('I18n.messageFormat("missing", {})')).to match(/missing/) + expect(ctx.eval('I18n.messageFormat("simple_MF", {})')).to match(/COUNT/) # error end it 'load pluralizations rules before precompile' do message = JsLocaleHelper.compile_message_format('ru', 'format') - message.should_not match 'Plural Function not found' + expect(message).not_to match 'Plural Function not found' end LocaleSiteSetting.values.each do |locale| diff --git a/spec/components/json_error_spec.rb b/spec/components/json_error_spec.rb index a95d284e3d..cd0c469310 100644 --- a/spec/components/json_error_spec.rb +++ b/spec/components/json_error_spec.rb @@ -5,11 +5,11 @@ shared_examples "a generic error" do let(:result) { creator.create_errors_json(obj) } it "should have a result object" do - result.should be_present + expect(result).to be_present end it "has a generic error message" do - result[:errors].should == [I18n.t('js.generic_error')] + expect(result[:errors]).to eq([I18n.t('js.generic_error')]) end end @@ -37,17 +37,17 @@ describe JsonError do describe "with a string" do it "returns the string in the error format" do - creator.create_errors_json("test error").should == {errors: ["test error"]} + expect(creator.create_errors_json("test error")).to eq({errors: ["test error"]}) end end describe "an activerecord objec with errors" do let(:invalid_user) { User.new } it "returns the errors correctly" do - invalid_user.should_not be_valid + expect(invalid_user).not_to be_valid result = creator.create_errors_json(invalid_user) - result.should be_present - result[:errors].should_not be_blank + expect(result).to be_present + expect(result[:errors]).not_to be_blank end end diff --git a/spec/components/middleware/anonymous_cache_spec.rb b/spec/components/middleware/anonymous_cache_spec.rb index 5555debea1..b562f344b7 100644 --- a/spec/components/middleware/anonymous_cache_spec.rb +++ b/spec/components/middleware/anonymous_cache_spec.rb @@ -3,25 +3,31 @@ require_dependency "middleware/anonymous_cache" describe Middleware::AnonymousCache::Helper do - def new_helper(env={}) - Middleware::AnonymousCache::Helper.new({ + def env(opts={}) + { "HTTP_HOST" => "http://test.com", "REQUEST_URI" => "/path?bla=1", - "REQUEST_METHOD" => "GET" - }.merge(env)) + "REQUEST_METHOD" => "GET", + "rack.input" => "" + }.merge(opts) end + def new_helper(opts={}) + Middleware::AnonymousCache::Helper.new(env(opts)) + end + + context "cachable?" do it "true by default" do - new_helper.cacheable?.should == true + expect(new_helper.cacheable?).to eq(true) end it "is false for non GET" do - new_helper("ANON_CACHE_DURATION" => 10, "REQUEST_METHOD" => "POST").cacheable?.should == false + expect(new_helper("ANON_CACHE_DURATION" => 10, "REQUEST_METHOD" => "POST").cacheable?).to eq(false) end it "is false if it has an auth cookie" do - new_helper("HTTP_COOKIE" => "jack=1; _t=#{"1"*32}; jill=2").cacheable?.should == false + expect(new_helper("HTTP_COOKIE" => "jack=1; _t=#{"1"*32}; jill=2").cacheable?).to eq(false) end end @@ -41,16 +47,16 @@ describe Middleware::AnonymousCache::Helper do it "returns cached data for cached requests" do helper.is_mobile = true - helper.cached.should == nil + expect(helper.cached).to eq(nil) helper.cache([200, {"HELLO" => "WORLD"}, ["hello ", "my world"]]) helper = new_helper("ANON_CACHE_DURATION" => 10) helper.is_mobile = true - helper.cached.should == [200, {"HELLO" => "WORLD"}, ["hello my world"]] + expect(helper.cached).to eq([200, {"HELLO" => "WORLD"}, ["hello my world"]]) - crawler.cached.should == nil + expect(crawler.cached).to eq(nil) crawler.cache([200, {"HELLO" => "WORLD"}, ["hello ", "world"]]) - crawler.cached.should == [200, {"HELLO" => "WORLD"}, ["hello world"]] + expect(crawler.cached).to eq([200, {"HELLO" => "WORLD"}, ["hello world"]]) end end diff --git a/spec/components/middleware/request_tracker_spec.rb b/spec/components/middleware/request_tracker_spec.rb new file mode 100644 index 0000000000..c8064be052 --- /dev/null +++ b/spec/components/middleware/request_tracker_spec.rb @@ -0,0 +1,42 @@ +require "spec_helper" +require_dependency "middleware/request_tracker" + +describe Middleware::RequestTracker do + + def env(opts={}) + { + "HTTP_HOST" => "http://test.com", + "REQUEST_URI" => "/path?bla=1", + "REQUEST_METHOD" => "GET", + "rack.input" => "" + }.merge(opts) + end + + context "log_request" do + it "can log requests correctly" do + freeze_time Time.now + + ApplicationRequest.clear_cache! + + data = Middleware::RequestTracker.get_data(env( + "HTTP_USER_AGENT" => "AdsBot-Google (+http://www.google.com/adsbot.html)" + ), ["200",{"Content-Type" => 'text/html'}]) + + Middleware::RequestTracker.log_request(data) + + data = Middleware::RequestTracker.get_data(env( + "HTTP_DISCOURSE_TRACK_VIEW" => "1" + ), ["200",{}]) + + Middleware::RequestTracker.log_request(data) + + ApplicationRequest.write_cache! + + ApplicationRequest.http_total.first.count.should == 2 + ApplicationRequest.http_2xx.first.count.should == 2 + + ApplicationRequest.page_view_anon.first.count.should == 1 + ApplicationRequest.page_view_crawler.first.count.should == 1 + end + end +end diff --git a/spec/components/onebox/engine/discourse_local_onebox_spec.rb b/spec/components/onebox/engine/discourse_local_onebox_spec.rb index 30bd1e8459..78c7bcb096 100644 --- a/spec/components/onebox/engine/discourse_local_onebox_spec.rb +++ b/spec/components/onebox/engine/discourse_local_onebox_spec.rb @@ -3,14 +3,14 @@ require 'spec_helper' describe Onebox::Engine::DiscourseLocalOnebox do it "matches for a topic url" do url = "#{Discourse.base_url}/t/hot-topic" - Onebox.has_matcher?(url).should == true - Onebox::Matcher.new(url).oneboxed.should == described_class + expect(Onebox.has_matcher?(url)).to eq(true) + expect(Onebox::Matcher.new(url).oneboxed).to eq(described_class) end it "matches for a post url" do url = "#{Discourse.base_url}/t/hot-topic/23/2" - Onebox.has_matcher?(url).should == true - Onebox::Matcher.new(url).oneboxed.should == described_class + expect(Onebox.has_matcher?(url)).to eq(true) + expect(Onebox::Matcher.new(url).oneboxed).to eq(described_class) end context "for a link to a post" do @@ -19,27 +19,27 @@ describe Onebox::Engine::DiscourseLocalOnebox do it "returns a link if post isn't found" do url = "#{Discourse.base_url}/t/not-exist/3/2" - Onebox.preview(url).to_s.should == "#{url}" + expect(Onebox.preview(url).to_s).to eq("#{url}") end it "returns a link if not allowed to see the post" do url = "#{Discourse.base_url}#{post2.url}" Guardian.any_instance.stubs(:can_see?).returns(false) - Onebox.preview(url).to_s.should == "#{url}" + expect(Onebox.preview(url).to_s).to eq("#{url}") end it "returns a link if post is hidden" do hidden_post = Fabricate(:post, topic: post.topic, post_number: 2, hidden: true, hidden_reason_id: Post.hidden_reasons[:flag_threshold_reached]) url = "#{Discourse.base_url}#{hidden_post.url}" - Onebox.preview(url).to_s.should == "#{url}" + expect(Onebox.preview(url).to_s).to eq("#{url}") end it "returns some onebox goodness if post exists and can be seen" do url = "#{Discourse.base_url}#{post2.url}" Guardian.any_instance.stubs(:can_see?).returns(true) html = Onebox.preview(url).to_s - html.should include(post2.user.username) - html.should include(post2.excerpt) + expect(html).to include(post2.user.username) + expect(html).to include(post2.excerpt) end end @@ -51,21 +51,21 @@ describe Onebox::Engine::DiscourseLocalOnebox do it "returns a link if topic isn't found" do url = "#{Discourse.base_url}/t/not-found/123" - Onebox.preview(url).to_s.should == "#{url}" + expect(Onebox.preview(url).to_s).to eq("#{url}") end it "returns a link if not allowed to see the post" do url = "#{topic.url}" Guardian.any_instance.stubs(:can_see?).returns(false) - Onebox.preview(url).to_s.should == "#{url}" + expect(Onebox.preview(url).to_s).to eq("#{url}") end it "returns some onebox goodness if post exists and can be seen" do url = "#{topic.url}" Guardian.any_instance.stubs(:can_see?).returns(true) html = Onebox.preview(url).to_s - html.should include(topic.posts.first.user.username) - html.should include("topic-info") + expect(html).to include(topic.posts.first.user.username) + expect(html).to include("topic-info") end end end diff --git a/spec/components/oneboxer_spec.rb b/spec/components/oneboxer_spec.rb index 41e492be04..96f1524a39 100644 --- a/spec/components/oneboxer_spec.rb +++ b/spec/components/oneboxer_spec.rb @@ -3,8 +3,8 @@ require_dependency 'oneboxer' describe Oneboxer do it "returns blank string for an invalid onebox" do - Oneboxer.preview("http://boom.com").should == "" - Oneboxer.onebox("http://boom.com").should == "" + expect(Oneboxer.preview("http://boom.com")).to eq("") + expect(Oneboxer.onebox("http://boom.com")).to eq("") end end diff --git a/spec/components/onpdiff_spec.rb b/spec/components/onpdiff_spec.rb index 2ca73cc688..09c6ff41dc 100644 --- a/spec/components/onpdiff_spec.rb +++ b/spec/components/onpdiff_spec.rb @@ -6,11 +6,11 @@ describe ONPDiff do describe "diff" do it "returns an empty array when there is no content to diff" do - ONPDiff.new("", "").diff.should == [] + expect(ONPDiff.new("", "").diff).to eq([]) end it "returns an array with the operation code for each element" do - ONPDiff.new("abcd", "abef").diff.should == [["a", :common], ["b", :common], ["e", :add], ["f", :add], ["c", :delete], ["d", :delete]] + expect(ONPDiff.new("abcd", "abef").diff).to eq([["a", :common], ["b", :common], ["e", :add], ["f", :add], ["c", :delete], ["d", :delete]]) end end @@ -18,15 +18,15 @@ describe ONPDiff do describe "short_diff" do it "returns an empty array when there is no content to diff" do - ONPDiff.new("", "").short_diff.should == [] + expect(ONPDiff.new("", "").short_diff).to eq([]) end it "returns an array with the operation code for each element" do - ONPDiff.new("abc", "acd").short_diff.should == [["a", :common], ["b", :delete], ["c", :common], ["d", :add]] + expect(ONPDiff.new("abc", "acd").short_diff).to eq([["a", :common], ["b", :delete], ["c", :common], ["d", :add]]) end it "returns an array with sequencially similar operations merged" do - ONPDiff.new("abcd", "abef").short_diff.should == [["ab", :common], ["ef", :add], ["cd", :delete]] + expect(ONPDiff.new("abcd", "abef").short_diff).to eq([["ab", :common], ["ef", :add], ["cd", :delete]]) end end diff --git a/spec/components/pbkdf2_spec.rb b/spec/components/pbkdf2_spec.rb index 05d03c77f7..1805cd14f6 100644 --- a/spec/components/pbkdf2_spec.rb +++ b/spec/components/pbkdf2_spec.rb @@ -3,7 +3,7 @@ require 'pbkdf2' describe Pbkdf2 do # trivial test to ensure this does not regress during extraction it "hashes stuff correctly" do - Pbkdf2.hash_password('test', 'abcd', 100).should == "0313a6aca54dd4c5d82a699a8a0f0ffb0191b4ef62414b8d9dbc11c0c5ac04da" - Pbkdf2.hash_password('test', 'abcd', 101).should == "c7a7b2891bf8e6f82d08cf8d83824edcf6c7c6bacb6a741f38e21fc7977bd20f" + expect(Pbkdf2.hash_password('test', 'abcd', 100)).to eq("0313a6aca54dd4c5d82a699a8a0f0ffb0191b4ef62414b8d9dbc11c0c5ac04da") + expect(Pbkdf2.hash_password('test', 'abcd', 101)).to eq("c7a7b2891bf8e6f82d08cf8d83824edcf6c7c6bacb6a741f38e21fc7977bd20f") end end diff --git a/spec/components/pinned_check_spec.rb b/spec/components/pinned_check_spec.rb index b108006ad4..009de0e7ca 100644 --- a/spec/components/pinned_check_spec.rb +++ b/spec/components/pinned_check_spec.rb @@ -10,11 +10,11 @@ describe PinnedCheck do context "without a topic_user record (either anonymous or never been in the topic)" do it "returns false if the topic is not pinned" do - PinnedCheck.pinned?(unpinned_topic).should == false + expect(PinnedCheck.pinned?(unpinned_topic)).to eq(false) end it "returns true if the topic is pinned" do - PinnedCheck.pinned?(unpinned_topic).should == false + expect(PinnedCheck.pinned?(unpinned_topic)).to eq(false) end end @@ -27,7 +27,7 @@ describe PinnedCheck do let(:topic_user) { TopicUser.new(topic: unpinned_topic, user: user) } it "returns false" do - PinnedCheck.pinned?(unpinned_topic, topic_user).should == false + expect(PinnedCheck.pinned?(unpinned_topic, topic_user)).to eq(false) end end @@ -36,17 +36,17 @@ describe PinnedCheck do let(:topic_user) { TopicUser.new(topic: pinned_topic, user: user) } it "is pinned if the topic_user's cleared_pinned_at is blank" do - PinnedCheck.pinned?(pinned_topic, topic_user).should == true + expect(PinnedCheck.pinned?(pinned_topic, topic_user)).to eq(true) end it "is not pinned if the topic_user's cleared_pinned_at is later than when it was pinned_at" do topic_user.cleared_pinned_at = (pinned_at + 1.hour) - PinnedCheck.pinned?(pinned_topic, topic_user).should == false + expect(PinnedCheck.pinned?(pinned_topic, topic_user)).to eq(false) end it "is pinned if the topic_user's cleared_pinned_at is earlier than when it was pinned_at" do topic_user.cleared_pinned_at = (pinned_at - 3.hours) - PinnedCheck.pinned?(pinned_topic, topic_user).should == true + expect(PinnedCheck.pinned?(pinned_topic, topic_user)).to eq(true) end end diff --git a/spec/components/plugin/filter_manager_spec.rb b/spec/components/plugin/filter_manager_spec.rb index ac0c9610c3..1ff4ee9640 100644 --- a/spec/components/plugin/filter_manager_spec.rb +++ b/spec/components/plugin/filter_manager_spec.rb @@ -13,23 +13,23 @@ describe Plugin::FilterManager do context + result + 2 end - instance.apply(:added_numbers, 1, 0).should == 5 + expect(instance.apply(:added_numbers, 1, 0)).to eq(5) end it "should raise an exception if wrong arity is passed in" do - lambda do + expect do instance.register(:test) do end - end.should raise_exception + end.to raise_exception end it "should return the original if no filters exist" do - instance.apply(:foo, nil, 42).should == 42 + expect(instance.apply(:foo, nil, 42)).to eq(42) end it "should raise an exception if no block is passed in" do - lambda do + expect do instance.register(:test) - end.should raise_exception + end.to raise_exception end end diff --git a/spec/components/plugin/instance_spec.rb b/spec/components/plugin/instance_spec.rb index a971139060..fc87a425ed 100644 --- a/spec/components/plugin/instance_spec.rb +++ b/spec/components/plugin/instance_spec.rb @@ -4,108 +4,96 @@ require_dependency 'plugin/instance' describe Plugin::Instance do after do - DiscoursePluginRegistry.javascripts.clear - DiscoursePluginRegistry.admin_javascripts.clear - DiscoursePluginRegistry.server_side_javascripts.clear - DiscoursePluginRegistry.stylesheets.clear - DiscoursePluginRegistry.mobile_stylesheets.clear - DiscoursePluginRegistry.desktop_stylesheets.clear - DiscoursePluginRegistry.sass_variables.clear - DiscoursePluginRegistry.serialized_current_user_fields + DiscoursePluginRegistry.reset! end context "find_all" do it "can find plugins correctly" do plugins = Plugin::Instance.find_all("#{Rails.root}/spec/fixtures/plugins") - plugins.count.should == 1 + expect(plugins.count).to eq(1) plugin = plugins[0] - plugin.name.should == "plugin-name" - plugin.path.should == "#{Rails.root}/spec/fixtures/plugins/my_plugin/plugin.rb" + expect(plugin.name).to eq("plugin-name") + expect(plugin.path).to eq("#{Rails.root}/spec/fixtures/plugins/my_plugin/plugin.rb") end it "does not blow up on missing directory" do plugins = Plugin::Instance.find_all("#{Rails.root}/frank_zappa") - plugins.count.should == 0 + expect(plugins.count).to eq(0) + end + end + + context "enabling/disabling" do + + it "is enabled by default" do + expect(Plugin::Instance.new.enabled?).to eq(true) + end + + context "with a plugin that extends things" do + + class Trout; end + class TroutSerializer < ApplicationSerializer; end + + class TroutPlugin < Plugin::Instance + attr_accessor :enabled + def enabled?; @enabled; end + end + + before do + @plugin = TroutPlugin.new + @trout = Trout.new + + # New method + @plugin.add_to_class(:trout, :status?) { "evil" } + + # DiscourseEvent + @hello_count = 0 + @plugin.on(:hello) { @hello_count += 1 } + + # Serializer + @plugin.add_to_serializer(:trout, :scales) { 1024 } + @serializer = TroutSerializer.new(@trout) + end + + after do + DiscourseEvent.clear + end + + it "checks enabled/disabled functionality for extensions" do + + # with an enabled plugin + @plugin.enabled = true + expect(@trout.status?).to eq("evil") + DiscourseEvent.trigger(:hello) + expect(@hello_count).to eq(1) + expect(@serializer.scales).to eq(1024) + expect(@serializer.include_scales?).to eq(true) + + # When a plugin is disabled + @plugin.enabled = false + expect(@trout.status?).to eq(nil) + DiscourseEvent.trigger(:hello) + expect(@hello_count).to eq(1) + expect(@serializer.scales).to eq(1024) + expect(@serializer.include_scales?).to eq(false) + + end end end context "register asset" do - it "does register general css properly" do + it "populates the DiscoursePluginRegistry" do plugin = Plugin::Instance.new nil, "/tmp/test.rb" plugin.register_asset("test.css") plugin.register_asset("test2.css") plugin.send :register_assets! - DiscoursePluginRegistry.mobile_stylesheets.count.should == 0 - DiscoursePluginRegistry.stylesheets.count.should == 2 + expect(DiscoursePluginRegistry.mobile_stylesheets.count).to eq(0) + expect(DiscoursePluginRegistry.stylesheets.count).to eq(2) end - - it "registers desktop css properly" do - plugin = Plugin::Instance.new nil, "/tmp/test.rb" - plugin.register_asset("test.css", :desktop) - plugin.send :register_assets! - - DiscoursePluginRegistry.mobile_stylesheets.count.should == 0 - DiscoursePluginRegistry.desktop_stylesheets.count.should == 1 - DiscoursePluginRegistry.stylesheets.count.should == 0 - end - - it "registers mobile css properly" do - plugin = Plugin::Instance.new nil, "/tmp/test.rb" - plugin.register_asset("test.css", :mobile) - plugin.send :register_assets! - - DiscoursePluginRegistry.mobile_stylesheets.count.should == 1 - DiscoursePluginRegistry.stylesheets.count.should == 0 - end - - it "registers desktop css properly" do - plugin = Plugin::Instance.new nil, "/tmp/test.rb" - plugin.register_asset("test.css", :desktop) - plugin.send :register_assets! - - DiscoursePluginRegistry.desktop_stylesheets.count.should == 1 - DiscoursePluginRegistry.stylesheets.count.should == 0 - end - - - it "registers sass variable properly" do - plugin = Plugin::Instance.new nil, "/tmp/test.rb" - plugin.register_asset("test.css", :variables) - plugin.send :register_assets! - - DiscoursePluginRegistry.sass_variables.count.should == 1 - DiscoursePluginRegistry.stylesheets.count.should == 0 - end - - - it "registers admin javascript properly" do - plugin = Plugin::Instance.new nil, "/tmp/test.rb" - plugin.register_asset("my_admin.js", :admin) - - plugin.send :register_assets! - - DiscoursePluginRegistry.admin_javascripts.count.should == 1 - DiscoursePluginRegistry.javascripts.count.should == 0 - DiscoursePluginRegistry.server_side_javascripts.count.should == 0 - end - - it "registers server side javascript properly" do - plugin = Plugin::Instance.new nil, "/tmp/test.rb" - plugin.register_asset("my_admin.js", :server_side) - - plugin.send :register_assets! - - DiscoursePluginRegistry.server_side_javascripts.count.should == 1 - DiscoursePluginRegistry.javascripts.count.should == 1 - DiscoursePluginRegistry.admin_javascripts.count.should == 0 - end - end - context "activate!" do it "can activate plugins correctly" do plugin = Plugin::Instance.new @@ -116,18 +104,18 @@ describe Plugin::Instance do File.open("#{plugin.auto_generated_path}/junk", "w") {|f| f.write("junk")} plugin.activate! - plugin.auth_providers.count.should == 1 + expect(plugin.auth_providers.count).to eq(1) auth_provider = plugin.auth_providers[0] - auth_provider.authenticator.name.should == 'ubuntu' + expect(auth_provider.authenticator.name).to eq('ubuntu') # calls ensure_assets! make sure they are there - plugin.assets.count.should == 1 + expect(plugin.assets.count).to eq(1) plugin.assets.each do |a, opts| - File.exists?(a).should == true + expect(File.exists?(a)).to eq(true) end # ensure it cleans up all crap in autogenerated directory - File.exists?(junk_file).should == false + expect(File.exists?(junk_file)).to eq(false) end it "finds all the custom assets" do @@ -152,13 +140,13 @@ describe Plugin::Instance do plugin.activate! - DiscoursePluginRegistry.javascripts.count.should == 3 - DiscoursePluginRegistry.admin_javascripts.count.should == 2 - DiscoursePluginRegistry.server_side_javascripts.count.should == 1 - DiscoursePluginRegistry.desktop_stylesheets.count.should == 2 - DiscoursePluginRegistry.sass_variables.count.should == 2 - DiscoursePluginRegistry.stylesheets.count.should == 2 - DiscoursePluginRegistry.mobile_stylesheets.count.should == 1 + expect(DiscoursePluginRegistry.javascripts.count).to eq(3) + expect(DiscoursePluginRegistry.admin_javascripts.count).to eq(2) + expect(DiscoursePluginRegistry.server_side_javascripts.count).to eq(1) + expect(DiscoursePluginRegistry.desktop_stylesheets.count).to eq(2) + expect(DiscoursePluginRegistry.sass_variables.count).to eq(2) + expect(DiscoursePluginRegistry.stylesheets.count).to eq(2) + expect(DiscoursePluginRegistry.mobile_stylesheets.count).to eq(1) end end @@ -170,7 +158,7 @@ describe Plugin::Instance do user.save! payload = JSON.parse(CurrentUserSerializer.new(user, scope: Guardian.new(user)).to_json) - payload["current_user"]["custom_fields"]["has_car"].should == "true" + expect(payload["current_user"]["custom_fields"]["has_car"]).to eq("true") end end @@ -181,7 +169,7 @@ describe Plugin::Instance do plugin.register_color_scheme("Purple", {primary: 'EEE0E5'}) plugin.notify_after_initialize }.to change { ColorScheme.count }.by(1) - ColorScheme.where(name: "Purple").should be_present + expect(ColorScheme.where(name: "Purple")).to be_present end it "doesn't add the same color scheme twice" do diff --git a/spec/components/plugin/metadata_spec.rb b/spec/components/plugin/metadata_spec.rb index 9004cf5752..7e922e2a4c 100644 --- a/spec/components/plugin/metadata_spec.rb +++ b/spec/components/plugin/metadata_spec.rb @@ -9,14 +9,16 @@ describe Plugin::Metadata do # about: about: my plugin # version: 0.1 # authors: Frank Zappa +# url: http://discourse.org some_ruby TEXT - metadata.name.should == "plugin-name" - metadata.about.should == "about: my plugin" - metadata.version.should == "0.1" - metadata.authors.should == "Frank Zappa" + expect(metadata.name).to eq("plugin-name") + expect(metadata.about).to eq("about: my plugin") + expect(metadata.version).to eq("0.1") + expect(metadata.authors).to eq("Frank Zappa") + expect(metadata.url).to eq("http://discourse.org") end end diff --git a/spec/components/post_creator_spec.rb b/spec/components/post_creator_spec.rb index 1cdcda9dd6..ef5d4f0c54 100644 --- a/spec/components/post_creator_spec.rb +++ b/spec/components/post_creator_spec.rb @@ -24,12 +24,12 @@ describe PostCreator do it "can be created with auto tracking disabled" do p = PostCreator.create(user, basic_topic_params.merge(auto_track: false)) # must be 0 otherwise it will think we read the topic which is clearly untrue - TopicUser.where(user_id: p.user_id, topic_id: p.topic_id).count.should == 0 + expect(TopicUser.where(user_id: p.user_id, topic_id: p.topic_id).count).to eq(0) end it "ensures the user can create the topic" do Guardian.any_instance.expects(:can_create?).with(Topic,nil).returns(false) - lambda { creator.create }.should raise_error(Discourse::InvalidAccess) + expect { creator.create }.to raise_error(Discourse::InvalidAccess) end @@ -59,7 +59,7 @@ describe PostCreator do it "doesn't return true for spam" do creator.create - creator.spam?.should == false + expect(creator.spam?).to eq(false) end it "does not notify on system messages" do @@ -70,8 +70,8 @@ describe PostCreator do end # don't notify on system messages they introduce too much noise channels = messages.map(&:channel) - channels.find{|s| s =~ /unread/}.should == nil - channels.find{|s| s =~ /new/}.should == nil + expect(channels.find{|s| s =~ /unread/}).to eq(nil) + expect(channels.find{|s| s =~ /new/}).to eq(nil) end it "generates the correct messages for a secure topic" do @@ -92,7 +92,7 @@ describe PostCreator do # 2 for topic, one to notify of new topic another for tracking state - messages.map{|m| m.channel}.sort.should == [ "/new", + expect(messages.map{|m| m.channel}.sort).to eq([ "/new", "/users/#{admin.username}", "/users/#{admin.username}", "/unread/#{admin.id}", @@ -101,10 +101,10 @@ describe PostCreator do "/latest", "/topic/#{created_post.topic_id}", "/topic/#{created_post.topic_id}" - ].sort + ].sort) admin_ids = [Group[:admins].id] - messages.any?{|m| m.group_ids != admin_ids && m.user_ids != [admin.id]}.should == false + expect(messages.any?{|m| m.group_ids != admin_ids && m.user_ids != [admin.id]}).to eq(false) end it 'generates the correct messages for a normal topic' do @@ -115,18 +115,18 @@ describe PostCreator do end latest = messages.find{|m| m.channel == "/latest"} - latest.should_not == nil + expect(latest).not_to eq(nil) latest = messages.find{|m| m.channel == "/new"} - latest.should_not == nil + expect(latest).not_to eq(nil) read = messages.find{|m| m.channel == "/unread/#{p.user_id}"} - read.should_not == nil + expect(read).not_to eq(nil) user_action = messages.find{|m| m.channel == "/users/#{p.user.username}"} - user_action.should_not == nil + expect(user_action).not_to eq(nil) - messages.length.should == 5 + expect(messages.length).to eq(5) end it 'extracts links from the post' do @@ -158,11 +158,11 @@ describe PostCreator do end it 'assigns a category when supplied' do - creator_with_category.create.topic.category.should == category + expect(creator_with_category.create.topic.category).to eq(category) end it 'adds meta data from the post' do - creator_with_meta_data.create.topic.meta_data['hello'].should == 'world' + expect(creator_with_meta_data.create.topic.meta_data['hello']).to eq('world') end it 'passes the image sizes through' do @@ -175,27 +175,27 @@ describe PostCreator do # ensure topic user is correct topic_user = first_post.user.topic_users.find_by(topic_id: first_post.topic_id) - topic_user.should be_present - topic_user.should be_posted - topic_user.last_read_post_number.should == first_post.post_number - topic_user.highest_seen_post_number.should == first_post.post_number + expect(topic_user).to be_present + expect(topic_user).to be_posted + expect(topic_user.last_read_post_number).to eq(first_post.post_number) + expect(topic_user.highest_seen_post_number).to eq(first_post.post_number) user2 = Fabricate(:coding_horror) - user2.user_stat.topic_reply_count.should == 0 + expect(user2.user_stat.topic_reply_count).to eq(0) - first_post.user.user_stat.reload.topic_reply_count.should == 0 + expect(first_post.user.user_stat.reload.topic_reply_count).to eq(0) PostCreator.new(user2, topic_id: first_post.topic_id, raw: "this is my test post 123").create - first_post.user.user_stat.reload.topic_reply_count.should == 0 + expect(first_post.user.user_stat.reload.topic_reply_count).to eq(0) - user2.user_stat.reload.topic_reply_count.should == 1 + expect(user2.user_stat.reload.topic_reply_count).to eq(1) end it 'sets topic excerpt if first post, but not second post' do first_post = creator.create topic = first_post.topic.reload - topic.excerpt.should be_present + expect(topic.excerpt).to be_present expect { PostCreator.new(first_post.user, topic_id: first_post.topic_id, raw: "this is the second post").create topic.reload @@ -211,7 +211,7 @@ describe PostCreator do PostCreator.new(topic.user, topic_id: topic.id, raw: "this is a second post").create topic.reload - topic.auto_close_at.should be_within(1.second).of(auto_close_time) + expect(topic.auto_close_at).to be_within(1.second).of(auto_close_time) end it "updates topic's auto close date when it's based on last post" do @@ -221,7 +221,7 @@ describe PostCreator do PostCreator.new(topic.user, topic_id: topic.id, raw: "this is a second post").create topic.reload - topic.auto_close_at.should_not be_within(1.second).of(auto_close_time) + expect(topic.auto_close_at).not_to be_within(1.second).of(auto_close_time) end end @@ -232,7 +232,7 @@ describe PostCreator do it 'ensures the user can auto-close the topic, but ignores auto-close param silently' do Guardian.any_instance.stubs(:can_moderate?).returns(false) post = PostCreator.new(user, basic_topic_params.merge(auto_close_time: 2)).create - post.topic.auto_close_at.should == nil + expect(post.topic.auto_close_at).to eq(nil) end end end @@ -251,7 +251,7 @@ describe PostCreator do it "returns true for another post with the same content" do new_creator = PostCreator.new(user, basic_topic_params) - new_creator.create.should be_present + expect(new_creator.create).to be_present end end @@ -271,28 +271,28 @@ describe PostCreator do response_1 = create_post(raw: dupe, user: first.user, topic_id: first.topic_id) response_2 = create_post(raw: dupe, user: first.user, topic_id: second.topic_id) - response_1.errors.count.should == 0 - response_2.errors.count.should == 1 + expect(response_1.errors.count).to eq(0) + expect(response_2.errors.count).to eq(1) end it "returns blank for another post with the same content" do creator.create new_post_creator.create - new_post_creator.errors.should be_present + expect(new_post_creator.errors).to be_present end it "returns a post for admins" do creator.create user.admin = true new_post_creator.create - new_post_creator.errors.should be_blank + expect(new_post_creator.errors).to be_blank end it "returns a post for moderators" do creator.create user.moderator = true new_post_creator.create - new_post_creator.errors.should be_blank + expect(new_post_creator.errors).to be_blank end end @@ -312,8 +312,8 @@ describe PostCreator do it "does not create the post" do GroupMessage.stubs(:create) creator.create - creator.errors.should be_present - creator.spam?.should == true + expect(creator.errors).to be_present + expect(creator.spam?).to eq(true) end it "sends a message to moderators" do @@ -332,15 +332,15 @@ describe PostCreator do it 'ensures the user can create the post' do Guardian.any_instance.expects(:can_create?).with(Post, topic).returns(false) - lambda { creator.create }.should raise_error(Discourse::InvalidAccess) + expect { creator.create }.to raise_error(Discourse::InvalidAccess) end context 'success' do it 'create correctly' do post = creator.create - Post.count.should == 1 - Topic.count.should == 1 - post.reply_to_post_number.should == 4 + expect(Post.count).to eq(1) + expect(Topic.count).to eq(1) + expect(post.reply_to_post_number).to eq(4) end end @@ -376,18 +376,18 @@ describe PostCreator do it 'acts correctly' do # It's not a warning - post.topic.warning.should be_blank + expect(post.topic.warning).to be_blank - post.topic.archetype.should == Archetype.private_message - post.topic.subtype.should == TopicSubtype.user_to_user - post.topic.topic_allowed_users.count.should == 3 + expect(post.topic.archetype).to eq(Archetype.private_message) + expect(post.topic.subtype).to eq(TopicSubtype.user_to_user) + expect(post.topic.topic_allowed_users.count).to eq(3) # PMs can't have a category - post.topic.category.should == nil + expect(post.topic.category).to eq(nil) # does not notify an unrelated user - unrelated.notifications.count.should == 0 - post.topic.subtype.should == TopicSubtype.user_to_user + expect(unrelated.notifications.count).to eq(0) + expect(post.topic.subtype).to eq(TopicSubtype.user_to_user) # if an admin replies they should be added to the allowed user list admin = Fabricate(:admin) @@ -395,7 +395,7 @@ describe PostCreator do topic_id: post.topic_id) post.topic.reload - post.topic.topic_allowed_users.where(user_id: admin.id).count.should == 1 + expect(post.topic.topic_allowed_users.where(user_id: admin.id).count).to eq(1) end end @@ -414,26 +414,26 @@ describe PostCreator do # Invalid archetype creator = PostCreator.new(user, base_args) creator.create - creator.errors.should be_present + expect(creator.errors).to be_present # Too many users creator = PostCreator.new(user, base_args.merge(archetype: Archetype.private_message, target_usernames: [target_user1.username, target_user2.username].join(','))) creator.create - creator.errors.should be_present + expect(creator.errors).to be_present # Success creator = PostCreator.new(user, base_args.merge(archetype: Archetype.private_message)) post = creator.create - creator.errors.should be_blank + expect(creator.errors).to be_blank topic = post.topic - topic.should be_present - topic.warning.should be_present - topic.subtype.should == TopicSubtype.moderator_warning - topic.warning.user.should == target_user1 - topic.warning.created_by.should == user - target_user1.warnings.count.should == 1 + expect(topic).to be_present + expect(topic.warning).to be_present + expect(topic.subtype).to eq(TopicSubtype.moderator_warning) + expect(topic.warning.user).to eq(target_user1) + expect(topic.warning.created_by).to eq(user) + expect(target_user1.warnings.count).to eq(1) end end @@ -456,15 +456,15 @@ describe PostCreator do end it 'acts correctly' do - post.topic.archetype.should == Archetype.private_message - post.topic.topic_allowed_users.count.should == 1 - post.topic.topic_allowed_groups.count.should == 1 + expect(post.topic.archetype).to eq(Archetype.private_message) + expect(post.topic.topic_allowed_users.count).to eq(1) + expect(post.topic.topic_allowed_groups.count).to eq(1) # does not notify an unrelated user - unrelated.notifications.count.should == 0 - post.topic.subtype.should == TopicSubtype.user_to_user - target_user1.notifications.count.should == 1 - target_user2.notifications.count.should == 1 + expect(unrelated.notifications.count).to eq(0) + expect(post.topic.subtype).to eq(TopicSubtype.user_to_user) + expect(target_user1.notifications.count).to eq(1) + expect(target_user2.notifications.count).to eq(1) end end @@ -485,8 +485,8 @@ describe PostCreator do end it 'acts correctly' do - topic.created_at.should be_within(10.seconds).of(created_at) - post.created_at.should be_within(10.seconds).of(created_at) + expect(topic.created_at).to be_within(10.seconds).of(created_at) + expect(post.created_at).to be_within(10.seconds).of(created_at) end end @@ -494,7 +494,7 @@ describe PostCreator do it 'can save a post' do creator = PostCreator.new(user, raw: 'q', title: 'q', skip_validations: true) creator.create - creator.errors.should == nil + expect(creator.errors).to eq(nil) end end @@ -502,10 +502,10 @@ describe PostCreator do it "has a word count" do creator = PostCreator.new(user, title: 'some inspired poetry for a rainy day', raw: 'mary had a little lamb, little lamb, little lamb. mary had a little lamb') post = creator.create - post.word_count.should == 14 + expect(post.word_count).to eq(14) post.topic.reload - post.topic.word_count.should == 14 + expect(post.topic.word_count).to eq(14) end end @@ -519,19 +519,19 @@ describe PostCreator do title: 'Reviews of Science Ovens', raw: 'Did you know that you can use microwaves to cook your dinner? Science!') creator.create - TopicEmbed.where(embed_url: embed_url).exists?.should == true + expect(TopicEmbed.where(embed_url: embed_url).exists?).to eq(true) end end describe "read credit for creator" do it "should give credit to creator" do post = create_post - PostTiming.find_by(topic_id: post.topic_id, + expect(PostTiming.find_by(topic_id: post.topic_id, post_number: post.post_number, - user_id: post.user_id).msecs.should be > 0 + user_id: post.user_id).msecs).to be > 0 - TopicUser.find_by(topic_id: post.topic_id, - user_id: post.user_id).last_read_post_number.should == 1 + expect(TopicUser.find_by(topic_id: post.topic_id, + user_id: post.user_id).last_read_post_number).to eq(1) end end @@ -541,13 +541,13 @@ describe PostCreator do creator = PostCreator.new(user, {title: "my test title 123", raw: "I should not be allowed to post"} ) creator.create - creator.errors.count.should be > 0 + expect(creator.errors.count).to be > 0 end end it "doesn't strip starting whitespaces" do post = PostCreator.new(user, { title: "testing whitespace stripping", raw: " <-- whitespaces --> " }).create - post.raw.should == " <-- whitespaces -->" + expect(post.raw).to eq(" <-- whitespaces -->") end end diff --git a/spec/components/post_destroyer_spec.rb b/spec/components/post_destroyer_spec.rb index 7785c2ef6c..aca69e2e2a 100644 --- a/spec/components/post_destroyer_spec.rb +++ b/spec/components/post_destroyer_spec.rb @@ -41,10 +41,10 @@ describe PostDestroyer do reply3.reload reply4.reload - reply1.deleted_at.should == nil - reply2.deleted_at.should_not == nil - reply3.deleted_at.should == nil - reply4.deleted_at.should == nil + expect(reply1.deleted_at).to eq(nil) + expect(reply2.deleted_at).not_to eq(nil) + expect(reply3.deleted_at).to eq(nil) + expect(reply4.deleted_at).to eq(nil) end end @@ -69,9 +69,9 @@ describe PostDestroyer do reply2.reload reply3.reload - reply1.deleted_at.should == nil - reply2.deleted_at.should_not == nil - reply3.deleted_at.should == nil + expect(reply1.deleted_at).to eq(nil) + expect(reply2.deleted_at).not_to eq(nil) + expect(reply3.deleted_at).to eq(nil) # if topic is deleted we should still be able to destroy stubs @@ -80,7 +80,7 @@ describe PostDestroyer do PostDestroyer.destroy_stubs reply1.reload - reply1.deleted_at.should == nil + expect(reply1.deleted_at).to eq(nil) # flag the post, it should not nuke the stub anymore topic.recover! @@ -89,7 +89,7 @@ describe PostDestroyer do PostDestroyer.destroy_stubs reply1.reload - reply1.deleted_at.should == nil + expect(reply1.deleted_at).to eq(nil) end @@ -111,8 +111,8 @@ describe PostDestroyer do reply1.reload reply2.reload - reply1.deleted_at.should == nil - reply2.deleted_at.should_not == nil + expect(reply1.deleted_at).to eq(nil) + expect(reply2.deleted_at).not_to eq(nil) SiteSetting.stubs(:delete_removed_posts_after).returns(72) @@ -120,13 +120,13 @@ describe PostDestroyer do PostDestroyer.destroy_stubs - reply1.reload.deleted_at.should == nil + expect(reply1.reload.deleted_at).to eq(nil) SiteSetting.stubs(:delete_removed_posts_after).returns(47) PostDestroyer.destroy_stubs - reply1.reload.deleted_at.should_not == nil + expect(reply1.reload.deleted_at).not_to eq(nil) end it "deletes posts immediately if delete_removed_posts_after is 0" do @@ -138,7 +138,7 @@ describe PostDestroyer do PostDestroyer.new(reply1.user, reply1).destroy - reply1.reload.deleted_at.should_not == nil + expect(reply1.reload.deleted_at).not_to eq(nil) end end @@ -154,25 +154,25 @@ describe PostDestroyer do PostDestroyer.new(post2.user, post2).destroy post2.reload - post2.deleted_at.should be_blank - post2.deleted_by.should be_blank - post2.user_deleted.should == true - post2.raw.should == I18n.t('js.post.deleted_by_author', {count: 24}) - post2.version.should == 2 + expect(post2.deleted_at).to be_blank + expect(post2.deleted_by).to be_blank + expect(post2.user_deleted).to eq(true) + expect(post2.raw).to eq(I18n.t('js.post.deleted_by_author', {count: 24})) + expect(post2.version).to eq(2) # lets try to recover PostDestroyer.new(post2.user, post2).recover post2.reload - post2.version.should == 3 - post2.user_deleted.should == false - post2.cooked.should == @orig + expect(post2.version).to eq(3) + expect(post2.user_deleted).to eq(false) + expect(post2.cooked).to eq(@orig) end context "as a moderator" do it "deletes the post" do PostDestroyer.new(moderator, post).destroy - post.deleted_at.should be_present - post.deleted_by.should == moderator + expect(post.deleted_at).to be_present + expect(post.deleted_by).to eq(moderator) end it "updates the user's post_count" do @@ -193,8 +193,8 @@ describe PostDestroyer do context "as an admin" do it "deletes the post" do PostDestroyer.new(admin, post).destroy - post.deleted_at.should be_present - post.deleted_by.should == admin + expect(post.deleted_at).to be_present + expect(post.deleted_by).to eq(admin) end it "updates the user's post_count" do @@ -221,11 +221,11 @@ describe PostDestroyer do end it 'resets the last_poster_id back to the OP' do - topic.last_post_user_id.should == user.id + expect(topic.last_post_user_id).to eq(user.id) end it 'resets the last_posted_at back to the OP' do - topic.last_posted_at.to_i.should == post.created_at.to_i + expect(topic.last_posted_at.to_i).to eq(post.created_at.to_i) end context 'topic_user' do @@ -233,15 +233,15 @@ describe PostDestroyer do let(:topic_user) { second_user.topic_users.find_by(topic_id: topic.id) } it 'clears the posted flag for the second user' do - topic_user.posted?.should == false + expect(topic_user.posted?).to eq(false) end it "sets the second user's last_read_post_number back to 1" do - topic_user.last_read_post_number.should == 1 + expect(topic_user.last_read_post_number).to eq(1) end it "sets the second user's last_read_post_number back to 1" do - topic_user.highest_seen_post_number.should == 1 + expect(topic_user.highest_seen_post_number).to eq(1) end end @@ -262,8 +262,8 @@ describe PostDestroyer do end it "deletes the post" do - post.deleted_at.should be_present - post.deleted_by.should == moderator + expect(post.deleted_at).to be_present + expect(post.deleted_by).to eq(moderator) end end @@ -273,8 +273,8 @@ describe PostDestroyer do end it "deletes the post" do - post.deleted_at.should be_present - post.deleted_by.should == admin + expect(post.deleted_at).to be_present + expect(post.deleted_by).to eq(admin) end it "creates a new user history entry" do @@ -302,22 +302,22 @@ describe PostDestroyer do it 'changes the post count of the topic' do post.reload - lambda { + expect { PostDestroyer.new(moderator, reply).destroy post.topic.reload - }.should change(post.topic, :posts_count).by(-1) + }.to change(post.topic, :posts_count).by(-1) end it 'lowers the reply_count when the reply is deleted' do - lambda { + expect { PostDestroyer.new(moderator, reply).destroy - }.should change(post.post_replies, :count).by(-1) + }.to change(post.post_replies, :count).by(-1) end it 'should increase the post_number when there are deletion gaps' do PostDestroyer.new(moderator, reply).destroy p = Fabricate(:post, user: post.user, topic: post.topic) - p.post_number.should == 3 + expect(p.post_number).to eq(3) end end @@ -328,9 +328,9 @@ describe PostDestroyer do it 'removes notifications when deleted' do user = Fabricate(:evil_trout) post = create_post(raw: 'Hello @eviltrout') - lambda { + expect { PostDestroyer.new(Fabricate(:moderator), post).destroy - }.should change(user.notifications, :count).by(-1) + }.to change(user.notifications, :count).by(-1) end end @@ -344,15 +344,15 @@ describe PostDestroyer do PostDestroyer.new(moderator, second_post).destroy - PostAction.find_by(id: bookmark.id).should == nil + expect(PostAction.find_by(id: bookmark.id)).to eq(nil) off_topic = PostAction.find_by(id: flag.id) - off_topic.should_not == nil - off_topic.agreed_at.should_not == nil + expect(off_topic).not_to eq(nil) + expect(off_topic.agreed_at).not_to eq(nil) second_post.reload - second_post.bookmark_count.should == 0 - second_post.off_topic_count.should == 1 + expect(second_post.bookmark_count).to eq(0) + expect(second_post.off_topic_count).to eq(1) end end diff --git a/spec/components/post_revisor_spec.rb b/spec/components/post_revisor_spec.rb index b1d710348e..0c16f0f172 100644 --- a/spec/components/post_revisor_spec.rb +++ b/spec/components/post_revisor_spec.rb @@ -7,6 +7,56 @@ describe PostRevisor do let(:newuser) { Fabricate(:newuser) } let(:post_args) { {user: newuser, topic: topic} } + context 'TopicChanges' do + let(:topic) { Fabricate(:topic) } + let(:tc) { + topic.reload + PostRevisor::TopicChanges.new(topic, topic.user) + } + + it 'provides a guardian' do + tc.guardian.should be_an_instance_of Guardian + end + + it 'tracks changes properly' do + tc.diff.should == {} + + # it remembers changes we tell it to + tc.record_change('height', '180cm', '170cm') + tc.diff['height'].should == ['180cm', '170cm'] + + # it works with arrays of values + tc.record_change('colors', nil, ['red', 'blue']) + tc.diff['colors'].should == [nil, ['red', 'blue']] + + # it does not record changes to the same val + tc.record_change('wat', 'js', 'js') + tc.diff['wat'].should be_nil + + tc.record_change('tags', ['a', 'b'], ['a', 'b']) + tc.diff['tags'].should be_nil + + end + end + + context 'revise wiki' do + + before do + # There used to be a bug where wiki changes were considered posting "too similar" + # so this is enabled and checked + $redis.delete_prefixed('unique-post') + SiteSetting.unique_posts_mins = 10 + end + + it 'allows the user to change it to a wiki' do + pc = PostCreator.new(newuser, topic_id: topic.id, raw: 'this is a post that will become a wiki') + post = pc.create + post.revise(post.user, wiki: true).should == true + post.reload + post.wiki.should be_true + end + end + context 'revise' do let(:post) { Fabricate(:post, post_args) } let(:first_version_at) { post.last_version_at } @@ -15,10 +65,10 @@ describe PostRevisor do describe 'with the same body' do it "doesn't change version" do - lambda { - subject.revise!(post.user, { raw: post.raw }).should == false + expect { + expect(subject.revise!(post.user, { raw: post.raw })).to eq(false) post.reload - }.should_not change(post, :version) + }.not_to change(post, :version) end end @@ -28,11 +78,11 @@ describe PostRevisor do subject.revise!(post.user, { raw: 'updated body' }, revised_at: post.updated_at + 10.seconds) post.reload - post.version.should == 1 - post.public_version.should == 1 - post.revisions.size.should == 0 - post.last_version_at.should == first_version_at - subject.category_changed.should be_blank + expect(post.version).to eq(1) + expect(post.public_version).to eq(1) + expect(post.revisions.size).to eq(0) + expect(post.last_version_at).to eq(first_version_at) + expect(subject.category_changed).to be_blank end end @@ -47,20 +97,20 @@ describe PostRevisor do end it "doesn't update a category" do - subject.category_changed.should be_blank + expect(subject.category_changed).to be_blank end it 'updates the versions' do - post.version.should == 2 - post.public_version.should == 2 + expect(post.version).to eq(2) + expect(post.public_version).to eq(2) end it 'creates a new revision' do - post.revisions.size.should == 1 + expect(post.revisions.size).to eq(1) end it "updates the last_version_at" do - post.last_version_at.to_i.should == revised_at.to_i + expect(post.last_version_at.to_i).to eq(revised_at.to_i) end describe "new edit window" do @@ -71,16 +121,16 @@ describe PostRevisor do end it "doesn't create a new version if you do another" do - post.version.should == 2 - post.public_version.should == 2 + expect(post.version).to eq(2) + expect(post.public_version).to eq(2) end it "doesn't change last_version_at" do - post.last_version_at.to_i.should == revised_at.to_i + expect(post.last_version_at.to_i).to eq(revised_at.to_i) end it "doesn't update a category" do - subject.category_changed.should be_blank + expect(subject.category_changed).to be_blank end context "after second window" do @@ -93,12 +143,12 @@ describe PostRevisor do end it "does create a new version after the edit window" do - post.version.should == 3 - post.public_version.should == 3 + expect(post.version).to eq(3) + expect(post.public_version).to eq(3) end it "does create a new version after the edit window" do - post.last_version_at.to_i.should == new_revised_at.to_i + expect(post.last_version_at.to_i).to eq(new_revised_at.to_i) end end end @@ -115,7 +165,7 @@ describe PostRevisor do let(:new_description) { "this is my new description." } it "should have no description by default" do - category.description.should be_blank + expect(category.description).to be_blank end context "one paragraph description" do @@ -125,11 +175,11 @@ describe PostRevisor do end it "returns the changed category info" do - subject.category_changed.should == category + expect(subject.category_changed).to eq(category) end it "updates the description of the category" do - category.description.should == new_description + expect(category.description).to eq(new_description) end end @@ -140,11 +190,11 @@ describe PostRevisor do end it "returns the changed category info" do - subject.category_changed.should == category + expect(subject.category_changed).to eq(category) end it "updates the description of the category" do - category.description.should == new_description + expect(category.description).to eq(new_description) end end @@ -156,11 +206,11 @@ describe PostRevisor do end it "puts the description back to nothing" do - category.description.should be_blank + expect(category.description).to be_blank end it "returns the changed category info" do - subject.category_changed.should == category + expect(subject.category_changed).to eq(category) end end @@ -186,11 +236,11 @@ describe PostRevisor do end it "allows an admin to insert images into a new user's post" do - post.errors.should be_blank + expect(post.errors).to be_blank end it "marks the admin as the last updater" do - post.last_editor_id.should == changed_by.id + expect(post.last_editor_id).to eq(changed_by.id) end end @@ -204,7 +254,7 @@ describe PostRevisor do end it "doesn't allow images to be inserted" do - post.errors.should be_present + expect(post.errors).to be_present end end @@ -215,34 +265,34 @@ describe PostRevisor do let!(:result) { subject.revise!(changed_by, { raw: "lets update the body" }) } it 'returns true' do - result.should == true + expect(result).to eq(true) end it 'updates the body' do - post.raw.should == "lets update the body" + expect(post.raw).to eq("lets update the body") end it 'sets the invalidate oneboxes attribute' do - post.invalidate_oneboxes.should == true + expect(post.invalidate_oneboxes).to eq(true) end it 'increased the versions' do - post.version.should == 2 - post.public_version.should == 2 + expect(post.version).to eq(2) + expect(post.public_version).to eq(2) end it 'has the new revision' do - post.revisions.size.should == 1 + expect(post.revisions.size).to eq(1) end it "saved the user who made the change in the revisions" do - post.revisions.first.user_id.should == changed_by.id + expect(post.revisions.first.user_id).to eq(changed_by.id) end it "updates the word count" do - post.word_count.should == 4 + expect(post.word_count).to eq(4) post.topic.reload - post.topic.word_count.should == 4 + expect(post.topic.word_count).to eq(4) end context 'second poster posts again quickly' do @@ -253,9 +303,9 @@ describe PostRevisor do end it 'is a ninja edit, because the second poster posted again quickly' do - post.version.should == 2 - post.public_version.should == 2 - post.revisions.size.should == 1 + expect(post.version).to eq(2) + expect(post.public_version).to eq(2) + expect(post.revisions.size).to eq(1) end end end @@ -279,7 +329,7 @@ describe PostRevisor do it "doesn't strip starting whitespaces" do subject.revise!(post.user, { raw: " <-- whitespaces --> " }) post.reload - post.raw.should == " <-- whitespaces -->" + expect(post.raw).to eq(" <-- whitespaces -->") end end diff --git a/spec/components/pretty_text_spec.rb b/spec/components/pretty_text_spec.rb index 76e5122121..00bb220717 100644 --- a/spec/components/pretty_text_spec.rb +++ b/spec/components/pretty_text_spec.rb @@ -12,40 +12,41 @@ describe PrettyText do before(:each) do eviltrout = User.new - eviltrout.stubs(:avatar_template).returns("http://test.localhost/uploads/default/avatars/42d/57c/46ce7ee487/{size}.png") + eviltrout.stubs(:avatar_template).returns("//test.localhost/uploads/default/avatars/42d/57c/46ce7ee487/{size}.png") User.expects(:find_by).with(username_lower: "eviltrout").returns(eviltrout) end it "produces a quote even with new lines in it" do - PrettyText.cook("[quote=\"EvilTrout, post:123, topic:456, full:true\"]ddd\n[/quote]").should match_html "" + expect(PrettyText.cook("[quote=\"EvilTrout, post:123, topic:456, full:true\"]ddd\n[/quote]")).to match_html "" end it "should produce a quote" do - PrettyText.cook("[quote=\"EvilTrout, post:123, topic:456, full:true\"]ddd[/quote]").should match_html "" + expect(PrettyText.cook("[quote=\"EvilTrout, post:123, topic:456, full:true\"]ddd[/quote]")).to match_html "" end it "trims spaces on quote params" do - PrettyText.cook("[quote=\"EvilTrout, post:555, topic: 666\"]ddd[/quote]").should match_html "" + expect(PrettyText.cook("[quote=\"EvilTrout, post:555, topic: 666\"]ddd[/quote]")).to match_html "" end end it "should handle 3 mentions in a row" do - PrettyText.cook('@hello @hello @hello').should match_html "

@hello @hello @hello

" + expect(PrettyText.cook('@hello @hello @hello')).to match_html "

@hello @hello @hello

" end it "should sanitize the html" do - PrettyText.cook("").should match_html "

" + expect(PrettyText.cook("")).to match_html "

" end it 'should allow for @mentions to have punctuation' do - PrettyText.cook("hello @bob's @bob,@bob; @bob\"").should - match_html "

hello @bob's @bob,@bob; @bob\"

" + expect(PrettyText.cook("hello @bob's @bob,@bob; @bob\"")).to match_html( + "

hello @bob's @bob,@bob; @bob\"

" + ) end # see: https://github.com/sparklemotion/nokogiri/issues/1173 skip 'allows html entities correctly' do - PrettyText.cook("ℵ£¢").should == "

ℵ£¢

" + expect(PrettyText.cook("ℵ£¢")).to eq("

ℵ£¢

") end end @@ -57,108 +58,107 @@ describe PrettyText do end it "should inject nofollow in all user provided links" do - PrettyText.cook('cnn').should =~ /nofollow/ + expect(PrettyText.cook('cnn')).to match(/nofollow/) end it "should not inject nofollow in all local links" do - (PrettyText.cook("cnn") !~ /nofollow/).should == true + expect(PrettyText.cook("cnn") !~ /nofollow/).to eq(true) end it "should not inject nofollow in all subdomain links" do - (PrettyText.cook("cnn") !~ /nofollow/).should == true + expect(PrettyText.cook("cnn") !~ /nofollow/).to eq(true) end it "should not inject nofollow for foo.com" do - (PrettyText.cook("cnn") !~ /nofollow/).should == true + expect(PrettyText.cook("cnn") !~ /nofollow/).to eq(true) end it "should not inject nofollow for bar.foo.com" do - (PrettyText.cook("cnn") !~ /nofollow/).should == true + expect(PrettyText.cook("cnn") !~ /nofollow/).to eq(true) end it "should not inject nofollow if omit_nofollow option is given" do - (PrettyText.cook('cnn', omit_nofollow: true) !~ /nofollow/).should == true + expect(PrettyText.cook('cnn', omit_nofollow: true) !~ /nofollow/).to eq(true) end end describe "Excerpt" do it "sanitizes attempts to inject invalid attributes" do - spinner = "",100).should == "[image]" + expect(PrettyText.excerpt("",100)).to eq("[image]") end it "should keep alt tags" do - PrettyText.excerpt("car",100).should == "[car]" + expect(PrettyText.excerpt("car",100)).to eq("[car]") end it "should keep title tags" do - PrettyText.excerpt("",100).should == "[car]" + expect(PrettyText.excerpt("",100)).to eq("[car]") end it "should convert images to markdown if the option is set" do - PrettyText.excerpt("", 100, markdown_images: true).should == "![car](http://cnn.com/a.gif)" + expect(PrettyText.excerpt("", 100, markdown_images: true)).to eq("![car](http://cnn.com/a.gif)") end it "should keep spoilers" do - PrettyText.excerpt("
", 100).should match_html "[image]" - PrettyText.excerpt("spoiler
", 100).should match_html "spoiler" + expect(PrettyText.excerpt("
", 100)).to match_html "[image]" + expect(PrettyText.excerpt("spoiler
", 100)).to match_html "spoiler" end it "should remove meta informations" do - PrettyText.excerpt(wrapped_image, 100).should match_html "[image]" + expect(PrettyText.excerpt(wrapped_image, 100)).to match_html "[image]" end end it "should have an option to strip links" do - PrettyText.excerpt("cnn",100, strip_links: true).should == "cnn" + expect(PrettyText.excerpt("cnn",100, strip_links: true)).to eq("cnn") end it "should preserve links" do - PrettyText.excerpt("cnn",100).should match_html "cnn" + expect(PrettyText.excerpt("cnn",100)).to match_html "cnn" end it "should deal with special keys properly" do - PrettyText.excerpt("
",100).should == "" + expect(PrettyText.excerpt("
",100)).to eq("") end it "should truncate stuff properly" do - PrettyText.excerpt("hello world",5).should == "hello…" - PrettyText.excerpt("

hello

world

",6).should == "hello w…" + expect(PrettyText.excerpt("hello world",5)).to eq("hello…") + expect(PrettyText.excerpt("

hello

world

",6)).to eq("hello w…") end it "should insert a space between to Ps" do - PrettyText.excerpt("

a

b

",5).should == "a b" + expect(PrettyText.excerpt("

a

b

",5)).to eq("a b") end it "should strip quotes" do - PrettyText.excerpt("boom",5).should == "boom" + expect(PrettyText.excerpt("boom",5)).to eq("boom") end it "should not count the surrounds of a link" do - PrettyText.excerpt("cnn",3).should match_html "cnn" + expect(PrettyText.excerpt("cnn",3)).to match_html "cnn" end it "uses an ellipsis instead of html entities if provided with the option" do - PrettyText.excerpt("cnn", 2, text_entities: true).should match_html "cn..." + expect(PrettyText.excerpt("cnn", 2, text_entities: true)).to match_html "cn..." end it "should truncate links" do - PrettyText.excerpt("cnn",2).should match_html "cn…" + expect(PrettyText.excerpt("cnn",2)).to match_html "cn…" end it "doesn't extract empty quotes as links" do - PrettyText.extract_links("\n").to_a.should be_empty + expect(PrettyText.extract_links("\n").to_a).to be_empty end def extract_urls(text) @@ -166,15 +166,15 @@ describe PrettyText do end it "should be able to extract links" do - extract_urls("http://bla.com").should == ["http://cnn.com"] + expect(extract_urls("http://bla.com")).to eq(["http://cnn.com"]) end it "should extract links to topics" do - extract_urls("").should == ["/t/topic/321"] + expect(extract_urls("")).to eq(["/t/topic/321"]) end it "should extract links to posts" do - extract_urls("").should == ["/t/topic/1234/4567"] + expect(extract_urls("")).to eq(["/t/topic/1234/4567"]) end it "should not extract links inside quotes" do @@ -187,33 +187,38 @@ describe PrettyText do http://useless2.com ") - links.map{|l| [l.url, l.is_quote]}.to_a.sort.should == + expect(links.map{|l| [l.url, l.is_quote]}.to_a.sort).to eq( [["http://body_only.com",false], ["http://body_and_quote.com", false], ["/t/topic/1234",true] ].sort + ) end it "should not preserve tags in code blocks" do - PrettyText.excerpt("
<h3>Hours</h3>
",100).should == "<h3>Hours</h3>" + expect(PrettyText.excerpt("
<h3>Hours</h3>
",100)).to eq("<h3>Hours</h3>") end it "should handle nil" do - PrettyText.excerpt(nil,100).should == '' + expect(PrettyText.excerpt(nil,100)).to eq('') end it "handles span excerpt at the beginning of a post" do - PrettyText.excerpt("hi test",100).should == 'hi' + expect(PrettyText.excerpt("hi test",100)).to eq('hi') post = Fabricate(:post, raw: "hi test") - post.excerpt.should == "hi" + expect(post.excerpt).to eq("hi") end it "ignores max excerpt length if a span excerpt is specified" do two_hundred = "123456789 " * 20 + "." text = two_hundred + "#{two_hundred}" + two_hundred - PrettyText.excerpt(text, 100).should == two_hundred + expect(PrettyText.excerpt(text, 100)).to eq(two_hundred) post = Fabricate(:post, raw: text) - post.excerpt.should == two_hundred + expect(post.excerpt).to eq(two_hundred) + end + + it "unescapes html entities when we want text entities" do + expect(PrettyText.excerpt("'", 500, text_entities: true)).to eq("'") end end @@ -252,17 +257,17 @@ describe PrettyText do it "adds base url to relative links" do html = "

@wiseguy, @trollol what do you guys think?

" output = make_abs_string(html) - output.should == "

@wiseguy, @trollol what do you guys think?

" + expect(output).to eq("

@wiseguy, @trollol what do you guys think?

") end it "doesn't change external absolute links" do html = "

Check out this guy.

" - make_abs_string(html).should == html + expect(make_abs_string(html)).to eq(html) end it "doesn't change internal absolute links" do html = "

Check out this guy.

" - make_abs_string(html).should == html + expect(make_abs_string(html)).to eq(html) end it "can tolerate invalid URLs" do @@ -280,11 +285,11 @@ describe PrettyText do it "doesn't change HTML when there's no wrapped image" do html = "" - strip_image_wrapping(html).should == html + expect(strip_image_wrapping(html)).to eq(html) end it "strips the metadata" do - strip_image_wrapping(wrapped_image).should match_html "
" + expect(strip_image_wrapping(wrapped_image)).to match_html "
" end end @@ -294,5 +299,9 @@ describe PrettyText do end end + it 'can escape *' do + expect(PrettyText.cook("***a***a")).to match_html("

aa

") + expect(PrettyText.cook("***\\****a")).to match_html("

*a

") + end end diff --git a/spec/components/promotion_spec.rb b/spec/components/promotion_spec.rb index af0f783c3d..0729e63672 100644 --- a/spec/components/promotion_spec.rb +++ b/spec/components/promotion_spec.rb @@ -19,18 +19,18 @@ describe Promotion do let(:promotion) { Promotion.new(user) } it "doesn't raise an error with a nil user" do - -> { Promotion.new(nil).review }.should_not raise_error + expect { Promotion.new(nil).review }.not_to raise_error end context 'that has done nothing' do let!(:result) { promotion.review } it "returns false" do - result.should == false + expect(result).to eq(false) end it "has not changed the user's trust level" do - user.trust_level.should == TrustLevel[0] + expect(user.trust_level).to eq(TrustLevel[0]) end end @@ -45,11 +45,11 @@ describe Promotion do end it "returns true" do - @result.should == true + expect(@result).to eq(true) end it "has upgraded the user to basic" do - user.trust_level.should == TrustLevel[1] + expect(user.trust_level).to eq(TrustLevel[1]) end end @@ -64,11 +64,11 @@ describe Promotion do let!(:result) { promotion.review } it "returns false" do - result.should == false + expect(result).to eq(false) end it "has not changed the user's trust level" do - user.trust_level.should == TrustLevel[1] + expect(user.trust_level).to eq(TrustLevel[1]) end end @@ -88,11 +88,11 @@ describe Promotion do end it "returns true" do - @result.should == true + expect(@result).to eq(true) end it "has upgraded the user to regular" do - user.trust_level.should == TrustLevel[2] + expect(user.trust_level).to eq(TrustLevel[2]) end end @@ -109,7 +109,7 @@ describe Promotion do it "review_tl2 returns false" do expect { - promotion.review_tl2.should == false + expect(promotion.review_tl2).to eq(false) }.to_not change { user.reload.trust_level } end @@ -132,12 +132,12 @@ describe Promotion do end it "review_tl2 returns true" do - promotion.review_tl2.should == true + expect(promotion.review_tl2).to eq(true) end it "promotes to tl3" do - promotion.review_tl2.should == true - user.reload.trust_level.should == TrustLevel[3] + expect(promotion.review_tl2).to eq(true) + expect(user.reload.trust_level).to eq(TrustLevel[3]) end it "logs a trust level change" do diff --git a/spec/components/rate_limiter_spec.rb b/spec/components/rate_limiter_spec.rb index bed1724897..1c264fb3d1 100644 --- a/spec/components/rate_limiter_spec.rb +++ b/spec/components/rate_limiter_spec.rb @@ -14,11 +14,11 @@ describe RateLimiter do end it "returns true for can_perform?" do - rate_limiter.can_perform?.should == true + expect(rate_limiter.can_perform?).to eq(true) end it "doesn't raise an error on performed!" do - lambda { rate_limiter.performed! }.should_not raise_error + expect { rate_limiter.performed! }.not_to raise_error end end @@ -31,11 +31,11 @@ describe RateLimiter do context 'never done' do it "should perform right away" do - rate_limiter.can_perform?.should == true + expect(rate_limiter.can_perform?).to eq(true) end it "performs without an error" do - lambda { rate_limiter.performed! }.should_not raise_error + expect { rate_limiter.performed! }.not_to raise_error end end @@ -46,33 +46,33 @@ describe RateLimiter do end it "returns false for can_perform when the limit has been hit" do - rate_limiter.can_perform?.should == false + expect(rate_limiter.can_perform?).to eq(false) end it "raises an error the third time called" do - lambda { rate_limiter.performed! }.should raise_error(RateLimiter::LimitExceeded) + expect { rate_limiter.performed! }.to raise_error(RateLimiter::LimitExceeded) end context "as an admin/moderator" do it "returns true for can_perform if the user is an admin" do user.admin = true - rate_limiter.can_perform?.should == true + expect(rate_limiter.can_perform?).to eq(true) end it "doesn't raise an error when an admin performs the task" do user.admin = true - lambda { rate_limiter.performed! }.should_not raise_error + expect { rate_limiter.performed! }.not_to raise_error end it "returns true for can_perform if the user is a mod" do user.moderator = true - rate_limiter.can_perform?.should == true + expect(rate_limiter.can_perform?).to eq(true) end it "doesn't raise an error when a moderator performs the task" do user.moderator = true - lambda { rate_limiter.performed! }.should_not raise_error + expect { rate_limiter.performed! }.not_to raise_error end @@ -84,11 +84,11 @@ describe RateLimiter do end it "returns true for can_perform since there is now room" do - rate_limiter.can_perform?.should == true + expect(rate_limiter.can_perform?).to eq(true) end it "raises no error now that there is room" do - lambda { rate_limiter.performed! }.should_not raise_error + expect { rate_limiter.performed! }.not_to raise_error end end diff --git a/spec/components/redis_store_spec.rb b/spec/components/redis_store_spec.rb index 5e10774fc4..d32e56d768 100644 --- a/spec/components/redis_store_spec.rb +++ b/spec/components/redis_store_spec.rb @@ -23,7 +23,7 @@ describe "Redis Store" do r = store.read "key" - r.should == "key in store" + expect(r).to eq("key in store") end it "doesn't collide with our Cache" do @@ -38,7 +38,7 @@ describe "Redis Store" do r = store.read "key" - r.should == "key in store" + expect(r).to eq("key in store") end it "can be cleared without clearing our cache" do @@ -51,8 +51,8 @@ describe "Redis Store" do end store.clear - store.read("key").should == nil - cache.fetch("key").should == "key in cache" + expect(store.read("key")).to eq(nil) + expect(cache.fetch("key")).to eq("key in cache") end diff --git a/spec/components/s3_helper_spec.rb b/spec/components/s3_helper_spec.rb index d8e5c23d12..ee1f971f55 100644 --- a/spec/components/s3_helper_spec.rb +++ b/spec/components/s3_helper_spec.rb @@ -25,7 +25,7 @@ describe "S3Helper" do let(:file) { file_from_fixtures(filename) } it "ensures the bucket name isn't blank" do - -> { S3Helper.new("") }.should raise_error(Discourse::InvalidParameters) + expect { S3Helper.new("") }.to raise_error(Discourse::InvalidParameters) end describe ".upload" do diff --git a/spec/components/scheduler/defer_spec.rb b/spec/components/scheduler/defer_spec.rb index f564e94a06..6f8d30ebf0 100644 --- a/spec/components/scheduler/defer_spec.rb +++ b/spec/components/scheduler/defer_spec.rb @@ -1,6 +1,6 @@ # encoding: utf-8 require 'spec_helper' -require 'scheduler/scheduler' +require_dependency 'scheduler/defer' describe Scheduler::Defer do class DeferInstance @@ -23,6 +23,32 @@ describe Scheduler::Defer do @defer.stop! end + it "can pause and resume" do + x = 1 + @defer.pause + + @defer.later do + x = 2 + end + + @defer.do_all_work + + expect(x).to eq(2) + + @defer.resume + + + @defer.later do + x = 3 + end + + wait_for(10) do + x == 3 + end + + expect(x).to eq(3) + end + it "recovers from a crash / fork" do s = nil @defer.stop! @@ -40,7 +66,7 @@ describe Scheduler::Defer do s == "good" end - s.should == "good" + expect(s).to eq("good") end it "can queue jobs properly" do @@ -54,7 +80,7 @@ describe Scheduler::Defer do s == "good" end - s.should == "good" + expect(s).to eq("good") end end diff --git a/spec/components/scheduler/manager_spec.rb b/spec/components/scheduler/manager_spec.rb index eec63758f5..52c92a9985 100644 --- a/spec/components/scheduler/manager_spec.rb +++ b/spec/components/scheduler/manager_spec.rb @@ -53,7 +53,7 @@ describe Scheduler::Manager do describe '#sync' do it 'increases' do - Scheduler::Manager.seq.should == Scheduler::Manager.seq - 1 + expect(Scheduler::Manager.seq).to eq(Scheduler::Manager.seq - 1) end end @@ -62,7 +62,7 @@ describe Scheduler::Manager do it 'should nuke missing jobs' do $redis.zadd Scheduler::Manager.queue_key, Time.now.to_i - 1000, "BLABLA" manager.tick - $redis.zcard(Scheduler::Manager.queue_key).should == 0 + expect($redis.zcard(Scheduler::Manager.queue_key)).to eq(0) end @@ -81,7 +81,7 @@ describe Scheduler::Manager do manager.reschedule_orphans! info = manager.schedule_info(Testing::SuperLongJob) - info.next_run.should <= Time.now.to_i + expect(info.next_run).to be <= Time.now.to_i end it 'should only run pending job once' do @@ -100,19 +100,19 @@ describe Scheduler::Manager do end end.map(&:join) - Testing::RandomJob.runs.should == 1 + expect(Testing::RandomJob.runs).to eq(1) info = manager.schedule_info(Testing::RandomJob) - info.prev_run.should be <= Time.now.to_i - info.prev_duration.should be > 0 - info.prev_result.should == "OK" + expect(info.prev_run).to be <= Time.now.to_i + expect(info.prev_duration).to be > 0 + expect(info.prev_result).to eq("OK") end end describe '#discover_schedules' do it 'Discovers Testing::RandomJob' do - Scheduler::Manager.discover_schedules.should include(Testing::RandomJob) + expect(Scheduler::Manager.discover_schedules).to include(Testing::RandomJob) end end @@ -122,8 +122,8 @@ describe Scheduler::Manager do manager.remove(Testing::RandomJob) manager.ensure_schedule!(Testing::RandomJob) - manager.next_run(Testing::RandomJob) - .should be_within(5.minutes.to_i).of(Time.now.to_i + 5.minutes) + expect(manager.next_run(Testing::RandomJob)) + .to be_within(5.minutes.to_i).of(Time.now.to_i + 5.minutes) end end end diff --git a/spec/components/scheduler/schedule_info_spec.rb b/spec/components/scheduler/schedule_info_spec.rb index 6b405f725d..96afbb8672 100644 --- a/spec/components/scheduler/schedule_info_spec.rb +++ b/spec/components/scheduler/schedule_info_spec.rb @@ -28,29 +28,29 @@ describe Scheduler::ScheduleInfo do end it "is a scheduled job" do - RandomJob.should be_scheduled + expect(RandomJob).to be_scheduled end it 'starts off invalid' do - @info.valid?.should == false + expect(@info.valid?).to eq(false) end it 'will have a due date in the next 5 minutes if it was blank' do @info.schedule! - @info.valid?.should == true - @info.next_run.should be_within(5.minutes).of(Time.now.to_i) + expect(@info.valid?).to eq(true) + expect(@info.next_run).to be_within(5.minutes).of(Time.now.to_i) end it 'will have a due date within the next hour if it just ran' do @info.prev_run = Time.now.to_i @info.schedule! - @info.valid?.should == true - @info.next_run.should be_within(1.hour * manager.random_ratio).of(Time.now.to_i + 1.hour) + expect(@info.valid?).to eq(true) + expect(@info.next_run).to be_within(1.hour * manager.random_ratio).of(Time.now.to_i + 1.hour) end it 'is invalid if way in the future' do @info.next_run = Time.now.to_i + 1.year - @info.valid?.should == false + expect(@info.valid?).to eq(false) end end @@ -75,22 +75,22 @@ describe Scheduler::ScheduleInfo do end it "is a scheduled job" do - DailyJob.should be_scheduled + expect(DailyJob).to be_scheduled end it "starts off invalid" do - @info.valid?.should == false + expect(@info.valid?).to eq(false) end skip "will have a due date at the appropriate time if blank" do - @info.next_run.should == nil + expect(@info.next_run).to eq(nil) @info.schedule! - @info.valid?.should == true + expect(@info.valid?).to eq(true) end it 'is invalid if way in the future' do @info.next_run = Time.now.to_i + 1.year - @info.valid?.should == false + expect(@info.valid?).to eq(false) end end diff --git a/spec/components/score_calculator_spec.rb b/spec/components/score_calculator_spec.rb index df7be0a7e9..34694c317e 100644 --- a/spec/components/score_calculator_spec.rb +++ b/spec/components/score_calculator_spec.rb @@ -15,21 +15,21 @@ describe ScoreCalculator do end it 'takes the supplied weightings into effect' do - post.score.should == 333 - another_post.score.should == 666 + expect(post.score).to eq(333) + expect(another_post.score).to eq(666) end it "creates the percent_ranks" do - another_post.percent_rank.should == 0.0 - post.percent_rank.should == 1.0 + expect(another_post.percent_rank).to eq(0.0) + expect(post.percent_rank).to eq(1.0) end it "gives the topic a score" do - topic.score.should be_present + expect(topic.score).to be_present end it "gives the topic a percent_rank" do - topic.percent_rank.should_not == 1.0 + expect(topic.percent_rank).not_to eq(1.0) end end @@ -39,14 +39,14 @@ describe ScoreCalculator do it "won't update the site settings when the site settings don't match" do ScoreCalculator.new(reads: 3).calculate topic.reload - topic.has_summary.should == false + expect(topic.has_summary).to eq(false) end it "removes the summary flag if the topic no longer qualifies" do topic.update_column(:has_summary, true) ScoreCalculator.new(reads: 3).calculate topic.reload - topic.has_summary.should == false + expect(topic.has_summary).to eq(false) end it "won't update the site settings when the site settings don't match" do @@ -56,7 +56,7 @@ describe ScoreCalculator do ScoreCalculator.new(reads: 3).calculate topic.reload - topic.has_summary.should == true + expect(topic.has_summary).to eq(true) end end diff --git a/spec/components/search_spec.rb b/spec/components/search_spec.rb index a80d648315..cdc5a31fc2 100644 --- a/spec/components/search_spec.rb +++ b/spec/components/search_spec.rb @@ -96,6 +96,66 @@ describe Search do end end + context 'private messages' do + + let(:topic) { + Fabricate(:topic, + category_id: nil, + archetype: 'private_message') + } + + let(:post) { Fabricate(:post, topic: topic) } + let(:reply) { Fabricate(:post, topic: topic, + raw: 'hello from mars, we just landed') } + + + + it 'searches correctly' do + + expect do + Search.execute('mars', type_filter: 'private_messages') + end.to raise_error(Discourse::InvalidAccess) + + TopicAllowedUser.create!(user_id: reply.user_id, topic_id: topic.id) + TopicAllowedUser.create!(user_id: post.user_id, topic_id: topic.id) + + + results = Search.execute('mars', + type_filter: 'private_messages', + guardian: Guardian.new(reply.user)) + + results.posts.length.should == 1 + + + results = Search.execute('mars', + search_context: topic, + guardian: Guardian.new(reply.user)) + + results.posts.length.should == 1 + + # does not leak out + results = Search.execute('mars', + type_filter: 'private_messages', + guardian: Guardian.new(Fabricate(:user))) + + results.posts.length.should == 0 + + Fabricate(:topic, category_id: nil, archetype: 'private_message') + Fabricate(:post, topic: topic, raw: 'another secret pm from mars, testing') + + + # admin can search everything with correct context + results = Search.execute('mars', + type_filter: 'private_messages', + search_context: post.user, + guardian: Guardian.new(Fabricate(:admin))) + + results.posts.length.should == 1 + + end + + end + context 'topics' do let(:post) { Fabricate(:post) } let(:topic) { post.topic} diff --git a/spec/components/sidekiq/pausable_spec.rb b/spec/components/sidekiq/pausable_spec.rb index d2055e62ac..887702489c 100644 --- a/spec/components/sidekiq/pausable_spec.rb +++ b/spec/components/sidekiq/pausable_spec.rb @@ -4,8 +4,8 @@ require_dependency 'sidekiq/pausable' describe Sidekiq do it "can pause and unpause" do Sidekiq.pause! - Sidekiq.paused?.should == true + expect(Sidekiq.paused?).to eq(true) Sidekiq.unpause! - Sidekiq.paused?.should == false + expect(Sidekiq.paused?).to eq(false) end end diff --git a/spec/components/site_setting_extension_spec.rb b/spec/components/site_setting_extension_spec.rb index 1e0adca5aa..8173ba5e30 100644 --- a/spec/components/site_setting_extension_spec.rb +++ b/spec/components/site_setting_extension_spec.rb @@ -4,22 +4,27 @@ require_dependency 'site_settings/local_process_provider' describe SiteSettingExtension do - class FakeSettings - extend SiteSettingExtension - self.provider = SiteSettings::LocalProcessProvider.new + + let :provider do + SiteSettings::LocalProcessProvider.new end - class FakeSettings2 - extend SiteSettingExtension - self.provider = FakeSettings.provider + def new_settings(provider) + c = Class.new + c.class_eval do + extend SiteSettingExtension + self.provider = provider + end + + c end let :settings do - FakeSettings + new_settings(provider) end let :settings2 do - FakeSettings2 + new_settings(provider) end describe "refresh!" do @@ -339,4 +344,71 @@ describe SiteSettingExtension do end end + describe "hidden" do + before do + settings.setting(:superman_identity, 'Clark Kent', hidden: true) + settings.refresh! + end + + it "is in the `hidden_settings` collection" do + settings.hidden_settings.include?(:superman_identity).should == true + end + + it "can be retrieved" do + settings.superman_identity.should == "Clark Kent" + end + + it "is not present in all_settings by default" do + settings.all_settings.find {|s| s[:setting] == :superman_identity }.should be_blank + end + + it "is present in all_settings when we ask for hidden" do + settings.all_settings(true).find {|s| s[:setting] == :superman_identity }.should be_present + end + end + + describe "shadowed_by_global" do + context "without global setting" do + before do + settings.setting(:trout_api_key, 'evil', shadowed_by_global: true) + settings.refresh! + end + + it "should not add the key to the shadowed_settings collection" do + settings.shadowed_settings.include?(:trout_api_key).should == false + end + + it "can return the default value" do + settings.trout_api_key.should == 'evil' + end + + it "can overwrite the default" do + settings.trout_api_key = 'tophat' + settings.refresh! + settings.trout_api_key.should == 'tophat' + end + end + + context "with global setting" do + before do + GlobalSetting.stubs(:trout_api_key).returns('purringcat') + settings.setting(:trout_api_key, 'evil', shadowed_by_global: true) + settings.refresh! + end + + it "should return the global setting instead of default" do + settings.trout_api_key.should == 'purringcat' + end + + it "should return the global setting after a refresh" do + settings.refresh! + settings.trout_api_key.should == 'purringcat' + end + + it "should add the key to the shadowed_settings collection" do + settings.shadowed_settings.include?(:trout_api_key).should == true + end + end + end + end diff --git a/spec/components/site_settings/yaml_loader_spec.rb b/spec/components/site_settings/yaml_loader_spec.rb index c317c52389..a6800a1134 100644 --- a/spec/components/site_settings/yaml_loader_spec.rb +++ b/spec/components/site_settings/yaml_loader_spec.rb @@ -53,7 +53,7 @@ describe SiteSettings::YamlLoader do it "maintains order of categories" do receiver.load_yaml(simple) - receiver.categories.should == ['category1', 'category2', 'category3'] + expect(receiver.categories).to eq(['category1', 'category2', 'category3']) end it "can load client settings" do diff --git a/spec/components/slug_spec.rb b/spec/components/slug_spec.rb index 8967dd3312..166f6b3cb0 100644 --- a/spec/components/slug_spec.rb +++ b/spec/components/slug_spec.rb @@ -6,58 +6,58 @@ require 'slug' describe Slug do it 'replaces spaces with hyphens' do - Slug.for("hello world").should == 'hello-world' + expect(Slug.for("hello world")).to eq('hello-world') end it 'changes accented characters' do - Slug.for('àllo').should == 'allo' + expect(Slug.for('àllo')).to eq('allo') end it 'replaces symbols' do - Slug.for('evil#trout').should == 'evil-trout' + expect(Slug.for('evil#trout')).to eq('evil-trout') end it 'handles a.b.c properly' do - Slug.for("a.b.c").should == "a-b-c" + expect(Slug.for("a.b.c")).to eq("a-b-c") end it 'handles double dots right' do - Slug.for("a....b.....c").should == "a-b-c" + expect(Slug.for("a....b.....c")).to eq("a-b-c") end it 'strips trailing punctuation' do - Slug.for("hello...").should == "hello" + expect(Slug.for("hello...")).to eq("hello") end it 'strips leading punctuation' do - Slug.for("...hello").should == "hello" + expect(Slug.for("...hello")).to eq("hello") end it 'handles our initial transliteration' do from = "àáäâčďèéëěêìíïîľĺňòóöôŕřšťůùúüûýžñç" to = "aaaacdeeeeeiiiillnoooorrstuuuuuyznc" - Slug.for(from).should == to + expect(Slug.for(from)).to eq(to) end it 'replaces underscores' do - Slug.for("o_o_o").should == "o-o-o" + expect(Slug.for("o_o_o")).to eq("o-o-o") end it "doesn't generate slugs that are just numbers" do - Slug.for('123').should be_blank + expect(Slug.for('123')).to be_blank end it "doesn't generate slugs that are just numbers" do - Slug.for('2').should be_blank + expect(Slug.for('2')).to be_blank end it "doesn't keep single quotes within word" do - Slug.for("Jeff hate's this").should == "jeff-hates-this" + expect(Slug.for("Jeff hate's this")).to eq("jeff-hates-this") end it "translate the chineses" do SiteSetting.default_locale = 'zh_CN' - Slug.for("习近平:中企承建港口电站等助斯里兰卡发展").should == "xi-jin-ping-zhong-qi-cheng-jian-gang-kou-dian-zhan-deng-zhu-si-li-lan-qia-fa-zhan" + expect(Slug.for("习近平:中企承建港口电站等助斯里兰卡发展")).to eq("xi-jin-ping-zhong-qi-cheng-jian-gang-kou-dian-zhan-deng-zhu-si-li-lan-qia-fa-zhan") end end diff --git a/spec/components/spam_handler_spec.rb b/spec/components/spam_handler_spec.rb index 1f6c02770b..325932b9dc 100644 --- a/spec/components/spam_handler_spec.rb +++ b/spec/components/spam_handler_spec.rb @@ -19,7 +19,7 @@ describe SpamHandler do Fabricate(:user, ip_address: "42.42.42.42", trust_level: TrustLevel[0]) Fabricate(:user, ip_address: "42.42.42.42", trust_level: TrustLevel[1]) - -> { Fabricate(:user, ip_address: "42.42.42.42", trust_level: TrustLevel[0]) }.should raise_error(ActiveRecord::RecordInvalid) + expect { Fabricate(:user, ip_address: "42.42.42.42", trust_level: TrustLevel[0]) }.to raise_error(ActiveRecord::RecordInvalid) end it "doesn't limit registrations since there is a TL2+ user with that IP" do diff --git a/spec/components/sql_builder_spec.rb b/spec/components/sql_builder_spec.rb index d0555ef10c..72926085d2 100644 --- a/spec/components/sql_builder_spec.rb +++ b/spec/components/sql_builder_spec.rb @@ -13,8 +13,8 @@ describe SqlBuilder do p = Fabricate(:post) @builder.where('id = :id and topic_id = :topic_id', id: p.id, topic_id: p.topic_id) p2 = @builder.exec.first - p2.id.should == p.id - p2.should == p + expect(p2.id).to eq(p.id) + expect(p2).to eq(p) end end @@ -32,13 +32,13 @@ describe SqlBuilder do true AS bool") .map_exec(SqlBuilder::TestClass) - rows.count.should == 1 + expect(rows.count).to eq(1) row = rows[0] - row.int.should == 1 - row.string.should == "string" - row.text.should == "text" - row.bool.should == true - row.date.should be_within(10.seconds).of(DateTime.now) + expect(row.int).to eq(1) + expect(row.string).to eq("string") + expect(row.text).to eq("text") + expect(row.bool).to eq(true) + expect(row.date).to be_within(10.seconds).of(DateTime.now) end end @@ -48,27 +48,27 @@ describe SqlBuilder do end it "should allow for 1 param exec" do - @builder.exec(a: 1, b: 2).values[0][0].should == '1' + expect(@builder.exec(a: 1, b: 2).values[0][0]).to eq('1') end it "should allow for a single where" do @builder.where(":a = 1") - @builder.exec(a: 1, b: 2).values[0][0].should == '1' + expect(@builder.exec(a: 1, b: 2).values[0][0]).to eq('1') end it "should allow where chaining" do @builder.where(":a = 1") @builder.where("2 = 1") - @builder.exec(a: 1, b: 2).to_a.length.should == 0 + expect(@builder.exec(a: 1, b: 2).to_a.length).to eq(0) end it "should allow order by" do - @builder.order_by("A desc").limit(1) - .exec(a:1, b:2).values[0][0].should == "2" + expect(@builder.order_by("A desc").limit(1) + .exec(a:1, b:2).values[0][0]).to eq("2") end it "should allow offset" do - @builder.order_by("A desc").offset(1) - .exec(a:1, b:2).values[0][0].should == "1" + expect(@builder.order_by("A desc").offset(1) + .exec(a:1, b:2).values[0][0]).to eq("1") end end diff --git a/spec/components/suggested_topics_builder_spec.rb b/spec/components/suggested_topics_builder_spec.rb index 86c8855445..414185582a 100644 --- a/spec/components/suggested_topics_builder_spec.rb +++ b/spec/components/suggested_topics_builder_spec.rb @@ -25,44 +25,44 @@ describe SuggestedTopicsBuilder do builder.splice_results([fake_topic(3,1)], :high) builder.splice_results([fake_topic(4,1)], :high) - builder.results.map(&:id).should == [3,4,2] + expect(builder.results.map(&:id)).to eq([3,4,2]) # we have 2 items in category 1 - builder.category_results_left.should == 3 + expect(builder.category_results_left).to eq(3) end it "inserts using default approach for non high priority" do builder.splice_results([fake_topic(2,2)], :high) builder.splice_results([fake_topic(3,1)], :low) - builder.results.map(&:id).should == [2,3] + expect(builder.results.map(&:id)).to eq([2,3]) end it "inserts multiple results and puts topics in the correct order" do builder.splice_results([fake_topic(2,1), fake_topic(3,2), fake_topic(4,1)], :high) - builder.results.map(&:id).should == [2,4,3] + expect(builder.results.map(&:id)).to eq([2,4,3]) end end it "has the correct defaults" do - builder.excluded_topic_ids.include?(topic.id).should == true - builder.results_left.should == 5 - builder.size.should == 0 - builder.should_not be_full + expect(builder.excluded_topic_ids.include?(topic.id)).to eq(true) + expect(builder.results_left).to eq(5) + expect(builder.size).to eq(0) + expect(builder).not_to be_full end it "returns full correctly" do builder.stubs(:results_left).returns(0) - builder.should be_full + expect(builder).to be_full end context "adding results" do it "adds nothing with nil results" do builder.add_results(nil) - builder.results_left.should == 5 - builder.size.should == 0 - builder.should_not be_full + expect(builder.results_left).to eq(5) + expect(builder.size).to eq(0) + expect(builder).not_to be_full end context "adding topics" do @@ -74,11 +74,11 @@ describe SuggestedTopicsBuilder do end it "added the result correctly" do - builder.size.should == 1 - builder.results_left.should == 4 - builder.should_not be_full - builder.excluded_topic_ids.include?(topic.id).should == true - builder.excluded_topic_ids.include?(other_topic.id).should == true + expect(builder.size).to eq(1) + expect(builder.results_left).to eq(4) + expect(builder).not_to be_full + expect(builder.excluded_topic_ids.include?(topic.id)).to eq(true) + expect(builder.excluded_topic_ids.include?(other_topic.id)).to eq(true) end end @@ -90,8 +90,8 @@ describe SuggestedTopicsBuilder do it "adds archived and closed, but not invisible topics" do builder.add_results(Topic) - builder.size.should == 2 - builder.should_not be_full + expect(builder.size).to eq(2) + expect(builder).not_to be_full end end @@ -99,10 +99,10 @@ describe SuggestedTopicsBuilder do let!(:category) { Fabricate(:category) } it "doesn't add a category definition topic" do - category.topic_id.should be_present + expect(category.topic_id).to be_present builder.add_results(Topic) - builder.size.should == 0 - builder.should_not be_full + expect(builder.size).to eq(0) + expect(builder).not_to be_full end end diff --git a/spec/components/summarize_spec.rb b/spec/components/summarize_spec.rb deleted file mode 100644 index d0f502dde1..0000000000 --- a/spec/components/summarize_spec.rb +++ /dev/null @@ -1,32 +0,0 @@ -require 'spec_helper' -require 'summarize' - -describe Summarize do - - it "is blank when the input is nil" do - Summarize.new(nil).summary.should be_blank - end - - it "is blank when the input is an empty string" do - Summarize.new("").summary.should be_blank - end - - it "removes html tags" do - Summarize.new("hello robin").summary.should == "hello robin" - end - - it "removes doctype entries" do - # this is not valid html but this is just testing DOCTYPE entries - Summarize.new("Discourse").summary.should == "Discourse" - end - - it "strips leading and trailing space" do - Summarize.new("\t \t hello \t ").summary.should == "hello" - end - - it "trims long strings and adds an ellipsis" do - Summarize.stubs(:max_length).returns(11) - Summarize.new("discourse is a cool forum").summary.should == "discourse is..." - end - -end diff --git a/spec/components/system_message_spec.rb b/spec/components/system_message_spec.rb index 71426e6f23..fe1eab9358 100644 --- a/spec/components/system_message_spec.rb +++ b/spec/components/system_message_spec.rb @@ -14,12 +14,12 @@ describe SystemMessage do let(:topic) { post.topic } it 'should create a post correctly' do - post.should be_present - post.should be_valid - topic.should be_private_message - topic.should be_valid - topic.subtype.should == TopicSubtype.system_message - topic.allowed_users.include?(user).should == true + expect(post).to be_present + expect(post).to be_valid + expect(topic).to be_private_message + expect(topic).to be_valid + expect(topic.subtype).to eq(TopicSubtype.system_message) + expect(topic.allowed_users.include?(user)).to eq(true) end end diff --git a/spec/components/text_cleaner_spec.rb b/spec/components/text_cleaner_spec.rb index 0603ba3d09..edcdccca6b 100644 --- a/spec/components/text_cleaner_spec.rb +++ b/spec/components/text_cleaner_spec.rb @@ -9,11 +9,11 @@ describe TextCleaner do let(:deduplicated_string) { "my precious!" } it "ignores multiple ! by default" do - TextCleaner.clean(duplicated_string).should == duplicated_string + expect(TextCleaner.clean(duplicated_string)).to eq(duplicated_string) end it "deduplicates ! when enabled" do - TextCleaner.clean(duplicated_string, deduplicate_exclamation_marks: true).should == deduplicated_string + expect(TextCleaner.clean(duplicated_string, deduplicate_exclamation_marks: true)).to eq(deduplicated_string) end end @@ -24,11 +24,11 @@ describe TextCleaner do let(:deduplicated_string) { "please help me?" } it "ignores multiple ? by default" do - TextCleaner.clean(duplicated_string).should == duplicated_string + expect(TextCleaner.clean(duplicated_string)).to eq(duplicated_string) end it "deduplicates ? when enabled" do - TextCleaner.clean(duplicated_string, deduplicate_question_marks: true).should == deduplicated_string + expect(TextCleaner.clean(duplicated_string, deduplicate_question_marks: true)).to eq(deduplicated_string) end end @@ -40,15 +40,15 @@ describe TextCleaner do let(:regular_case) { "entire text is all caps" } it "ignores all upper case text by default" do - TextCleaner.clean(all_caps).should == all_caps + expect(TextCleaner.clean(all_caps)).to eq(all_caps) end it "replaces all upper case text with regular case letters when enabled" do - TextCleaner.clean(all_caps, replace_all_upper_case: true).should == regular_case + expect(TextCleaner.clean(all_caps, replace_all_upper_case: true)).to eq(regular_case) end it "ignores almost all upper case text when enabled" do - TextCleaner.clean(almost_all_caps, replace_all_upper_case: true).should == almost_all_caps + expect(TextCleaner.clean(almost_all_caps, replace_all_upper_case: true)).to eq(almost_all_caps) end end @@ -60,15 +60,15 @@ describe TextCleaner do let(:iletter) { "iLetter" } it "ignores first letter case by default" do - TextCleaner.clean(lowercased).should == lowercased - TextCleaner.clean(capitalized).should == capitalized - TextCleaner.clean(iletter).should == iletter + expect(TextCleaner.clean(lowercased)).to eq(lowercased) + expect(TextCleaner.clean(capitalized)).to eq(capitalized) + expect(TextCleaner.clean(iletter)).to eq(iletter) end it "capitalizes first letter when enabled" do - TextCleaner.clean(lowercased, capitalize_first_letter: true).should == capitalized - TextCleaner.clean(capitalized, capitalize_first_letter: true).should == capitalized - TextCleaner.clean(iletter, capitalize_first_letter: true).should == iletter + expect(TextCleaner.clean(lowercased, capitalize_first_letter: true)).to eq(capitalized) + expect(TextCleaner.clean(capitalized, capitalize_first_letter: true)).to eq(capitalized) + expect(TextCleaner.clean(iletter, capitalize_first_letter: true)).to eq(iletter) end end @@ -80,17 +80,17 @@ describe TextCleaner do let(:without_period) { "oops" } it "ignores unnecessary periods at the end by default" do - TextCleaner.clean(with_one_period).should == with_one_period - TextCleaner.clean(with_several_periods).should == with_several_periods + expect(TextCleaner.clean(with_one_period)).to eq(with_one_period) + expect(TextCleaner.clean(with_several_periods)).to eq(with_several_periods) end it "removes unnecessary periods at the end when enabled" do - TextCleaner.clean(with_one_period, remove_all_periods_from_the_end: true).should == without_period - TextCleaner.clean(with_several_periods, remove_all_periods_from_the_end: true).should == without_period + expect(TextCleaner.clean(with_one_period, remove_all_periods_from_the_end: true)).to eq(without_period) + expect(TextCleaner.clean(with_several_periods, remove_all_periods_from_the_end: true)).to eq(without_period) end it "keeps trailing whitespaces when enabled" do - TextCleaner.clean(with_several_periods + " ", remove_all_periods_from_the_end: true).should == without_period + " " + expect(TextCleaner.clean(with_several_periods + " ", remove_all_periods_from_the_end: true)).to eq(without_period + " ") end end @@ -103,18 +103,18 @@ describe TextCleaner do let(:without_space_question) { "oops?" } it "ignores extraneous space before the end punctuation by default" do - TextCleaner.clean(with_space_exclamation).should == with_space_exclamation - TextCleaner.clean(with_space_question).should == with_space_question + expect(TextCleaner.clean(with_space_exclamation)).to eq(with_space_exclamation) + expect(TextCleaner.clean(with_space_question)).to eq(with_space_question) end it "removes extraneous space before the end punctuation when enabled" do - TextCleaner.clean(with_space_exclamation, remove_extraneous_space: true).should == without_space_exclamation - TextCleaner.clean(with_space_question, remove_extraneous_space: true).should == without_space_question + expect(TextCleaner.clean(with_space_exclamation, remove_extraneous_space: true)).to eq(without_space_exclamation) + expect(TextCleaner.clean(with_space_question, remove_extraneous_space: true)).to eq(without_space_question) end it "keep trailing whitespaces when enabled" do - TextCleaner.clean(with_space_exclamation + " ", remove_extraneous_space: true).should == without_space_exclamation + " " - TextCleaner.clean(with_space_question + " ", remove_extraneous_space: true).should == without_space_question + " " + expect(TextCleaner.clean(with_space_exclamation + " ", remove_extraneous_space: true)).to eq(without_space_exclamation + " ") + expect(TextCleaner.clean(with_space_question + " ", remove_extraneous_space: true)).to eq(without_space_question + " ") end end @@ -125,11 +125,11 @@ describe TextCleaner do let(:unspacey_string) { "hello there's weird spaces here." } it "ignores interior spaces by default" do - TextCleaner.clean(spacey_string).should == spacey_string + expect(TextCleaner.clean(spacey_string)).to eq(spacey_string) end it "fixes interior spaces when enabled" do - TextCleaner.clean(spacey_string, fixes_interior_spaces: true).should == unspacey_string + expect(TextCleaner.clean(spacey_string, fixes_interior_spaces: true)).to eq(unspacey_string) end end @@ -140,11 +140,11 @@ describe TextCleaner do let(:unspacey_string) { "test" } it "ignores leading and trailing whitespaces by default" do - TextCleaner.clean(spacey_string).should == spacey_string + expect(TextCleaner.clean(spacey_string)).to eq(spacey_string) end it "strips leading and trailing whitespaces when enabled" do - TextCleaner.clean(spacey_string, strip_whitespaces: true).should == unspacey_string + expect(TextCleaner.clean(spacey_string, strip_whitespaces: true)).to eq(unspacey_string) end end @@ -152,11 +152,11 @@ describe TextCleaner do context "title" do it "fixes interior spaces" do - TextCleaner.clean_title("Hello there").should == "Hello there" + expect(TextCleaner.clean_title("Hello there")).to eq("Hello there") end it "strips leading and trailing whitespaces" do - TextCleaner.clean_title(" \t Hello there \n ").should == "Hello there" + expect(TextCleaner.clean_title(" \t Hello there \n ")).to eq("Hello there") end context "title_prettify site setting is enabled" do @@ -164,27 +164,27 @@ describe TextCleaner do before { SiteSetting.title_prettify = true } it "deduplicates !" do - TextCleaner.clean_title("Hello there!!!!").should == "Hello there!" + expect(TextCleaner.clean_title("Hello there!!!!")).to eq("Hello there!") end it "deduplicates ?" do - TextCleaner.clean_title("Hello there????").should == "Hello there?" + expect(TextCleaner.clean_title("Hello there????")).to eq("Hello there?") end it "replaces all upper case text with regular case letters" do - TextCleaner.clean_title("HELLO THERE").should == "Hello there" + expect(TextCleaner.clean_title("HELLO THERE")).to eq("Hello there") end it "capitalizes first letter" do - TextCleaner.clean_title("hello there").should == "Hello there" + expect(TextCleaner.clean_title("hello there")).to eq("Hello there") end it "removes unnecessary period at the end" do - TextCleaner.clean_title("Hello there.").should == "Hello there" + expect(TextCleaner.clean_title("Hello there.")).to eq("Hello there") end it "removes extraneous space before the end punctuation" do - TextCleaner.clean_title("Hello there ?").should == "Hello there?" + expect(TextCleaner.clean_title("Hello there ?")).to eq("Hello there?") end end @@ -194,8 +194,8 @@ describe TextCleaner do describe "#normalize_whitespaces" do it "normalize whitespaces" do whitespaces = "\u0020\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u200B\u2028\u2029\u202F\u205F\u3000\uFEFF" - whitespaces.strip.should_not == "" - TextCleaner.normalize_whitespaces(whitespaces).strip.should == "" + expect(whitespaces.strip).not_to eq("") + expect(TextCleaner.normalize_whitespaces(whitespaces).strip).to eq("") end end diff --git a/spec/components/text_sentinel_spec.rb b/spec/components/text_sentinel_spec.rb index 5c6f903fa6..ce19e41623 100644 --- a/spec/components/text_sentinel_spec.rb +++ b/spec/components/text_sentinel_spec.rb @@ -6,41 +6,41 @@ require 'text_sentinel' describe TextSentinel do it "allows utf-8 chars" do - TextSentinel.new("йȝîûηыეமிᚉ⠛").text.should == "йȝîûηыეமிᚉ⠛" + expect(TextSentinel.new("йȝîûηыეமிᚉ⠛").text).to eq("йȝîûηыეமிᚉ⠛") end context "entropy" do it "returns 0 for an empty string" do - TextSentinel.new("").entropy.should == 0 + expect(TextSentinel.new("").entropy).to eq(0) end it "returns 0 for a nil string" do - TextSentinel.new(nil).entropy.should == 0 + expect(TextSentinel.new(nil).entropy).to eq(0) end it "returns 1 for a string with many leading spaces" do - TextSentinel.new((" " * 10) + "x").entropy.should == 1 + expect(TextSentinel.new((" " * 10) + "x").entropy).to eq(1) end it "returns 1 for one char, even repeated" do - TextSentinel.new("a" * 10).entropy.should == 1 + expect(TextSentinel.new("a" * 10).entropy).to eq(1) end it "returns an accurate count of many chars" do - TextSentinel.new("evil trout is evil").entropy.should == 10 + expect(TextSentinel.new("evil trout is evil").entropy).to eq(10) end it "Works on foreign characters" do - TextSentinel.new("去年十社會警告").entropy.should == 19 + expect(TextSentinel.new("去年十社會警告").entropy).to eq(19) end it "generates enough entropy for short foreign strings" do - TextSentinel.new("又一个测").entropy.should == 11 + expect(TextSentinel.new("又一个测").entropy).to eq(11) end it "handles repeated foreign characters" do - TextSentinel.new("又一个测试话题" * 3).entropy.should == 18 + expect(TextSentinel.new("又一个测试话题" * 3).entropy).to eq(18) end end @@ -67,41 +67,41 @@ describe TextSentinel do let(:valid_string) { "This is a cool topic about Discourse" } it "allows a valid string" do - TextSentinel.new(valid_string).should be_valid + expect(TextSentinel.new(valid_string)).to be_valid end it "doesn't allow all caps topics" do - TextSentinel.new(valid_string.upcase).should_not be_valid + expect(TextSentinel.new(valid_string.upcase)).not_to be_valid end it "enforces the minimum entropy" do - TextSentinel.new(valid_string, min_entropy: 16).should be_valid + expect(TextSentinel.new(valid_string, min_entropy: 16)).to be_valid end it "enforces the minimum entropy" do - TextSentinel.new(valid_string, min_entropy: 17).should_not be_valid + expect(TextSentinel.new(valid_string, min_entropy: 17)).not_to be_valid end it "allows all foreign characters" do - TextSentinel.new("去年十二月,北韓不顧國際社會警告").should be_valid + expect(TextSentinel.new("去年十二月,北韓不顧國際社會警告")).to be_valid end it "doesn't allow a long alphanumeric string with no spaces" do - TextSentinel.new("jfewjfoejwfojeojfoejofjeo3" * 5, max_word_length: 30).should_not be_valid + expect(TextSentinel.new("jfewjfoejwfojeojfoejofjeo3" * 5, max_word_length: 30)).not_to be_valid end it "doesn't accept junk symbols as a string" do - TextSentinel.new("[[[").should_not be_valid - TextSentinel.new("<<<").should_not be_valid - TextSentinel.new("{{$!").should_not be_valid + expect(TextSentinel.new("[[[")).not_to be_valid + expect(TextSentinel.new("<<<")).not_to be_valid + expect(TextSentinel.new("{{$!")).not_to be_valid end it "does allow a long alphanumeric string joined with slashes" do - TextSentinel.new("gdfgdfgdfg/fgdfgdfgdg/dfgdfgdfgd/dfgdfgdfgf", max_word_length: 30).should be_valid + expect(TextSentinel.new("gdfgdfgdfg/fgdfgdfgdg/dfgdfgdfgd/dfgdfgdfgf", max_word_length: 30)).to be_valid end it "does allow a long alphanumeric string joined with dashes" do - TextSentinel.new("gdfgdfgdfg-fgdfgdfgdg-dfgdfgdfgd-dfgdfgdfgf", max_word_length: 30).should be_valid + expect(TextSentinel.new("gdfgdfgdfg-fgdfgdfgdg-dfgdfgdfgd-dfgdfgdfgf", max_word_length: 30)).to be_valid end end @@ -111,7 +111,7 @@ describe TextSentinel do it "uses a sensible min entropy value when min title length is less than title_min_entropy" do SiteSetting.stubs(:min_topic_title_length).returns(3) SiteSetting.stubs(:title_min_entropy).returns(10) - TextSentinel.title_sentinel('Hey').should be_valid + expect(TextSentinel.title_sentinel('Hey')).to be_valid end end @@ -121,14 +121,14 @@ describe TextSentinel do it "uses a sensible min entropy value when min body length is less than min entropy" do SiteSetting.stubs(:min_post_length).returns(3) SiteSetting.stubs(:body_min_entropy).returns(7) - TextSentinel.body_sentinel('Yup').should be_valid + expect(TextSentinel.body_sentinel('Yup')).to be_valid end it "uses a sensible min entropy value when min pm body length is less than min entropy" do SiteSetting.stubs(:min_post_length).returns(5) SiteSetting.stubs(:min_private_message_post_length).returns(3) SiteSetting.stubs(:body_min_entropy).returns(7) - TextSentinel.body_sentinel('Lol', private_message: true).should be_valid + expect(TextSentinel.body_sentinel('Lol', private_message: true)).to be_valid end end diff --git a/spec/components/topic_creator_spec.rb b/spec/components/topic_creator_spec.rb index 3370084e28..c858ebaa1b 100644 --- a/spec/components/topic_creator_spec.rb +++ b/spec/components/topic_creator_spec.rb @@ -17,35 +17,35 @@ describe TopicCreator do end it "should be possible for an admin to create a topic" do - TopicCreator.create(admin, Guardian.new(admin), valid_attrs).should be_valid + expect(TopicCreator.create(admin, Guardian.new(admin), valid_attrs)).to be_valid end it "should be possible for a moderator to create a topic" do - TopicCreator.create(moderator, Guardian.new(moderator), valid_attrs).should be_valid + expect(TopicCreator.create(moderator, Guardian.new(moderator), valid_attrs)).to be_valid end context 'regular user' do before { SiteSetting.stubs(:min_trust_to_create_topic).returns(TrustLevel[0]) } it "should be possible for a regular user to create a topic" do - TopicCreator.create(user, Guardian.new(user), valid_attrs).should be_valid + expect(TopicCreator.create(user, Guardian.new(user), valid_attrs)).to be_valid end it "should be possible for a regular user to create a topic with blank auto_close_time" do - TopicCreator.create(user, Guardian.new(user), valid_attrs.merge(auto_close_time: '')).should be_valid + expect(TopicCreator.create(user, Guardian.new(user), valid_attrs.merge(auto_close_time: ''))).to be_valid end it "ignores auto_close_time without raising an error" do topic = TopicCreator.create(user, Guardian.new(user), valid_attrs.merge(auto_close_time: '24')) - topic.should be_valid - topic.auto_close_at.should == nil + expect(topic).to be_valid + expect(topic.auto_close_at).to eq(nil) end it "category name is case insensitive" do category = Fabricate(:category, name: "Neil's Blog") topic = TopicCreator.create(user, Guardian.new(user), valid_attrs.merge(category: "neil's blog")) - topic.should be_valid - topic.category.should == category + expect(topic).to be_valid + expect(topic.category).to eq(category) end end end diff --git a/spec/components/topic_query_spec.rb b/spec/components/topic_query_spec.rb index 1774a89035..eafd8a5b79 100644 --- a/spec/components/topic_query_spec.rb +++ b/spec/components/topic_query_spec.rb @@ -20,53 +20,83 @@ describe TopicQuery do topic = Fabricate(:topic, category: category) topic = Fabricate(:topic, visible: false) - TopicQuery.new(nil).list_latest.topics.count.should == 0 - TopicQuery.new(user).list_latest.topics.count.should == 0 + expect(TopicQuery.new(nil).list_latest.topics.count).to eq(0) + expect(TopicQuery.new(user).list_latest.topics.count).to eq(0) - Topic.top_viewed(10).count.should == 0 - Topic.recent(10).count.should == 0 + expect(Topic.top_viewed(10).count).to eq(0) + expect(Topic.recent(10).count).to eq(0) # mods can see hidden topics - TopicQuery.new(moderator).list_latest.topics.count.should == 1 + expect(TopicQuery.new(moderator).list_latest.topics.count).to eq(1) # admins can see all the topics - TopicQuery.new(admin).list_latest.topics.count.should == 3 + expect(TopicQuery.new(admin).list_latest.topics.count).to eq(3) group.add(user) group.save - TopicQuery.new(user).list_latest.topics.count.should == 2 + expect(TopicQuery.new(user).list_latest.topics.count).to eq(2) end end + context 'bookmarks' do + it "filters and returns bookmarks correctly" do + post = Fabricate(:post) + reply = Fabricate(:post, topic_id: post.topic_id) + + post2 = Fabricate(:post) + + PostAction.act(user, post, PostActionType.types[:bookmark]) + PostAction.act(user, reply, PostActionType.types[:bookmark]) + TopicUser.change(user, post.topic, notification_level: 1) + TopicUser.change(user, post2.topic, notification_level: 1) + + query = TopicQuery.new(user, filter: 'bookmarked').list_latest + + expect(query.topics.length).to eq(1) + expect(query.topics.first.user_data.post_action_data).to eq({PostActionType.types[:bookmark] => [1,2]}) + end + end + + context 'deleted filter' do + it "filters deleted topics correctly" do + _topic = Fabricate(:topic, deleted_at: 1.year.ago) + + expect(TopicQuery.new(admin, status: 'deleted').list_latest.topics.size).to eq(1) + expect(TopicQuery.new(moderator, status: 'deleted').list_latest.topics.size).to eq(1) + expect(TopicQuery.new(user, status: 'deleted').list_latest.topics.size).to eq(0) + expect(TopicQuery.new(nil, status: 'deleted').list_latest.topics.size).to eq(0) + end + end + context 'category filter' do let(:category) { Fabricate(:category) } let(:diff_category) { Fabricate(:diff_category) } it "returns topics in the category when we filter to it" do - TopicQuery.new(moderator).list_latest.topics.size.should == 0 + expect(TopicQuery.new(moderator).list_latest.topics.size).to eq(0) # Filter by slug - TopicQuery.new(moderator, category: category.slug).list_latest.topics.size.should == 1 - TopicQuery.new(moderator, category: "#{category.id}-category").list_latest.topics.size.should == 1 + expect(TopicQuery.new(moderator, category: category.slug).list_latest.topics.size).to eq(1) + expect(TopicQuery.new(moderator, category: "#{category.id}-category").list_latest.topics.size).to eq(1) list = TopicQuery.new(moderator, category: diff_category.slug).list_latest - list.topics.size.should == 1 - list.preload_key.should == "topic_list_category/different-category/l/latest" + expect(list.topics.size).to eq(1) + expect(list.preload_key).to eq("topic_list_c/different-category/l/latest") # Defaults to no category filter when slug does not exist - TopicQuery.new(moderator, category: 'made up slug').list_latest.topics.size.should == 2 + expect(TopicQuery.new(moderator, category: 'made up slug').list_latest.topics.size).to eq(2) end context 'subcategories' do let!(:subcategory) { Fabricate(:category, parent_category_id: category.id)} it "works with subcategories" do - TopicQuery.new(moderator, category: category.id).list_latest.topics.size.should == 1 - TopicQuery.new(moderator, category: subcategory.id).list_latest.topics.size.should == 1 - TopicQuery.new(moderator, category: category.id, no_subcategories: true).list_latest.topics.size.should == 1 + expect(TopicQuery.new(moderator, category: category.id).list_latest.topics.size).to eq(1) + expect(TopicQuery.new(moderator, category: subcategory.id).list_latest.topics.size).to eq(1) + expect(TopicQuery.new(moderator, category: category.id, no_subcategories: true).list_latest.topics.size).to eq(1) end end @@ -81,8 +111,8 @@ describe TopicQuery do CategoryUser.create!(user_id: user.id, category_id: category.id, notification_level: CategoryUser.notification_levels[:muted]) - topic_query.list_new.topics.map(&:id).should_not include(topic.id) - topic_query.list_latest.topics.map(&:id).should_not include(topic.id) + expect(topic_query.list_new.topics.map(&:id)).not_to include(topic.id) + expect(topic_query.list_latest.topics.map(&:id)).not_to include(topic.id) end end @@ -151,13 +181,13 @@ describe TopicQuery do context 'list_latest' do it "returns the topics in the correct order" do - topics.map(&:id).should == [pinned_topic, future_topic, closed_topic, archived_topic, regular_topic].map(&:id) + expect(topics.map(&:id)).to eq([pinned_topic, future_topic, closed_topic, archived_topic, regular_topic].map(&:id)) # includes the invisible topic if you're a moderator - TopicQuery.new(moderator).list_latest.topics.include?(invisible_topic).should == true + expect(TopicQuery.new(moderator).list_latest.topics.include?(invisible_topic)).to eq(true) # includes the invisible topic if you're an admin" do - TopicQuery.new(admin).list_latest.topics.include?(invisible_topic).should == true + expect(TopicQuery.new(admin).list_latest.topics.include?(invisible_topic)).to eq(true) end context 'sort_order' do @@ -168,28 +198,28 @@ describe TopicQuery do it "returns the topics in correct order" do # returns the topics in likes order if requested - ids_in_order('posts').should == [future_topic, pinned_topic, archived_topic, regular_topic, invisible_topic, closed_topic].map(&:id) + expect(ids_in_order('posts')).to eq([future_topic, pinned_topic, archived_topic, regular_topic, invisible_topic, closed_topic].map(&:id)) # returns the topics in reverse likes order if requested - ids_in_order('posts', false).should == [closed_topic, invisible_topic, regular_topic, archived_topic, pinned_topic, future_topic].map(&:id) + expect(ids_in_order('posts', false)).to eq([closed_topic, invisible_topic, regular_topic, archived_topic, pinned_topic, future_topic].map(&:id)) # returns the topics in likes order if requested - ids_in_order('likes').should == [pinned_topic, regular_topic, archived_topic, future_topic, invisible_topic, closed_topic].map(&:id) + expect(ids_in_order('likes')).to eq([pinned_topic, regular_topic, archived_topic, future_topic, invisible_topic, closed_topic].map(&:id)) # returns the topics in reverse likes order if requested - ids_in_order('likes', false).should == [closed_topic, invisible_topic, future_topic, archived_topic, regular_topic, pinned_topic].map(&:id) + expect(ids_in_order('likes', false)).to eq([closed_topic, invisible_topic, future_topic, archived_topic, regular_topic, pinned_topic].map(&:id)) # returns the topics in views order if requested - ids_in_order('views').should == [regular_topic, archived_topic, future_topic, pinned_topic, closed_topic, invisible_topic].map(&:id) + expect(ids_in_order('views')).to eq([regular_topic, archived_topic, future_topic, pinned_topic, closed_topic, invisible_topic].map(&:id)) # returns the topics in reverse views order if requested" do - ids_in_order('views', false).should == [invisible_topic, closed_topic, pinned_topic, future_topic, archived_topic, regular_topic].map(&:id) + expect(ids_in_order('views', false)).to eq([invisible_topic, closed_topic, pinned_topic, future_topic, archived_topic, regular_topic].map(&:id)) # returns the topics in posters order if requested" do - ids_in_order('posters').should == [pinned_topic, regular_topic, future_topic, invisible_topic, closed_topic, archived_topic].map(&:id) + expect(ids_in_order('posters')).to eq([pinned_topic, regular_topic, future_topic, invisible_topic, closed_topic, archived_topic].map(&:id)) # returns the topics in reverse posters order if requested" do - ids_in_order('posters', false).should == [archived_topic, closed_topic, invisible_topic, future_topic, regular_topic, pinned_topic].map(&:id) + expect(ids_in_order('posters', false)).to eq([archived_topic, closed_topic, invisible_topic, future_topic, regular_topic, pinned_topic].map(&:id)) end end @@ -203,7 +233,7 @@ describe TopicQuery do end it "no longer shows the pinned topic at the top" do - topics.should == [future_topic, closed_topic, archived_topic, pinned_topic, regular_topic] + expect(topics).to eq([future_topic, closed_topic, archived_topic, pinned_topic, regular_topic]) end end @@ -216,17 +246,17 @@ describe TopicQuery do let!(:topic_in_cat) { Fabricate(:topic, category: category) } it "returns the topic with a category when filtering by category" do - topic_query.list_category(category).topics.should == [topic_category, topic_in_cat] + expect(topic_query.list_category(category).topics).to eq([topic_category, topic_in_cat]) end it "returns only the topic category when filtering by another category" do another_category = Fabricate(:category, name: 'new cat') - topic_query.list_category(another_category).topics.should == [another_category.topic] + expect(topic_query.list_category(another_category).topics).to eq([another_category.topic]) end describe '#list_new_in_category' do it 'returns the topic category and the categorized topic' do - topic_query.list_new_in_category(category).topics.should == [topic_in_cat, topic_category] + expect(topic_query.list_new_in_category(category).topics).to eq([topic_in_cat, topic_category]) end end end @@ -235,7 +265,7 @@ describe TopicQuery do context 'with no data' do it "has no unread topics" do - topic_query.list_unread.topics.should be_blank + expect(topic_query.list_unread.topics).to be_blank end end @@ -250,7 +280,7 @@ describe TopicQuery do context 'list_unread' do it 'contains no topics' do - topic_query.list_unread.topics.should == [] + expect(topic_query.list_unread.topics).to eq([]) end end @@ -261,45 +291,24 @@ describe TopicQuery do end it 'only contains the partially read topic' do - topic_query.list_unread.topics.should == [partially_read] + expect(topic_query.list_unread.topics).to eq([partially_read]) end end context 'list_read' do it 'contain both topics ' do - topic_query.list_read.topics.should =~ [fully_read, partially_read] + expect(topic_query.list_read.topics).to match_array([fully_read, partially_read]) end end end end - context 'list_starred' do - - let(:topic) { Fabricate(:topic) } - - it "returns no results when the user hasn't starred any topics" do - topic_query.list_starred.topics.should be_blank - end - - context 'with a starred topic' do - - before do - topic.toggle_star(user, true) - end - - it "returns the topic after it has been starred" do - topic_query.list_starred.topics.should == [topic] - end - end - - end - context 'list_new' do context 'without a new topic' do it "has no new topics" do - topic_query.list_new.topics.should be_blank + expect(topic_query.list_new.topics).to be_blank end end @@ -309,7 +318,7 @@ describe TopicQuery do it "contains the new topic" do - topics.should == [new_topic] + expect(topics).to eq([new_topic]) end it "contains no new topics for a user that has missed the window" do @@ -317,7 +326,7 @@ describe TopicQuery do user.save new_topic.created_at = 10.minutes.ago new_topic.save - topics.should == [] + expect(topics).to eq([]) end context "muted topics" do @@ -326,7 +335,7 @@ describe TopicQuery do end it "returns an empty set" do - topics.should be_blank + expect(topics).to be_blank end context 'un-muted' do @@ -335,7 +344,7 @@ describe TopicQuery do end it "returns the topic again" do - topics.should == [new_topic] + expect(topics).to eq([new_topic]) end end end @@ -347,14 +356,14 @@ describe TopicQuery do let(:topics) { topic_query.list_posted.topics } it "returns blank when there are no posted topics" do - topics.should be_blank + expect(topics).to be_blank end context 'created topics' do let!(:created_topic) { create_post(user: user).topic } it "includes the created topic" do - topics.include?(created_topic).should == true + expect(topics.include?(created_topic)).to eq(true) end end @@ -363,7 +372,7 @@ describe TopicQuery do let!(:your_post) { create_post(user: user, topic: other_users_topic )} it "includes the posted topic" do - topics.include?(other_users_topic).should == true + expect(topics.include?(other_users_topic)).to eq(true) end end @@ -371,32 +380,27 @@ describe TopicQuery do let(:other_users_topic) { create_post(user: creator).topic } it "does not include the topic" do - topics.should be_blank + expect(topics).to be_blank end context "but interacted with" do - it "is not included if starred" do - other_users_topic.toggle_star(user, true) - - topics.should be_blank - end it "is not included if read" do TopicUser.update_last_read(user, other_users_topic.id, 0, 0) - topics.should be_blank + expect(topics).to be_blank end it "is not included if muted" do other_users_topic.notify_muted!(user) - topics.should be_blank + expect(topics).to be_blank end it "is not included if tracking" do other_users_topic.notify_tracking!(user) - topics.should be_blank + expect(topics).to be_blank end end end @@ -409,7 +413,7 @@ describe TopicQuery do let!(:new_topic) { Fabricate(:post, user: creator).topic } it "should return the new topic" do - TopicQuery.new.list_suggested_for(topic).topics.should == [new_topic] + expect(TopicQuery.new.list_suggested_for(topic).topics).to eq([new_topic]) end end @@ -421,7 +425,7 @@ describe TopicQuery do let!(:invisible_topic) { Fabricate(:topic, user: creator, visible: false) } it "should omit the closed/archived/invisbiel topics from suggested" do - TopicQuery.new.list_suggested_for(topic).topics.should == [regular_topic] + expect(TopicQuery.new.list_suggested_for(topic).topics).to eq([regular_topic]) end end @@ -431,7 +435,7 @@ describe TopicQuery do let(:suggested_topics) { topic_query.list_suggested_for(topic).topics.map{|t| t.id} } it "should return empty results when there is nothing to find" do - suggested_topics.should be_blank + expect(suggested_topics).to be_blank end context 'with some existing topics' do @@ -459,24 +463,24 @@ describe TopicQuery do it "won't return new or fully read if there are enough partially read topics" do SiteSetting.stubs(:suggested_topics).returns(1) - suggested_topics.should == [partially_read.id] + expect(suggested_topics).to eq([partially_read.id]) end it "won't return fully read if there are enough partially read topics and new topics" do SiteSetting.stubs(:suggested_topics).returns(4) - suggested_topics[0].should == partially_read.id - suggested_topics[1,3].should include(new_topic.id) - suggested_topics[1,3].should include(closed_topic.id) - suggested_topics[1,3].should include(archived_topic.id) + expect(suggested_topics[0]).to eq(partially_read.id) + expect(suggested_topics[1,3]).to include(new_topic.id) + expect(suggested_topics[1,3]).to include(closed_topic.id) + expect(suggested_topics[1,3]).to include(archived_topic.id) end it "returns unread, then new, then random" do SiteSetting.stubs(:suggested_topics).returns(7) - suggested_topics[0].should == partially_read.id - suggested_topics[1,3].should include(new_topic.id) - suggested_topics[1,3].should include(closed_topic.id) - suggested_topics[1,3].should include(archived_topic.id) - suggested_topics[4].should == fully_read.id + expect(suggested_topics[0]).to eq(partially_read.id) + expect(suggested_topics[1,3]).to include(new_topic.id) + expect(suggested_topics[1,3]).to include(closed_topic.id) + expect(suggested_topics[1,3]).to include(archived_topic.id) + expect(suggested_topics[4]).to eq(fully_read.id) # random doesn't include closed and archived end diff --git a/spec/components/topic_view_spec.rb b/spec/components/topic_view_spec.rb index b25263bd9d..c10fc0128a 100644 --- a/spec/components/topic_view_spec.rb +++ b/spec/components/topic_view_spec.rb @@ -10,21 +10,31 @@ describe TopicView do let(:topic_view) { TopicView.new(topic.id, coding_horror) } it "raises a not found error if the topic doesn't exist" do - lambda { TopicView.new(1231232, coding_horror) }.should raise_error(Discourse::NotFound) + expect { TopicView.new(1231232, coding_horror) }.to raise_error(Discourse::NotFound) end it "raises an error if the user can't see the topic" do Guardian.any_instance.expects(:can_see?).with(topic).returns(false) - lambda { topic_view }.should raise_error(Discourse::InvalidAccess) + expect { topic_view }.to raise_error(Discourse::InvalidAccess) end it "handles deleted topics" do admin = Fabricate(:admin) topic.trash!(admin) - lambda { TopicView.new(topic.id, Fabricate(:user)) }.should raise_error(Discourse::NotFound) - lambda { TopicView.new(topic.id, admin) }.should_not raise_error + expect { TopicView.new(topic.id, Fabricate(:user)) }.to raise_error(Discourse::NotFound) + expect { TopicView.new(topic.id, admin) }.not_to raise_error end + context "chunk_size" do + it "returns `chunk_size` by default" do + expect(TopicView.new(topic.id, coding_horror).chunk_size).to eq(TopicView.chunk_size) + end + + it "returns `slow_chunk_size` when slow_platform is true" do + tv = TopicView.new(topic.id, coding_horror, slow_platform: true) + expect(tv.chunk_size).to eq(TopicView.slow_chunk_size) + end + end context "with a few sample posts" do let!(:p1) { Fabricate(:post, topic: topic, user: first_poster, percent_rank: 1 )} @@ -37,69 +47,69 @@ describe TopicView do it "it can find the best responses" do best2 = TopicView.new(topic.id, coding_horror, best: 2) - best2.posts.count.should == 2 - best2.posts[0].id.should == p2.id - best2.posts[1].id.should == p3.id + expect(best2.posts.count).to eq(2) + expect(best2.posts[0].id).to eq(p2.id) + expect(best2.posts[1].id).to eq(p3.id) topic.update_status('closed', true, Fabricate(:admin)) - topic.posts.count.should == 4 + expect(topic.posts.count).to eq(4) # should not get the status post best = TopicView.new(topic.id, nil, best: 99) - best.posts.count.should == 2 - best.filtered_post_ids.size.should == 3 - best.current_post_ids.should =~ [p2.id, p3.id] + expect(best.posts.count).to eq(2) + expect(best.filtered_post_ids.size).to eq(3) + expect(best.current_post_ids).to match_array([p2.id, p3.id]) # should get no results for trust level too low best = TopicView.new(topic.id, nil, best: 99, min_trust_level: coding_horror.trust_level + 1) - best.posts.count.should == 0 + expect(best.posts.count).to eq(0) # should filter out the posts with a score that is too low best = TopicView.new(topic.id, nil, best: 99, min_score: 99) - best.posts.count.should == 0 + expect(best.posts.count).to eq(0) # should filter out everything if min replies not met best = TopicView.new(topic.id, nil, best: 99, min_replies: 99) - best.posts.count.should == 0 + expect(best.posts.count).to eq(0) # should punch through posts if the score is high enough p2.update_column(:score, 100) best = TopicView.new(topic.id, nil, best: 99, bypass_trust_level_score: 100, min_trust_level: coding_horror.trust_level + 1) - best.posts.count.should == 1 + expect(best.posts.count).to eq(1) # 0 means ignore best = TopicView.new(topic.id, nil, best: 99, bypass_trust_level_score: 0, min_trust_level: coding_horror.trust_level + 1) - best.posts.count.should == 0 + expect(best.posts.count).to eq(0) # If we restrict to posts a moderator liked, return none best = TopicView.new(topic.id, nil, best: 99, only_moderator_liked: true) - best.posts.count.should == 0 + expect(best.posts.count).to eq(0) # It doesn't count likes from admins PostAction.act(admin, p3, PostActionType.types[:like]) best = TopicView.new(topic.id, nil, best: 99, only_moderator_liked: true) - best.posts.count.should == 0 + expect(best.posts.count).to eq(0) # It should find the post liked by the moderator PostAction.act(moderator, p2, PostActionType.types[:like]) best = TopicView.new(topic.id, nil, best: 99, only_moderator_liked: true) - best.posts.count.should == 1 + expect(best.posts.count).to eq(1) end it "raises NotLoggedIn if the user isn't logged in and is trying to view a private message" do Topic.any_instance.expects(:private_message?).returns(true) - lambda { TopicView.new(topic.id, nil) }.should raise_error(Discourse::NotLoggedIn) + expect { TopicView.new(topic.id, nil) }.to raise_error(Discourse::NotLoggedIn) end it "provides an absolute url" do - topic_view.absolute_url.should be_present + expect(topic_view.absolute_url).to be_present end it "provides a summary of the first post" do - topic_view.summary.should be_present + expect(topic_view.summary).to be_present end describe "#get_canonical_path" do @@ -113,13 +123,13 @@ describe TopicView do end it "generates canonical path correctly" do - TopicView.new(1234, user).canonical_path.should eql(path) - TopicView.new(1234, user, page: 5).canonical_path.should eql("/1234?page=5") + expect(TopicView.new(1234, user).canonical_path).to eql(path) + expect(TopicView.new(1234, user, page: 5).canonical_path).to eql("/1234?page=5") end it "generates a canonical correctly for paged results" do - SiteSetting.stubs(:posts_chunksize).returns(5) - TopicView.new(1234, user, post_number: 50).canonical_path.should eql("/1234?page=10") + expect(TopicView.new(1234, user, post_number: 10 * TopicView.chunk_size ) + .canonical_path).to eql("/1234?page=10") end end @@ -136,47 +146,53 @@ describe TopicView do TopicView.any_instance.expects(:find_topic).with(1234).returns(topic) TopicView.any_instance.stubs(:filter_posts) TopicView.any_instance.stubs(:last_post).returns(p2) - SiteSetting.stubs(:posts_chunksize).returns(2) + TopicView.stubs(:chunk_size).returns(2) end it "should return the next page" do - TopicView.new(1234, user).next_page.should eql(2) + expect(TopicView.new(1234, user).next_page).to eql(2) end end context '.post_counts_by_user' do it 'returns the two posters with their counts' do - topic_view.post_counts_by_user.to_a.should =~ [[first_poster.id, 2], [coding_horror.id, 1]] + expect(topic_view.post_counts_by_user.to_a).to match_array([[first_poster.id, 2], [coding_horror.id, 1]]) + end + + it "doesn't return counts for posts with authors who have been deleted" do + p2.user_id = nil + p2.save! + expect(topic_view.post_counts_by_user.to_a).to match_array([[first_poster.id, 2]]) end end context '.participants' do it 'returns the two participants hashed by id' do - topic_view.participants.to_a.should =~ [[first_poster.id, first_poster], [coding_horror.id, coding_horror]] + expect(topic_view.participants.to_a).to match_array([[first_poster.id, first_poster], [coding_horror.id, coding_horror]]) end end context '.all_post_actions' do it 'is blank at first' do - topic_view.all_post_actions.should be_blank + expect(topic_view.all_post_actions).to be_blank end it 'returns the like' do PostAction.act(coding_horror, p1, PostActionType.types[:like]) - topic_view.all_post_actions[p1.id][PostActionType.types[:like]].should be_present + expect(topic_view.all_post_actions[p1.id][PostActionType.types[:like]]).to be_present end end context '.all_active_flags' do it 'is blank at first' do - topic_view.all_active_flags.should be_blank + expect(topic_view.all_active_flags).to be_blank end it 'returns the active flags' do PostAction.act(moderator, p1, PostActionType.types[:off_topic]) PostAction.act(coding_horror, p1, PostActionType.types[:off_topic]) - topic_view.all_active_flags[p1.id][PostActionType.types[:off_topic]].count.should == 2 + expect(topic_view.all_active_flags[p1.id][PostActionType.types[:off_topic]].count).to eq(2) end it 'returns only the active flags' do @@ -185,7 +201,7 @@ describe TopicView do PostAction.defer_flags!(p1, moderator) - topic_view.all_active_flags[p1.id].should == nil + expect(topic_view.all_active_flags[p1.id]).to eq(nil) end end @@ -193,21 +209,21 @@ describe TopicView do context '.read?' do it 'tracks correctly' do # anon is assumed to have read everything - TopicView.new(topic.id).read?(1).should == true + expect(TopicView.new(topic.id).read?(1)).to eq(true) # random user has nothing - topic_view.read?(1).should == false + expect(topic_view.read?(1)).to eq(false) # a real user that just read it should have it marked PostTiming.process_timings(coding_horror, topic.id, 1, [[1,1000]]) - TopicView.new(topic.id, coding_horror).read?(1).should == true - TopicView.new(topic.id, coding_horror).topic_user.should be_present + expect(TopicView.new(topic.id, coding_horror).read?(1)).to eq(true) + expect(TopicView.new(topic.id, coding_horror).topic_user).to be_present end end context '.topic_user' do it 'returns nil when there is no user' do - TopicView.new(topic.id, nil).topic_user.should be_blank + expect(TopicView.new(topic.id, nil).topic_user).to be_blank end end @@ -222,12 +238,12 @@ describe TopicView do recent_posts = topic_view.recent_posts # count - recent_posts.count.should == 25 + expect(recent_posts.count).to eq(25) # ordering - recent_posts.include?(p1).should == false - recent_posts.include?(p3).should == true - recent_posts.first.created_at.should > recent_posts.last.created_at + expect(recent_posts.include?(p1)).to eq(false) + expect(recent_posts.include?(p3)).to eq(true) + expect(recent_posts.first.created_at).to be > recent_posts.last.created_at end end @@ -245,7 +261,7 @@ describe TopicView do let!(:p3) { Fabricate(:post, topic: topic, user: first_poster)} before do - SiteSetting.posts_chunksize = 3 + TopicView.stubs(:chunk_size).returns(3) # Update them to the sort order we're checking for [p1, p2, p3, p4, p5, p6, p7].each_with_index do |p, idx| @@ -259,13 +275,13 @@ describe TopicView do describe "contains_gaps?" do it "works" do # does not contain contains_gaps with default filtering - topic_view.contains_gaps?.should == false + expect(topic_view.contains_gaps?).to eq(false) # contains contains_gaps when filtered by username" do - TopicView.new(topic.id, coding_horror, username_filters: ['eviltrout']).contains_gaps?.should == true + expect(TopicView.new(topic.id, coding_horror, username_filters: ['eviltrout']).contains_gaps?).to eq(true) # contains contains_gaps when filtered by summary - TopicView.new(topic.id, coding_horror, filter: 'summary').contains_gaps?.should == true + expect(TopicView.new(topic.id, coding_horror, filter: 'summary').contains_gaps?).to eq(true) # contains contains_gaps when filtered by best - TopicView.new(topic.id, coding_horror, best: 5).contains_gaps?.should == true + expect(TopicView.new(topic.id, coding_horror, best: 5).contains_gaps?).to eq(true) end end @@ -283,19 +299,19 @@ describe TopicView do TopicView.new(topic.id, coding_horror).posts.count }.to raise_error(Discourse::InvalidAccess) - TopicView.new(t2.id, coding_horror, post_ids: [p1.id,p2.id]).posts.count.should == 0 + expect(TopicView.new(t2.id, coding_horror, post_ids: [p1.id,p2.id]).posts.count).to eq(0) end describe '#filter_posts_paged' do - before { SiteSetting.stubs(:posts_chunksize).returns(2) } + before { TopicView.stubs(:chunk_size).returns(2) } it 'returns correct posts for all pages' do - topic_view.filter_posts_paged(1).should == [p1, p2] - topic_view.filter_posts_paged(2).should == [p3, p5] - topic_view.filter_posts_paged(3).should == [] - topic_view.filter_posts_paged(100).should == [] + expect(topic_view.filter_posts_paged(1)).to eq([p1, p2]) + expect(topic_view.filter_posts_paged(2)).to eq([p3, p5]) + expect(topic_view.filter_posts_paged(3)).to eq([]) + expect(topic_view.filter_posts_paged(100)).to eq([]) end end @@ -307,82 +323,82 @@ describe TopicView do it "snaps to the lower boundary" do near_view = topic_view_near(p1) - near_view.desired_post.should == p1 - near_view.posts.should == [p1, p2, p3] - near_view.contains_gaps?.should == false + expect(near_view.desired_post).to eq(p1) + expect(near_view.posts).to eq([p1, p2, p3]) + expect(near_view.contains_gaps?).to eq(false) end it "snaps to the upper boundary" do near_view = topic_view_near(p5) - near_view.desired_post.should == p5 - near_view.posts.should == [p2, p3, p5] - near_view.contains_gaps?.should == false + expect(near_view.desired_post).to eq(p5) + expect(near_view.posts).to eq([p2, p3, p5]) + expect(near_view.contains_gaps?).to eq(false) end it "returns the posts in the middle" do near_view = topic_view_near(p2) - near_view.desired_post.should == p2 - near_view.posts.should == [p1, p2, p3] - near_view.contains_gaps?.should == false + expect(near_view.desired_post).to eq(p2) + expect(near_view.posts).to eq([p1, p2, p3]) + expect(near_view.contains_gaps?).to eq(false) end it "gaps deleted posts to an admin" do coding_horror.admin = true near_view = topic_view_near(p3) - near_view.desired_post.should == p3 - near_view.posts.should == [p2, p3, p5] - near_view.gaps.before.should == {p5.id => [p4.id]} - near_view.gaps.after.should == {p5.id => [p6.id, p7.id]} + expect(near_view.desired_post).to eq(p3) + expect(near_view.posts).to eq([p2, p3, p5]) + expect(near_view.gaps.before).to eq({p5.id => [p4.id]}) + expect(near_view.gaps.after).to eq({p5.id => [p6.id, p7.id]}) end it "returns deleted posts to an admin with show_deleted" do coding_horror.admin = true near_view = topic_view_near(p3, true) - near_view.desired_post.should == p3 - near_view.posts.should == [p2, p3, p4] - near_view.contains_gaps?.should == false + expect(near_view.desired_post).to eq(p3) + expect(near_view.posts).to eq([p2, p3, p4]) + expect(near_view.contains_gaps?).to eq(false) end it "gaps deleted posts by nuked users to an admin" do coding_horror.admin = true near_view = topic_view_near(p5) - near_view.desired_post.should == p5 + expect(near_view.desired_post).to eq(p5) # note: both p4 and p6 get skipped - near_view.posts.should == [p2, p3, p5] - near_view.gaps.before.should == {p5.id => [p4.id]} - near_view.gaps.after.should == {p5.id => [p6.id, p7.id]} + expect(near_view.posts).to eq([p2, p3, p5]) + expect(near_view.gaps.before).to eq({p5.id => [p4.id]}) + expect(near_view.gaps.after).to eq({p5.id => [p6.id, p7.id]}) end it "returns deleted posts by nuked users to an admin with show_deleted" do coding_horror.admin = true near_view = topic_view_near(p5, true) - near_view.desired_post.should == p5 - near_view.posts.should == [p4, p5, p6] - near_view.contains_gaps?.should == false + expect(near_view.desired_post).to eq(p5) + expect(near_view.posts).to eq([p4, p5, p6]) + expect(near_view.contains_gaps?).to eq(false) end context "when 'posts per page' exceeds the number of posts" do - before { SiteSetting.stubs(:posts_chunksize).returns(100) } + before { TopicView.stubs(:chunk_size).returns(100) } it 'returns all the posts' do near_view = topic_view_near(p5) - near_view.posts.should == [p1, p2, p3, p5] - near_view.contains_gaps?.should == false + expect(near_view.posts).to eq([p1, p2, p3, p5]) + expect(near_view.contains_gaps?).to eq(false) end it 'gaps deleted posts to admins' do coding_horror.admin = true near_view = topic_view_near(p5) - near_view.posts.should == [p1, p2, p3, p5] - near_view.gaps.before.should == {p5.id => [p4.id]} - near_view.gaps.after.should == {p5.id => [p6.id, p7.id]} + expect(near_view.posts).to eq([p1, p2, p3, p5]) + expect(near_view.gaps.before).to eq({p5.id => [p4.id]}) + expect(near_view.gaps.after).to eq({p5.id => [p6.id, p7.id]}) end it 'returns deleted posts to admins' do coding_horror.admin = true near_view = topic_view_near(p5, true) - near_view.posts.should == [p1, p2, p3, p4, p5, p6, p7] - near_view.contains_gaps?.should == false + expect(near_view.posts).to eq([p1, p2, p3, p4, p5, p6, p7]) + expect(near_view.contains_gaps?).to eq(false) end end end diff --git a/spec/components/topics_bulk_action_spec.rb b/spec/components/topics_bulk_action_spec.rb index 533c876a27..2a3396d2dc 100644 --- a/spec/components/topics_bulk_action_spec.rb +++ b/spec/components/topics_bulk_action_spec.rb @@ -15,8 +15,8 @@ describe TopicsBulkAction do tu = TopicUser.find_by(user_id: post1.user_id, topic_id: post1.topic_id) - tu.last_read_post_number.should == 3 - tu.highest_seen_post_number.should == 3 + expect(tu.last_read_post_number).to eq(3) + expect(tu.highest_seen_post_number).to eq(3) end end @@ -25,7 +25,7 @@ describe TopicsBulkAction do it "raises an error with an invalid operation" do tba = TopicsBulkAction.new(user, [1], type: 'rm_root') - -> { tba.perform! }.should raise_error(Discourse::InvalidParameters) + expect { tba.perform! }.to raise_error(Discourse::InvalidParameters) end end @@ -37,9 +37,9 @@ describe TopicsBulkAction do it "changes the category and returns the topic_id" do tba = TopicsBulkAction.new(topic.user, [topic.id], type: 'change_category', category_id: category.id) topic_ids = tba.perform! - topic_ids.should == [topic.id] + expect(topic_ids).to eq([topic.id]) topic.reload - topic.category.should == category + expect(topic.category).to eq(category) end end @@ -48,9 +48,9 @@ describe TopicsBulkAction do Guardian.any_instance.expects(:can_edit?).returns(false) tba = TopicsBulkAction.new(topic.user, [topic.id], type: 'change_category', category_id: category.id) topic_ids = tba.perform! - topic_ids.should == [] + expect(topic_ids).to eq([]) topic.reload - topic.category.should_not == category + expect(topic.category).not_to eq(category) end end end @@ -73,7 +73,7 @@ describe TopicsBulkAction do tba = TopicsBulkAction.new(moderator, [topic.id], type: 'delete') tba.perform! topic.reload - topic.should be_trashed + expect(topic).to be_trashed end end @@ -84,8 +84,8 @@ describe TopicsBulkAction do it "updates the notification level" do tba = TopicsBulkAction.new(topic.user, [topic.id], type: 'change_notification_level', notification_level_id: 2) topic_ids = tba.perform! - topic_ids.should == [topic.id] - TopicUser.get(topic, topic.user).notification_level.should == 2 + expect(topic_ids).to eq([topic.id]) + expect(TopicUser.get(topic, topic.user).notification_level).to eq(2) end end @@ -94,8 +94,8 @@ describe TopicsBulkAction do Guardian.any_instance.expects(:can_see?).returns(false) tba = TopicsBulkAction.new(topic.user, [topic.id], type: 'change_notification_level', notification_level_id: 2) topic_ids = tba.perform! - topic_ids.should == [] - TopicUser.get(topic, topic.user).should be_blank + expect(topic_ids).to eq([]) + expect(TopicUser.get(topic, topic.user)).to be_blank end end end @@ -109,9 +109,9 @@ describe TopicsBulkAction do Guardian.any_instance.expects(:can_create?).returns(true) tba = TopicsBulkAction.new(topic.user, [topic.id], type: 'close') topic_ids = tba.perform! - topic_ids.should == [topic.id] + expect(topic_ids).to eq([topic.id]) topic.reload - topic.should be_closed + expect(topic).to be_closed end end @@ -120,9 +120,9 @@ describe TopicsBulkAction do Guardian.any_instance.expects(:can_moderate?).returns(false) tba = TopicsBulkAction.new(topic.user, [topic.id], type: 'close') topic_ids = tba.perform! - topic_ids.should be_blank + expect(topic_ids).to be_blank topic.reload - topic.should_not be_closed + expect(topic).not_to be_closed end end end @@ -136,9 +136,9 @@ describe TopicsBulkAction do Guardian.any_instance.expects(:can_create?).returns(true) tba = TopicsBulkAction.new(topic.user, [topic.id], type: 'archive') topic_ids = tba.perform! - topic_ids.should == [topic.id] + expect(topic_ids).to eq([topic.id]) topic.reload - topic.should be_archived + expect(topic).to be_archived end end @@ -147,9 +147,9 @@ describe TopicsBulkAction do Guardian.any_instance.expects(:can_moderate?).returns(false) tba = TopicsBulkAction.new(topic.user, [topic.id], type: 'archive') topic_ids = tba.perform! - topic_ids.should be_blank + expect(topic_ids).to be_blank topic.reload - topic.should_not be_archived + expect(topic).not_to be_archived end end end diff --git a/spec/components/trashable_spec.rb b/spec/components/trashable_spec.rb index 779816f768..aa54d85d46 100644 --- a/spec/components/trashable_spec.rb +++ b/spec/components/trashable_spec.rb @@ -8,7 +8,7 @@ describe Trashable do p2 = Fabricate(:post) expect { p1.trash! }.to change{Post.count}.by(-1) - Post.with_deleted.count.should == Post.count + 1 + expect(Post.with_deleted.count).to eq(Post.count + 1) end end diff --git a/spec/components/unread_spec.rb b/spec/components/unread_spec.rb index 61a9b9bc4a..cf9d814ac3 100644 --- a/spec/components/unread_spec.rb +++ b/spec/components/unread_spec.rb @@ -17,47 +17,47 @@ describe Unread do it 'should have 0 unread posts if the user has seen all posts' do @topic_user.stubs(:last_read_post_number).returns(13) @topic_user.stubs(:highest_seen_post_number).returns(13) - @unread.unread_posts.should == 0 + expect(@unread.unread_posts).to eq(0) end it 'should have 6 unread posts if the user has seen all but 6 posts' do @topic_user.stubs(:last_read_post_number).returns(5) @topic_user.stubs(:highest_seen_post_number).returns(11) - @unread.unread_posts.should == 6 + expect(@unread.unread_posts).to eq(6) end it 'should have 0 unread posts if the user has seen more posts than exist (deleted)' do @topic_user.stubs(:last_read_post_number).returns(100) @topic_user.stubs(:highest_seen_post_number).returns(13) - @unread.unread_posts.should == 0 + expect(@unread.unread_posts).to eq(0) end end describe 'new_posts' do it 'should have 0 new posts if the user has read all posts' do @topic_user.stubs(:last_read_post_number).returns(13) - @unread.new_posts.should == 0 + expect(@unread.new_posts).to eq(0) end it 'returns 0 when the topic is the same length as when you last saw it' do @topic_user.stubs(:highest_seen_post_number).returns(13) - @unread.new_posts.should == 0 + expect(@unread.new_posts).to eq(0) end it 'has 3 new posts if the user has read 10 posts' do @topic_user.stubs(:highest_seen_post_number).returns(10) - @unread.new_posts.should == 3 + expect(@unread.new_posts).to eq(3) end it 'has 0 new posts if the user has read 10 posts but is not tracking' do @topic_user.stubs(:highest_seen_post_number).returns(10) @topic_user.stubs(:notification_level).returns(TopicUser.notification_levels[:regular]) - @unread.new_posts.should == 0 + expect(@unread.new_posts).to eq(0) end it 'has 0 new posts if the user read more posts than exist (deleted)' do @topic_user.stubs(:highest_seen_post_number).returns(16) - @unread.new_posts.should == 0 + expect(@unread.new_posts).to eq(0) end end diff --git a/spec/components/url_helper_spec.rb b/spec/components/url_helper_spec.rb index a72bc5d723..8f77f793f1 100644 --- a/spec/components/url_helper_spec.rb +++ b/spec/components/url_helper_spec.rb @@ -15,21 +15,21 @@ describe UrlHelper do store = stub store.expects(:has_been_uploaded?).returns(true) Discourse.stubs(:store).returns(store) - helper.is_local("http://discuss.site.com/path/to/file.png").should == true + expect(helper.is_local("http://discuss.site.com/path/to/file.png")).to eq(true) end it "is true for relative assets" do store = stub store.expects(:has_been_uploaded?).returns(false) Discourse.stubs(:store).returns(store) - helper.is_local("/assets/javascripts/all.js").should == true + expect(helper.is_local("/assets/javascripts/all.js")).to eq(true) end it "is true for plugin assets" do store = stub store.expects(:has_been_uploaded?).returns(false) Discourse.stubs(:store).returns(store) - helper.is_local("/plugins/all.js").should == true + expect(helper.is_local("/plugins/all.js")).to eq(true) end end @@ -37,16 +37,16 @@ describe UrlHelper do describe "#absolute" do it "does not change non-relative url" do - helper.absolute("http://www.discourse.org").should == "http://www.discourse.org" + expect(helper.absolute("http://www.discourse.org")).to eq("http://www.discourse.org") end it "changes a relative url to an absolute one using base url by default" do - helper.absolute("/path/to/file").should == "http://test.localhost/path/to/file" + expect(helper.absolute("/path/to/file")).to eq("http://test.localhost/path/to/file") end it "changes a relative url to an absolute one using the cdn when enabled" do Rails.configuration.action_controller.stubs(:asset_host).returns("http://my.cdn.com") - helper.absolute("/path/to/file").should == "http://my.cdn.com/path/to/file" + expect(helper.absolute("/path/to/file")).to eq("http://my.cdn.com/path/to/file") end end @@ -55,7 +55,7 @@ describe UrlHelper do it "changes a relative url to an absolute one using base url even when cdn is enabled" do Rails.configuration.action_controller.stubs(:asset_host).returns("http://my.cdn.com") - helper.absolute_without_cdn("/path/to/file").should == "http://test.localhost/path/to/file" + expect(helper.absolute_without_cdn("/path/to/file")).to eq("http://test.localhost/path/to/file") end end @@ -63,9 +63,9 @@ describe UrlHelper do describe "#schemaless" do it "removes http or https schemas only" do - helper.schemaless("http://www.discourse.org").should == "//www.discourse.org" - helper.schemaless("https://secure.discourse.org").should == "//secure.discourse.org" - helper.schemaless("ftp://ftp.discourse.org").should == "ftp://ftp.discourse.org" + expect(helper.schemaless("http://www.discourse.org")).to eq("//www.discourse.org") + expect(helper.schemaless("https://secure.discourse.org")).to eq("//secure.discourse.org") + expect(helper.schemaless("ftp://ftp.discourse.org")).to eq("ftp://ftp.discourse.org") end end diff --git a/spec/components/user_name_suggester_spec.rb b/spec/components/user_name_suggester_spec.rb index de14323e5a..b16bfee4b7 100644 --- a/spec/components/user_name_suggester_spec.rb +++ b/spec/components/user_name_suggester_spec.rb @@ -5,7 +5,7 @@ describe UserNameSuggester do describe 'name heuristics' do it 'is able to guess a decent username from an email' do - UserNameSuggester.suggest('bob@bob.com').should == 'bob' + expect(UserNameSuggester.suggest('bob@bob.com')).to eq('bob') end end @@ -15,62 +15,62 @@ describe UserNameSuggester do end it "doesn't raise an error on nil username" do - UserNameSuggester.suggest(nil).should == nil + expect(UserNameSuggester.suggest(nil)).to eq(nil) end it 'corrects weird characters' do - UserNameSuggester.suggest("Darth%^Vader").should == 'Darth_Vader' + expect(UserNameSuggester.suggest("Darth%^Vader")).to eq('Darth_Vader') end it "transliterates some characters" do - UserNameSuggester.suggest("Jørn").should == 'Jorn' + expect(UserNameSuggester.suggest("Jørn")).to eq('Jorn') end it 'adds 1 to an existing username' do user = Fabricate(:user) - UserNameSuggester.suggest(user.username).should == "#{user.username}1" + expect(UserNameSuggester.suggest(user.username)).to eq("#{user.username}1") end it "adds numbers if it's too short" do - UserNameSuggester.suggest('a').should == 'a11' + expect(UserNameSuggester.suggest('a')).to eq('a11') end it "has a special case for me and i emails" do - UserNameSuggester.suggest('me@eviltrout.com').should == 'eviltrout' - UserNameSuggester.suggest('i@eviltrout.com').should == 'eviltrout' + expect(UserNameSuggester.suggest('me@eviltrout.com')).to eq('eviltrout') + expect(UserNameSuggester.suggest('i@eviltrout.com')).to eq('eviltrout') end it "shortens very long suggestions" do - UserNameSuggester.suggest("myreallylongnameisrobinwardesquire").should == 'myreallylongnam' + expect(UserNameSuggester.suggest("myreallylongnameisrobinwardesquire")).to eq('myreallylongnam') end it "makes room for the digit added if the username is too long" do User.create(username: 'myreallylongnam', email: 'fake@discourse.org') - UserNameSuggester.suggest("myreallylongnam").should == 'myreallylongna1' + expect(UserNameSuggester.suggest("myreallylongnam")).to eq('myreallylongna1') end it "removes leading character if it is not alphanumeric" do - UserNameSuggester.suggest("_myname").should == 'myname' + expect(UserNameSuggester.suggest("_myname")).to eq('myname') end it "removes trailing characters if they are invalid" do - UserNameSuggester.suggest("myname!^$=").should == 'myname' + expect(UserNameSuggester.suggest("myname!^$=")).to eq('myname') end it "replace dots" do - UserNameSuggester.suggest("my.name").should == 'my_name' + expect(UserNameSuggester.suggest("my.name")).to eq('my_name') end it "remove leading dots" do - UserNameSuggester.suggest(".myname").should == 'myname' + expect(UserNameSuggester.suggest(".myname")).to eq('myname') end it "remove trailing dots" do - UserNameSuggester.suggest("myname.").should == 'myname' + expect(UserNameSuggester.suggest("myname.")).to eq('myname') end it 'should handle typical facebook usernames' do - UserNameSuggester.suggest('roger.nelson.3344913').should == 'roger_nelson_33' + expect(UserNameSuggester.suggest('roger.nelson.3344913')).to eq('roger_nelson_33') end end diff --git a/spec/components/validators/allowed_ip_address_validator_spec.rb b/spec/components/validators/allowed_ip_address_validator_spec.rb index 96bd162f50..4834528588 100644 --- a/spec/components/validators/allowed_ip_address_validator_spec.rb +++ b/spec/components/validators/allowed_ip_address_validator_spec.rb @@ -10,7 +10,7 @@ describe AllowedIpAddressValidator do it 'should add an error' do ScreenedIpAddress.stubs(:should_block?).returns(true) validate - record.errors[:ip_address].should be_present + expect(record.errors[:ip_address]).to be_present end end @@ -18,7 +18,7 @@ describe AllowedIpAddressValidator do it 'should add an error' do SpamHandler.stubs(:should_prevent_registration_from_ip?).returns(true) validate - record.errors[:ip_address].should be_present + expect(record.errors[:ip_address]).to be_present end end @@ -26,7 +26,7 @@ describe AllowedIpAddressValidator do it "shouldn't add an error" do ScreenedIpAddress.stubs(:should_block?).returns(false) validate - record.errors[:ip_address].should_not be_present + expect(record.errors[:ip_address]).not_to be_present end end @@ -35,7 +35,7 @@ describe AllowedIpAddressValidator do ScreenedIpAddress.expects(:should_block?).never record.ip_address = nil validate - record.errors[:ip_address].should_not be_present + expect(record.errors[:ip_address]).not_to be_present end end diff --git a/spec/components/validators/email_setting_validator_spec.rb b/spec/components/validators/email_setting_validator_spec.rb index 8c5f800b9b..67fd6e82da 100644 --- a/spec/components/validators/email_setting_validator_spec.rb +++ b/spec/components/validators/email_setting_validator_spec.rb @@ -5,16 +5,16 @@ describe EmailSettingValidator do subject(:validator) { described_class.new } it "returns true for blank values" do - validator.valid_value?('').should == true - validator.valid_value?(nil).should == true + expect(validator.valid_value?('')).to eq(true) + expect(validator.valid_value?(nil)).to eq(true) end it "returns true if value is a valid email address" do - validator.valid_value?('vader@example.com').should == true + expect(validator.valid_value?('vader@example.com')).to eq(true) end it "returns false if value is not a valid email address" do - validator.valid_value?('my house').should == false + expect(validator.valid_value?('my house')).to eq(false) end end end diff --git a/spec/components/validators/email_validator_spec.rb b/spec/components/validators/email_validator_spec.rb index e310aab342..f852cf1b71 100644 --- a/spec/components/validators/email_validator_spec.rb +++ b/spec/components/validators/email_validator_spec.rb @@ -10,13 +10,13 @@ describe EmailValidator do it "doesn't add an error when email doesn't match a blocked email" do ScreenedEmail.stubs(:should_block?).with(record.email).returns(false) validate - record.errors[:email].should_not be_present + expect(record.errors[:email]).not_to be_present end it "adds an error when email matches a blocked email" do ScreenedEmail.stubs(:should_block?).with(record.email).returns(true) validate - record.errors[:email].should be_present + expect(record.errors[:email]).to be_present end end diff --git a/spec/components/validators/integer_setting_validator_spec.rb b/spec/components/validators/integer_setting_validator_spec.rb index 7b0eca1e3e..64b2819431 100644 --- a/spec/components/validators/integer_setting_validator_spec.rb +++ b/spec/components/validators/integer_setting_validator_spec.rb @@ -5,12 +5,12 @@ describe IntegerSettingValidator do shared_examples "for all IntegerSettingValidator opts" do it "returns false for blank values" do - validator.valid_value?('').should == false - validator.valid_value?(nil).should == false + expect(validator.valid_value?('')).to eq(false) + expect(validator.valid_value?(nil)).to eq(false) end it "returns false if value is not a valid integer" do - validator.valid_value?('two').should == false + expect(validator.valid_value?('two')).to eq(false) end end @@ -20,10 +20,10 @@ describe IntegerSettingValidator do include_examples "for all IntegerSettingValidator opts" it "returns true if value is a valid integer" do - validator.valid_value?(1).should == true - validator.valid_value?(-1).should == true - validator.valid_value?('1').should == true - validator.valid_value?('-1').should == true + expect(validator.valid_value?(1)).to eq(true) + expect(validator.valid_value?(-1)).to eq(true) + expect(validator.valid_value?('1')).to eq(true) + expect(validator.valid_value?('-1')).to eq(true) end end @@ -33,18 +33,18 @@ describe IntegerSettingValidator do include_examples "for all IntegerSettingValidator opts" it "returns true if value is equal to min" do - validator.valid_value?(2).should == true - validator.valid_value?('2').should == true + expect(validator.valid_value?(2)).to eq(true) + expect(validator.valid_value?('2')).to eq(true) end it "returns true if value is greater than min" do - validator.valid_value?(3).should == true - validator.valid_value?('3').should == true + expect(validator.valid_value?(3)).to eq(true) + expect(validator.valid_value?('3')).to eq(true) end it "returns false if value is less than min" do - validator.valid_value?(1).should == false - validator.valid_value?('1').should == false + expect(validator.valid_value?(1)).to eq(false) + expect(validator.valid_value?('1')).to eq(false) end end @@ -54,18 +54,18 @@ describe IntegerSettingValidator do include_examples "for all IntegerSettingValidator opts" it "returns true if value is equal to max" do - validator.valid_value?(3).should == true - validator.valid_value?('3').should == true + expect(validator.valid_value?(3)).to eq(true) + expect(validator.valid_value?('3')).to eq(true) end it "returns true if value is less than max" do - validator.valid_value?(2).should == true - validator.valid_value?('2').should == true + expect(validator.valid_value?(2)).to eq(true) + expect(validator.valid_value?('2')).to eq(true) end it "returns false if value is greater than min" do - validator.valid_value?(4).should == false - validator.valid_value?('4').should == false + expect(validator.valid_value?(4)).to eq(false) + expect(validator.valid_value?('4')).to eq(false) end end @@ -75,14 +75,14 @@ describe IntegerSettingValidator do include_examples "for all IntegerSettingValidator opts" it "returns true if value is in range" do - validator.valid_value?(-1).should == true - validator.valid_value?(0).should == true - validator.valid_value?(3).should == true + expect(validator.valid_value?(-1)).to eq(true) + expect(validator.valid_value?(0)).to eq(true) + expect(validator.valid_value?(3)).to eq(true) end it "returns false if value is out of range" do - validator.valid_value?(4).should == false - validator.valid_value?(-2).should == false + expect(validator.valid_value?(4)).to eq(false) + expect(validator.valid_value?(-2)).to eq(false) end end end diff --git a/spec/components/validators/ip_address_format_validator_spec.rb b/spec/components/validators/ip_address_format_validator_spec.rb index 5f06345b5c..20265a2d16 100644 --- a/spec/components/validators/ip_address_format_validator_spec.rb +++ b/spec/components/validators/ip_address_format_validator_spec.rb @@ -10,19 +10,19 @@ describe IpAddressFormatValidator do it "should not add an error for #{arg}" do record.ip_address = arg validate - record.errors[:ip_address].should_not be_present + expect(record.errors[:ip_address]).not_to be_present end end it 'should add an error for nil IP address' do record.ip_address = nil validate - record.errors[:ip_address].should be_present + expect(record.errors[:ip_address]).to be_present end it 'should add an error for invalid IP address' do record.ip_address = '99.99.99' validate - record.errors[:ip_address].should be_present + expect(record.errors[:ip_address]).to be_present end end diff --git a/spec/components/validators/password_validator_spec.rb b/spec/components/validators/password_validator_spec.rb index f0f8608cdc..fe14bfdd4b 100644 --- a/spec/components/validators/password_validator_spec.rb +++ b/spec/components/validators/password_validator_spec.rb @@ -20,25 +20,25 @@ describe PasswordValidator do it "doesn't add an error when password is good" do @password = "weron235alsfn234" validate - record.errors[:password].should_not be_present + expect(record.errors[:password]).not_to be_present end it "adds an error when password is too short" do @password = "p" validate - record.errors[:password].should be_present + expect(record.errors[:password]).to be_present end it "adds an error when password is blank" do @password = '' validate - record.errors[:password].should be_present + expect(record.errors[:password]).to be_present end it "adds an error when password is nil" do @password = nil validate - record.errors[:password].should be_present + expect(record.errors[:password]).to be_present end end @@ -48,7 +48,7 @@ describe PasswordValidator do it "adds an error when password length is 11" do @password = "gt38sdt92bv" validate - record.errors[:password].should be_present + expect(record.errors[:password]).to be_present end end end @@ -62,14 +62,14 @@ describe PasswordValidator do SiteSetting.stubs(:block_common_passwords).returns(true) @password = "password" validate - record.errors[:password].should be_present + expect(record.errors[:password]).to be_present end it "doesn't add an error when block_common_passwords is disabled" do SiteSetting.stubs(:block_common_passwords).returns(false) @password = "password" validate - record.errors[:password].should_not be_present + expect(record.errors[:password]).not_to be_present end end end @@ -80,7 +80,7 @@ describe PasswordValidator do it "doesn't add an error if password is not required" do @password = nil validate - record.errors[:password].should_not be_present + expect(record.errors[:password]).not_to be_present end end diff --git a/spec/components/validators/post_validator_spec.rb b/spec/components/validators/post_validator_spec.rb index 4ecf1ba02d..064da5c709 100644 --- a/spec/components/validators/post_validator_spec.rb +++ b/spec/components/validators/post_validator_spec.rb @@ -64,7 +64,7 @@ describe Validators::PostValidator do it "should not add an error" do validator.unique_post_validator(post) - post.errors.count.should == 0 + expect(post.errors.count).to eq(0) end end @@ -81,7 +81,7 @@ describe Validators::PostValidator do it "should not add an error if post.skip_unique_check is true" do post.skip_unique_check = true validator.unique_post_validator(post) - post.errors.count.should == 0 + expect(post.errors.count).to eq(0) end end end diff --git a/spec/components/validators/quality_title_validator_spec.rb b/spec/components/validators/quality_title_validator_spec.rb index ba67d61bfc..065fcdcfd1 100644 --- a/spec/components/validators/quality_title_validator_spec.rb +++ b/spec/components/validators/quality_title_validator_spec.rb @@ -25,45 +25,45 @@ describe "A record validated with QualityTitleValidator" do it "allows a regular title with a few ascii characters" do topic.title = valid_title - topic.should be_valid + expect(topic).to be_valid end it "allows non ascii" do topic.title = "Iñtërnâtiônàlizætiøn" - topic.should be_valid + expect(topic).to be_valid end it 'allows Chinese characters' do topic.title = '现在发现使用中文标题没法发帖子了' - topic.should be_valid + expect(topic).to be_valid end it "allows anything in a private message" do topic.stubs(:private_message? => true) [short_title, long_title, xxxxx_title].each do |bad_title| topic.title = bad_title - topic.should be_valid + expect(topic).to be_valid end end it "strips a title when identifying length" do topic.title = short_title.center(SiteSetting.min_topic_title_length + 1, ' ') - topic.should_not be_valid + expect(topic).not_to be_valid end it "doesn't allow a long title" do topic.title = long_title - topic.should_not be_valid + expect(topic).not_to be_valid end it "doesn't allow a short title" do topic.title = short_title - topic.should_not be_valid + expect(topic).not_to be_valid end it "doesn't allow a title of one repeated character" do topic.title = xxxxx_title - topic.should_not be_valid + expect(topic).not_to be_valid end # describe "with a name" do diff --git a/spec/components/validators/reply_by_email_address_validator_spec.rb b/spec/components/validators/reply_by_email_address_validator_spec.rb new file mode 100644 index 0000000000..81f6f24a01 --- /dev/null +++ b/spec/components/validators/reply_by_email_address_validator_spec.rb @@ -0,0 +1,33 @@ +require 'spec_helper' + +describe ReplyByEmailAddressValidator do + + describe '#valid_value?' do + subject(:validator) { described_class.new } + + it "returns true for blank values" do + expect(validator.valid_value?('')).to eq(true) + expect(validator.valid_value?(nil)).to eq(true) + end + + it "returns false if value is not an email address" do + expect(validator.valid_value?('WAT%{reply_key}.com')).to eq(false) + end + + it "returns false if value does not contain '%{reply_key}'" do + expect(validator.valid_value?('foo@bar.com')).to eq(false) + end + + it "returns false if value is the same as SiteSetting.notification_email" do + SiteSetting.expects(:notification_email).returns("foo@bar.com") + expect(validator.valid_value?('foo+%{reply_key}@bar.com')).to eq(false) + end + + it "returns true when value is OK" do + SiteSetting.expects(:notification_email).returns("foo@bar.com") + expect(validator.valid_value?('bar%{reply_key}@foo.com')).to eq(true) + end + + end + +end diff --git a/spec/components/validators/string_setting_validator_spec.rb b/spec/components/validators/string_setting_validator_spec.rb index b241428e07..69370d6433 100644 --- a/spec/components/validators/string_setting_validator_spec.rb +++ b/spec/components/validators/string_setting_validator_spec.rb @@ -5,8 +5,8 @@ describe StringSettingValidator do describe '#valid_value?' do shared_examples "for all StringSettingValidator opts" do it "returns true for blank values" do - validator.valid_value?('').should == true - validator.valid_value?(nil).should == true + expect(validator.valid_value?('')).to eq(true) + expect(validator.valid_value?(nil)).to eq(true) end end @@ -16,21 +16,21 @@ describe StringSettingValidator do include_examples "for all StringSettingValidator opts" it "returns true if value matches the regex" do - validator.valid_value?('The bacon is delicious').should == true + expect(validator.valid_value?('The bacon is delicious')).to eq(true) end it "returns false if the value doesn't match the regex" do - validator.valid_value?('The vegetables are delicious').should == false + expect(validator.valid_value?('The vegetables are delicious')).to eq(false) end it "test some other regexes" do v = described_class.new(regex: '^(chocolate|banana)$') - v.valid_value?('chocolate').should == true - v.valid_value?('chocolates').should == false + expect(v.valid_value?('chocolate')).to eq(true) + expect(v.valid_value?('chocolates')).to eq(false) v = described_class.new(regex: '^[\w]+$') - v.valid_value?('the_file').should == true - v.valid_value?('the_file.bat').should == false + expect(v.valid_value?('the_file')).to eq(true) + expect(v.valid_value?('the_file.bat')).to eq(false) end end @@ -40,12 +40,12 @@ describe StringSettingValidator do include_examples "for all StringSettingValidator opts" it "returns true if length is ok" do - validator.valid_value?('ok').should == true - validator.valid_value?('yep long enough').should == true + expect(validator.valid_value?('ok')).to eq(true) + expect(validator.valid_value?('yep long enough')).to eq(true) end it "returns false if too short" do - validator.valid_value?('x').should == false + expect(validator.valid_value?('x')).to eq(false) end end @@ -55,43 +55,43 @@ describe StringSettingValidator do include_examples "for all StringSettingValidator opts" it "returns true if length is ok" do - validator.valid_value?('Z').should == true - validator.valid_value?('abcde').should == true + expect(validator.valid_value?('Z')).to eq(true) + expect(validator.valid_value?('abcde')).to eq(true) end it "returns false if too long" do - validator.valid_value?('banana').should == false + expect(validator.valid_value?('banana')).to eq(false) end end context 'combinations of options' do it "min and regex" do v = described_class.new(regex: '^[\w]+$', min: 3) - v.valid_value?('chocolate').should == true - v.valid_value?('hi').should == false - v.valid_value?('game.exe').should == false + expect(v.valid_value?('chocolate')).to eq(true) + expect(v.valid_value?('hi')).to eq(false) + expect(v.valid_value?('game.exe')).to eq(false) end it "max and regex" do v = described_class.new(regex: '^[\w]+$', max: 5) - v.valid_value?('chocolate').should == false - v.valid_value?('a_b_c').should == true - v.valid_value?('a b c').should == false + expect(v.valid_value?('chocolate')).to eq(false) + expect(v.valid_value?('a_b_c')).to eq(true) + expect(v.valid_value?('a b c')).to eq(false) end it "min and max" do v = described_class.new(min: 3, max: 5) - v.valid_value?('chocolate').should == false - v.valid_value?('a').should == false - v.valid_value?('a b c').should == true - v.valid_value?('a b').should == true + expect(v.valid_value?('chocolate')).to eq(false) + expect(v.valid_value?('a')).to eq(false) + expect(v.valid_value?('a b c')).to eq(true) + expect(v.valid_value?('a b')).to eq(true) end it "min, max, and regex" do v = described_class.new(min: 3, max: 12, regex: 'bacon') - v.valid_value?('go bacon!').should == true - v.valid_value?('sprinkle bacon on your cereal').should == false - v.valid_value?('ba').should == false + expect(v.valid_value?('go bacon!')).to eq(true) + expect(v.valid_value?('sprinkle bacon on your cereal')).to eq(false) + expect(v.valid_value?('ba')).to eq(false) end end diff --git a/spec/components/validators/username_setting_validator_spec.rb b/spec/components/validators/username_setting_validator_spec.rb index 6bffe2ec50..fcc417a0a2 100644 --- a/spec/components/validators/username_setting_validator_spec.rb +++ b/spec/components/validators/username_setting_validator_spec.rb @@ -5,17 +5,17 @@ describe UsernameSettingValidator do subject(:validator) { described_class.new } it "returns true for blank values" do - validator.valid_value?('').should == true - validator.valid_value?(nil).should == true + expect(validator.valid_value?('')).to eq(true) + expect(validator.valid_value?(nil)).to eq(true) end it "returns true if value matches an existing user's username" do Fabricate(:user, username: 'vader') - validator.valid_value?('vader').should == true + expect(validator.valid_value?('vader')).to eq(true) end it "returns false if value does not match a user's username" do - validator.valid_value?('no way').should == false + expect(validator.valid_value?('no way')).to eq(false) end end end diff --git a/spec/controllers/admin/admin_controller_spec.rb b/spec/controllers/admin/admin_controller_spec.rb index e26da8259c..21206230f8 100644 --- a/spec/controllers/admin/admin_controller_spec.rb +++ b/spec/controllers/admin/admin_controller_spec.rb @@ -5,13 +5,13 @@ describe Admin::AdminController do context 'index' do it 'needs you to be logged in' do - lambda { xhr :get, :index }.should raise_error(Discourse::NotLoggedIn) + expect { xhr :get, :index }.to raise_error(Discourse::NotLoggedIn) end it "raises an error if you aren't an admin" do user = log_in xhr :get, :index - response.should be_forbidden + expect(response).to be_forbidden end end diff --git a/spec/controllers/admin/api_controller_spec.rb b/spec/controllers/admin/api_controller_spec.rb index 1349f22795..f940ddea5c 100644 --- a/spec/controllers/admin/api_controller_spec.rb +++ b/spec/controllers/admin/api_controller_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Admin::ApiController do it "is a subclass of AdminController" do - (Admin::ApiController < Admin::AdminController).should == true + expect(Admin::ApiController < Admin::AdminController).to eq(true) end let!(:user) { log_in(:admin) } @@ -11,7 +11,7 @@ describe Admin::ApiController do context '.index' do it "succeeds" do xhr :get, :index - response.should be_success + expect(response).to be_success end end @@ -20,8 +20,8 @@ describe Admin::ApiController do it "returns 404 when there is no key" do xhr :put, :regenerate_key, id: 1234 - response.should_not be_success - response.status.should == 404 + expect(response).not_to be_success + expect(response.status).to eq(404) end it "delegates to the api key's `regenerate!` method" do @@ -35,8 +35,8 @@ describe Admin::ApiController do it "returns 404 when there is no key" do xhr :delete, :revoke_key, id: 1234 - response.should_not be_success - response.status.should == 404 + expect(response).not_to be_success + expect(response.status).to eq(404) end it "delegates to the api key's `regenerate!` method" do @@ -47,9 +47,9 @@ describe Admin::ApiController do context '.create_master_key' do it "creates a record" do - lambda { + expect { xhr :post, :create_master_key - }.should change(ApiKey, :count).by(1) + }.to change(ApiKey, :count).by(1) end end diff --git a/spec/controllers/admin/backups_controller_spec.rb b/spec/controllers/admin/backups_controller_spec.rb index a27c781e6a..4faeef1ea7 100644 --- a/spec/controllers/admin/backups_controller_spec.rb +++ b/spec/controllers/admin/backups_controller_spec.rb @@ -3,7 +3,7 @@ require "spec_helper" describe Admin::BackupsController do it "is a subclass of AdminController" do - (Admin::BackupsController < Admin::AdminController).should == true + expect(Admin::BackupsController < Admin::AdminController).to eq(true) end let(:backup_filename) { "2014-02-10-065935.tar.gz" } @@ -28,7 +28,7 @@ describe Admin::BackupsController do xhr :get, :index, format: :html - response.should be_success + expect(response).to be_success end end @@ -40,11 +40,11 @@ describe Admin::BackupsController do xhr :get, :index, format: :json - response.should be_success + expect(response).to be_success json = JSON.parse(response.body) - json[0]["filename"].should == "backup1" - json[1]["filename"].should == "backup2" + expect(json[0]["filename"]).to eq("backup1") + expect(json[1]["filename"]).to eq("backup2") end end @@ -58,7 +58,7 @@ describe Admin::BackupsController do xhr :get, :status - response.should be_success + expect(response).to be_success end end @@ -70,20 +70,9 @@ describe Admin::BackupsController do xhr :post, :create, { with_uploads: false } - response.should be_success + expect(response).to be_success end - # it "catches OperationRunningError exception" do - # BackupRestore.expects(:is_operation_running?).returns(true) - - # xhr :post, :create - - # response.should be_success - - # json = JSON.parse(response.body) - # json["message"].should_not == nil - # end - end describe ".cancel" do @@ -93,7 +82,7 @@ describe Admin::BackupsController do xhr :delete, :cancel - response.should be_success + expect(response).to be_success end end @@ -110,8 +99,8 @@ describe Admin::BackupsController do get :show, id: backup_filename - response.headers['Content-Length'].should == 5 - response.headers['Content-Disposition'].should =~ /attachment; filename/ + expect(response.headers['Content-Length']).to eq(5) + expect(response.headers['Content-Disposition']).to match(/attachment; filename/) end it "returns 404 when the backup does not exist" do @@ -119,7 +108,7 @@ describe Admin::BackupsController do get :show, id: backup_filename - response.should be_not_found + expect(response).to be_not_found end end @@ -132,14 +121,14 @@ describe Admin::BackupsController do Backup.expects(:[]).with(backup_filename).returns(b) b.expects(:remove) xhr :delete, :destroy, id: backup_filename - response.should be_success + expect(response).to be_success end it "doesn't remove the backup if not found" do Backup.expects(:[]).with(backup_filename).returns(nil) b.expects(:remove).never xhr :delete, :destroy, id: backup_filename - response.should_not be_success + expect(response).not_to be_success end end @@ -155,7 +144,7 @@ describe Admin::BackupsController do xhr :get, :logs, format: :html - response.should be_success + expect(response).to be_success end end @@ -166,7 +155,7 @@ describe Admin::BackupsController do xhr :post, :restore, id: backup_filename - response.should be_success + expect(response).to be_success end end @@ -178,7 +167,7 @@ describe Admin::BackupsController do xhr :get, :rollback - response.should be_success + expect(response).to be_success end end @@ -190,7 +179,7 @@ describe Admin::BackupsController do xhr :put, :readonly, enable: true - response.should be_success + expect(response).to be_success end it "disables readonly mode" do @@ -198,7 +187,7 @@ describe Admin::BackupsController do xhr :put, :readonly, enable: false - response.should be_success + expect(response).to be_success end end diff --git a/spec/controllers/admin/badges_controller_spec.rb b/spec/controllers/admin/badges_controller_spec.rb index e5102cafbc..94e9c763ec 100644 --- a/spec/controllers/admin/badges_controller_spec.rb +++ b/spec/controllers/admin/badges_controller_spec.rb @@ -9,7 +9,7 @@ describe Admin::BadgesController do context 'index' do it 'returns badge index' do xhr :get, :index - response.should be_success + expect(response).to be_success end end @@ -30,46 +30,46 @@ describe Admin::BadgesController do groupings2 = BadgeGrouping.all.order(:position).to_a - groupings2.map{|g| g.name}.should == names - (groupings.map(&:id) - groupings2.map{|g| g.id}).compact.should be_blank + expect(groupings2.map{|g| g.name}).to eq(names) + expect((groupings.map(&:id) - groupings2.map{|g| g.id}).compact).to be_blank - ::JSON.parse(response.body)["badge_groupings"].length.should == groupings2.length + expect(::JSON.parse(response.body)["badge_groupings"].length).to eq(groupings2.length) end end context '.badge_types' do it 'returns success' do xhr :get, :badge_types - response.should be_success + expect(response).to be_success end it 'returns JSON' do xhr :get, :badge_types - ::JSON.parse(response.body)["badge_types"].should be_present + expect(::JSON.parse(response.body)["badge_types"]).to be_present end end context '.destroy' do it 'returns success' do xhr :delete, :destroy, id: badge.id - response.should be_success + expect(response).to be_success end it 'deletes the badge' do xhr :delete, :destroy, id: badge.id - Badge.where(id: badge.id).count.should eq(0) + expect(Badge.where(id: badge.id).count).to eq(0) end end context '.update' do it 'returns success' do xhr :put, :update, id: badge.id, name: "123456", badge_type_id: badge.badge_type_id, allow_title: false, multiple_grant: false, enabled: true - response.should be_success + expect(response).to be_success end it 'updates the badge' do xhr :put, :update, id: badge.id, name: "123456", badge_type_id: badge.badge_type_id, allow_title: false, multiple_grant: true, enabled: true - badge.reload.name.should eq('123456') + expect(badge.reload.name).to eq('123456') end end end diff --git a/spec/controllers/admin/color_schemes_controller_spec.rb b/spec/controllers/admin/color_schemes_controller_spec.rb index 9654d17bf7..cebda75720 100644 --- a/spec/controllers/admin/color_schemes_controller_spec.rb +++ b/spec/controllers/admin/color_schemes_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Admin::ColorSchemesController do it "is a subclass of AdminController" do - (described_class < Admin::AdminController).should == true + expect(described_class < Admin::AdminController).to eq(true) end context "while logged in as an admin" do @@ -20,33 +20,33 @@ describe Admin::ColorSchemesController do describe "index" do it "returns success" do xhr :get, :index - response.should be_success + expect(response).to be_success end it "returns JSON" do Fabricate(:color_scheme) xhr :get, :index - ::JSON.parse(response.body).should be_present + expect(::JSON.parse(response.body)).to be_present end end describe "create" do it "returns success" do xhr :post, :create, valid_params - response.should be_success + expect(response).to be_success end it "returns JSON" do xhr :post, :create, valid_params - ::JSON.parse(response.body)['id'].should be_present + expect(::JSON.parse(response.body)['id']).to be_present end it "returns failure with invalid params" do params = valid_params params[:color_scheme][:colors][0][:hex] = 'cool color please' xhr :post, :create, valid_params - response.should_not be_success - ::JSON.parse(response.body)['errors'].should be_present + expect(response).not_to be_success + expect(::JSON.parse(response.body)['errors']).to be_present end end @@ -56,13 +56,13 @@ describe Admin::ColorSchemesController do it "returns success" do ColorSchemeRevisor.expects(:revise).returns(existing) xhr :put, :update, valid_params.merge(id: existing.id) - response.should be_success + expect(response).to be_success end it "returns JSON" do ColorSchemeRevisor.expects(:revise).returns(existing) xhr :put, :update, valid_params.merge(id: existing.id) - ::JSON.parse(response.body)['id'].should be_present + expect(::JSON.parse(response.body)['id']).to be_present end it "returns failure with invalid params" do @@ -71,8 +71,8 @@ describe Admin::ColorSchemesController do params[:color_scheme][:colors][0][:name] = color_scheme.colors.first.name params[:color_scheme][:colors][0][:hex] = 'cool color please' xhr :put, :update, params - response.should_not be_success - ::JSON.parse(response.body)['errors'].should be_present + expect(response).not_to be_success + expect(::JSON.parse(response.body)['errors']).to be_present end end @@ -83,7 +83,7 @@ describe Admin::ColorSchemesController do expect { xhr :delete, :destroy, id: existing.id }.to change { ColorScheme.count }.by(-1) - response.should be_success + expect(response).to be_success end end end diff --git a/spec/controllers/admin/dashboard_controller_spec.rb b/spec/controllers/admin/dashboard_controller_spec.rb index 97cb8727d1..c501fe4c78 100644 --- a/spec/controllers/admin/dashboard_controller_spec.rb +++ b/spec/controllers/admin/dashboard_controller_spec.rb @@ -8,7 +8,7 @@ describe Admin::DashboardController do end it "is a subclass of AdminController" do - (Admin::DashboardController < Admin::AdminController).should == true + expect(Admin::DashboardController < Admin::AdminController).to eq(true) end context 'while logged in as an admin' do @@ -17,7 +17,7 @@ describe Admin::DashboardController do context '.index' do it 'should be successful' do xhr :get, :index - response.should be_successful + expect(response).to be_successful end context 'version checking is enabled' do @@ -28,7 +28,7 @@ describe Admin::DashboardController do it 'returns discourse version info' do xhr :get, :index json = JSON.parse(response.body) - json['version_check'].should be_present + expect(json['version_check']).to be_present end end @@ -40,7 +40,7 @@ describe Admin::DashboardController do it 'does not return discourse version info' do xhr :get, :index json = JSON.parse(response.body) - json['version_check'].should_not be_present + expect(json['version_check']).not_to be_present end end end @@ -49,7 +49,7 @@ describe Admin::DashboardController do it 'should be successful' do AdminDashboardData.stubs(:fetch_problems).returns([]) xhr :get, :problems - response.should be_successful + expect(response).to be_successful end context 'when there are no problems' do @@ -60,7 +60,7 @@ describe Admin::DashboardController do it 'returns an empty array' do xhr :get, :problems json = JSON.parse(response.body) - json['problems'].size.should == 0 + expect(json['problems'].size).to eq(0) end end @@ -72,9 +72,9 @@ describe Admin::DashboardController do it 'returns an array of strings' do xhr :get, :problems json = JSON.parse(response.body) - json['problems'].size.should == 2 - json['problems'][0].should be_a(String) - json['problems'][1].should be_a(String) + expect(json['problems'].size).to eq(2) + expect(json['problems'][0]).to be_a(String) + expect(json['problems'][1]).to be_a(String) end end end diff --git a/spec/controllers/admin/email_controller_spec.rb b/spec/controllers/admin/email_controller_spec.rb index 1c914714ae..2820cb1543 100644 --- a/spec/controllers/admin/email_controller_spec.rb +++ b/spec/controllers/admin/email_controller_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Admin::EmailController do it "is a subclass of AdminController" do - (Admin::EmailController < Admin::AdminController).should == true + expect(Admin::EmailController < Admin::AdminController).to eq(true) end let!(:user) { log_in(:admin) } @@ -33,7 +33,7 @@ describe Admin::EmailController do end subject { response } - it { should be_success } + it { is_expected.to be_success } end context '.skipped' do @@ -42,12 +42,12 @@ describe Admin::EmailController do end subject { response } - it { should be_success } + it { is_expected.to be_success } end context '.test' do it 'raises an error without the email parameter' do - lambda { xhr :post, :test }.should raise_error(ActionController::ParameterMissing) + expect { xhr :post, :test }.to raise_error(ActionController::ParameterMissing) end context 'with an email address' do @@ -62,7 +62,7 @@ describe Admin::EmailController do context '.preview_digest' do it 'raises an error without the last_seen_at parameter' do - lambda { xhr :get, :preview_digest }.should raise_error(ActionController::ParameterMissing) + expect { xhr :get, :preview_digest }.to raise_error(ActionController::ParameterMissing) end it "previews the digest" do diff --git a/spec/controllers/admin/emojis_controller_spec.rb b/spec/controllers/admin/emojis_controller_spec.rb new file mode 100644 index 0000000000..dd0e68b853 --- /dev/null +++ b/spec/controllers/admin/emojis_controller_spec.rb @@ -0,0 +1,80 @@ +require 'spec_helper' + +describe Admin::EmojisController do + + let(:custom_emoji) do + Emoji.new("/path/to/hello").tap do |e| + e.name = "hello" + e.url = "/url/to/hello.png" + end + end + + it "is a subclass of AdminController" do + expect(Admin::EmojisController < Admin::AdminController).to eq(true) + end + + context "when logged in" do + let!(:user) { log_in(:admin) } + + context '.index' do + it "returns a list of custom emojis" do + Emoji.expects(:custom).returns([custom_emoji]) + xhr :get, :index + expect(response).to be_success + json = ::JSON.parse(response.body) + expect(json[0]['name']).to eq(custom_emoji.name) + expect(json[0]['url']).to eq(custom_emoji.url) + end + end + + context '.create' do + + before { Emoji.expects(:custom).returns([custom_emoji]) } + + context 'name already exist' do + it "throws an error" do + xhr :post, :create, { name: "hello", file: "" } + expect(response).not_to be_success + end + end + + context 'error while saving emoji' do + it "throws an error" do + Emoji.expects(:create_for).returns(nil) + xhr :post, :create, { name: "garbage", file: "" } + expect(response).not_to be_success + end + end + + context 'it works' do + let(:custom_emoji2) do + Emoji.new("/path/to/hello2").tap do |e| + e.name = "hello2" + e.url = "/url/to/hello2.png" + end + end + + it "creates a custom emoji" do + Emoji.expects(:create_for).returns(custom_emoji2) + xhr :post, :create, { name: "hello2", file: ""} + expect(response).to be_success + json = ::JSON.parse(response.body) + expect(json['name']).to eq(custom_emoji2.name) + expect(json['url']).to eq(custom_emoji2.url) + end + + end + end + + context '.destroy' do + it "deletes the custom emoji" do + custom_emoji.expects(:remove) + Emoji.expects(:custom).returns([custom_emoji]) + xhr :delete, :destroy, id: "hello" + expect(response).to be_success + end + end + end + +end + diff --git a/spec/controllers/admin/export_csv_controller_spec.rb b/spec/controllers/admin/export_csv_controller_spec.rb deleted file mode 100644 index cbf037e4cc..0000000000 --- a/spec/controllers/admin/export_csv_controller_spec.rb +++ /dev/null @@ -1,35 +0,0 @@ -require "spec_helper" - -describe Admin::ExportCsvController do - - it "is a subclass of AdminController" do - (Admin::ExportCsvController < Admin::AdminController).should == true - end - - let(:export_filename) { "export_b6a2bc87.csv" } - - context "while logged in as an admin" do - - before { @admin = log_in(:admin) } - - describe ".download" do - - it "uses send_file to transmit the export file" do - controller.stubs(:render) - export = ExportCsv.new() - ExportCsv.expects(:get_download_path).with(export_filename).returns(export) - subject.expects(:send_file).with(export) - get :download, id: export_filename - end - - it "returns 404 when the export file does not exist" do - ExportCsv.expects(:get_download_path).returns(nil) - get :download, id: export_filename - response.should be_not_found - end - - end - - end - -end diff --git a/spec/controllers/admin/flags_controller_spec.rb b/spec/controllers/admin/flags_controller_spec.rb index 766523e269..60ab321147 100644 --- a/spec/controllers/admin/flags_controller_spec.rb +++ b/spec/controllers/admin/flags_controller_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Admin::FlagsController do it "is a subclass of AdminController" do - (Admin::FlagsController < Admin::AdminController).should == true + expect(Admin::FlagsController < Admin::AdminController).to eq(true) end context 'while logged in as an admin' do @@ -16,8 +16,8 @@ describe Admin::FlagsController do xhr :get, :index data = ::JSON.parse(response.body) - data["users"].should == [] - data["posts"].should == [] + expect(data["users"]).to eq([]) + expect(data["posts"]).to eq([]) end it 'returns a valid json payload when some thing is flagged' do diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb index 7c5192ea33..f81e492b55 100644 --- a/spec/controllers/admin/groups_controller_spec.rb +++ b/spec/controllers/admin/groups_controller_spec.rb @@ -7,90 +7,149 @@ describe Admin::GroupsController do end it "is a subclass of AdminController" do - (Admin::GroupsController < Admin::AdminController).should == true + expect(Admin::GroupsController < Admin::AdminController).to eq(true) end - it "produces valid json for groups" do - group = Fabricate.build(:group, name: "test") - group.add(@admin) - group.save + context ".index" do + + it "produces valid json for groups" do + group = Fabricate.build(:group, name: "test") + group.add(@admin) + group.save + + xhr :get, :index + expect(response.status).to eq(200) + expect(::JSON.parse(response.body).keep_if {|r| r["id"] == group.id }).to eq([{ + "id"=>group.id, + "automatic"=>false, + "name"=>group.name, + "user_count"=>1, + "automatic"=>false, + "alias_level"=>0, + "visible"=>true, + "automatic_membership_email_domains"=>nil, + "automatic_membership_retroactive"=>false + }]) + end - xhr :get, :index - response.status.should == 200 - ::JSON.parse(response.body).keep_if{|r| r["id"] == group.id}.should == [{ - "id"=>group.id, - "name"=>group.name, - "user_count"=>1, - "automatic"=>false, - "alias_level"=>0, - "visible"=>true - }] end - it "is able to refresh automatic groups" do - Group.expects(:refresh_automatic_groups!).returns(true) + context ".create" do + + it "strip spaces on the group name" do + xhr :post, :create, name: " bob " + + expect(response.status).to eq(200) + + groups = Group.where(name: "bob").to_a + + expect(groups.count).to eq(1) + expect(groups[0].name).to eq("bob") + end - xhr :post, :refresh_automatic_groups - response.status.should == 200 end - context '.destroy' do + context ".update" do + + it "ignore name change on automatic group" do + xhr :put, :update, id: 1, name: "WAT", visible: "true" + expect(response).to be_success + + group = Group.find(1) + expect(group.name).not_to eq("WAT") + expect(group.visible).to eq(true) + end + + it "doesn't launch the 'automatic group membership' job when it's not retroactive" do + Jobs.expects(:enqueue).never + xhr :put, :update, id: 1, automatic_membership_retroactive: "false" + expect(response).to be_success + end + + it "launches the 'automatic group membership' job when it's retroactive" do + Jobs.expects(:enqueue).with(:automatic_group_membership, group_id: 1) + xhr :put, :update, id: 1, automatic_membership_retroactive: "true" + expect(response).to be_success + end + + end + + context ".destroy" do + it "returns a 422 if the group is automatic" do group = Fabricate(:group, automatic: true) xhr :delete, :destroy, id: group.id - response.status.should == 422 - Group.where(id: group.id).count.should == 1 + expect(response.status).to eq(422) + expect(Group.where(id: group.id).count).to eq(1) end it "is able to destroy a non-automatic group" do group = Fabricate(:group) xhr :delete, :destroy, id: group.id - response.status.should == 200 - Group.where(id: group.id).count.should == 0 - end - end - - context '.create' do - let(:usernames) { @admin.username } - - it "is able to create a group" do - xhr :post, :create, group: { - usernames: usernames, - name: "bob" - } - - response.status.should == 200 - - groups = Group.where(name: "bob").to_a - - groups.count.should == 1 - groups[0].usernames.should == usernames - groups[0].name.should == "bob" + expect(response.status).to eq(200) + expect(Group.where(id: group.id).count).to eq(0) end - it "strips spaces from group name" do - lambda { - xhr :post, :create, group: { - usernames: usernames, - name: " bob " - } - }.should_not raise_error() - Group.where(name: "bob").count.should == 1 + end + + context ".refresh_automatic_groups" do + + it "is able to refresh automatic groups" do + Group.expects(:refresh_automatic_groups!).returns(true) + + xhr :post, :refresh_automatic_groups + expect(response.status).to eq(200) end + end - it "is able to update group members" do - user1 = Fabricate(:user) - user2 = Fabricate(:user) - group = Fabricate(:group) + context ".add_members" do - xhr :put, :update, id: group.id, name: 'fred', group: { - name: 'fred', - usernames: "#{user1.username},#{user2.username}" - } + it "cannot add members to automatic groups" do + xhr :put, :add_members, id: 1, usernames: "l77t" + expect(response.status).to eq(422) + end + + it "is able to add several members to a group" do + user1 = Fabricate(:user) + user2 = Fabricate(:user) + group = Fabricate(:group) + + xhr :put, :add_members, id: group.id, usernames: [user1.username, user2.username].join(",") + + expect(response).to be_success + group.reload + expect(group.users.count).to eq(2) + end - group.reload - group.users.count.should == 2 - group.name.should == 'fred' end + + context ".remove_member" do + + it "cannot remove members from automatic groups" do + xhr :put, :remove_member, id: 1, user_id: 42 + expect(response.status).to eq(422) + end + + it "is able to remove a member" do + group = Fabricate(:group) + user = Fabricate(:user) + group.add(user) + group.save + + user.primary_group_id = group.id + user.save + + xhr :delete, :remove_member, id: group.id, user_id: user.id + + expect(response).to be_success + group.reload + expect(group.users.count).to eq(0) + + user.reload + expect(user.primary_group_id).to eq(nil) + end + + end + end diff --git a/spec/controllers/admin/impersonate_controller_spec.rb b/spec/controllers/admin/impersonate_controller_spec.rb index 841e72ff34..5130551ef8 100644 --- a/spec/controllers/admin/impersonate_controller_spec.rb +++ b/spec/controllers/admin/impersonate_controller_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Admin::ImpersonateController do it "is a subclass of AdminController" do - (Admin::ImpersonateController < Admin::AdminController).should == true + expect(Admin::ImpersonateController < Admin::AdminController).to eq(true) end context 'while logged in as an admin' do @@ -13,25 +13,25 @@ describe Admin::ImpersonateController do context 'index' do it 'returns success' do xhr :get, :index - response.should be_success + expect(response).to be_success end end context 'create' do it 'requires a username_or_email parameter' do - -> { xhr :put, :create }.should raise_error(ActionController::ParameterMissing) + expect { xhr :put, :create }.to raise_error(ActionController::ParameterMissing) end it 'returns 404 when that user does not exist' do xhr :post, :create, username_or_email: 'hedonismbot' - response.status.should == 404 + expect(response.status).to eq(404) end it "raises an invalid access error if the user can't be impersonated" do Guardian.any_instance.expects(:can_impersonate?).with(user).returns(false) xhr :post, :create, username_or_email: user.email - response.should be_forbidden + expect(response).to be_forbidden end context 'success' do @@ -43,17 +43,17 @@ describe Admin::ImpersonateController do it "changes the current user session id" do xhr :post, :create, username_or_email: user.username - session[:current_user_id].should == user.id + expect(session[:current_user_id]).to eq(user.id) end it "returns success" do xhr :post, :create, username_or_email: user.email - response.should be_success + expect(response).to be_success end it "also works with an email address" do xhr :post, :create, username_or_email: user.email - session[:current_user_id].should == user.id + expect(session[:current_user_id]).to eq(user.id) end end diff --git a/spec/controllers/admin/plugins_controller_spec.rb b/spec/controllers/admin/plugins_controller_spec.rb new file mode 100644 index 0000000000..f4b70bac0b --- /dev/null +++ b/spec/controllers/admin/plugins_controller_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe Admin::PluginsController do + + it "is a subclass of AdminController" do + expect(Admin::PluginsController < Admin::AdminController).to eq(true) + end + + context "while logged in as an admin" do + let!(:admin) { log_in(:admin) } + + it 'should return JSON' do + xhr :get, :index + response.should be_success + ::JSON.parse(response.body).has_key?('plugins').should == true + end + end + +end + diff --git a/spec/controllers/admin/reports_controller_spec.rb b/spec/controllers/admin/reports_controller_spec.rb index 5a3cf0ab03..b57ae429aa 100644 --- a/spec/controllers/admin/reports_controller_spec.rb +++ b/spec/controllers/admin/reports_controller_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Admin::ReportsController do it "is a subclass of AdminController" do - (Admin::ReportsController < Admin::AdminController).should == true + expect(Admin::ReportsController < Admin::AdminController).to eq(true) end context 'while logged in as an admin' do @@ -23,7 +23,7 @@ describe Admin::ReportsController do it "returns 404" do xhr :get, :show, type: invalid_id - response.status.should == 404 + expect(response.status).to eq(404) end end @@ -36,7 +36,7 @@ describe Admin::ReportsController do end it "renders the report as JSON" do - response.status.should == 404 + expect(response.status).to eq(404) end end @@ -47,11 +47,11 @@ describe Admin::ReportsController do end it "renders the report as JSON" do - response.should be_success + expect(response).to be_success end it "renders the report as JSON" do - ::JSON.parse(response.body).should be_present + expect(::JSON.parse(response.body)).to be_present end end diff --git a/spec/controllers/admin/screened_emails_controller_spec.rb b/spec/controllers/admin/screened_emails_controller_spec.rb index a68661f877..f05b80ce73 100644 --- a/spec/controllers/admin/screened_emails_controller_spec.rb +++ b/spec/controllers/admin/screened_emails_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Admin::ScreenedEmailsController do it "is a subclass of AdminController" do - (Admin::ScreenedEmailsController < Admin::AdminController).should == true + expect(Admin::ScreenedEmailsController < Admin::AdminController).to eq(true) end let!(:user) { log_in(:admin) } @@ -13,10 +13,10 @@ describe Admin::ScreenedEmailsController do end subject { response } - it { should be_success } + it { is_expected.to be_success } it 'returns JSON' do - ::JSON.parse(subject.body).should be_a(Array) + expect(::JSON.parse(subject.body)).to be_a(Array) end end end diff --git a/spec/controllers/admin/screened_ip_addresses_controller_spec.rb b/spec/controllers/admin/screened_ip_addresses_controller_spec.rb index edd6af13bb..e814c7abf1 100644 --- a/spec/controllers/admin/screened_ip_addresses_controller_spec.rb +++ b/spec/controllers/admin/screened_ip_addresses_controller_spec.rb @@ -1,22 +1,72 @@ require 'spec_helper' describe Admin::ScreenedIpAddressesController do + it "is a subclass of AdminController" do - (Admin::ScreenedIpAddressesController < Admin::AdminController).should == true + expect(Admin::ScreenedIpAddressesController < Admin::AdminController).to eq(true) end let!(:user) { log_in(:admin) } describe 'index' do - before do - xhr :get, :index + + it 'filters screened ip addresses' do + Fabricate(:screened_ip_address, ip_address: "1.2.3.4") + Fabricate(:screened_ip_address, ip_address: "1.2.3.5") + Fabricate(:screened_ip_address, ip_address: "1.2.3.6") + Fabricate(:screened_ip_address, ip_address: "4.5.6.7") + + xhr :get, :index, filter: "4.*" + + expect(response).to be_success + + result = JSON.parse(response.body) + expect(result.length).to eq(1) end - subject { response } - it { should be_success } - - it 'returns JSON' do - ::JSON.parse(subject.body).should be_a(Array) - end end + + describe 'roll_up' do + + it "rolls up 1.2.3.* entries" do + Fabricate(:screened_ip_address, ip_address: "1.2.3.4", match_count: 1) + Fabricate(:screened_ip_address, ip_address: "1.2.3.5", match_count: 1) + Fabricate(:screened_ip_address, ip_address: "1.2.3.6", match_count: 1) + + Fabricate(:screened_ip_address, ip_address: "42.42.42.4", match_count: 1) + Fabricate(:screened_ip_address, ip_address: "42.42.42.5", match_count: 1) + + StaffActionLogger.any_instance.expects(:log_roll_up) + SiteSetting.stubs(:min_ban_entries_for_roll_up).returns(3) + + xhr :post, :roll_up + expect(response).to be_success + + subnet = ScreenedIpAddress.where(ip_address: "1.2.3.0/24").first + expect(subnet).to be_present + expect(subnet.match_count).to eq(3) + end + + it "rolls up 1.2.*.* entries" do + Fabricate(:screened_ip_address, ip_address: "1.2.3.4", match_count: 1) + Fabricate(:screened_ip_address, ip_address: "1.2.3.5", match_count: 1) + Fabricate(:screened_ip_address, ip_address: "1.2.4.6", match_count: 1) + Fabricate(:screened_ip_address, ip_address: "1.2.7.8", match_count: 1) + Fabricate(:screened_ip_address, ip_address: "1.2.9.1", match_count: 1) + + Fabricate(:screened_ip_address, ip_address: "1.2.42.0/24", match_count: 1) + + StaffActionLogger.any_instance.expects(:log_roll_up) + SiteSetting.stubs(:min_ban_entries_for_roll_up).returns(5) + + xhr :post, :roll_up + expect(response).to be_success + + subnet = ScreenedIpAddress.where(ip_address: "1.2.0.0/16").first + expect(subnet).to be_present + expect(subnet.match_count).to eq(6) + end + + end + end diff --git a/spec/controllers/admin/screened_urls_controller_spec.rb b/spec/controllers/admin/screened_urls_controller_spec.rb index 0966ee95d8..8d9fce01f0 100644 --- a/spec/controllers/admin/screened_urls_controller_spec.rb +++ b/spec/controllers/admin/screened_urls_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Admin::ScreenedUrlsController do it "is a subclass of AdminController" do - (Admin::ScreenedUrlsController < Admin::AdminController).should == true + expect(Admin::ScreenedUrlsController < Admin::AdminController).to eq(true) end let!(:user) { log_in(:admin) } @@ -13,10 +13,10 @@ describe Admin::ScreenedUrlsController do end subject { response } - it { should be_success } + it { is_expected.to be_success } it 'returns JSON' do - ::JSON.parse(subject.body).should be_a(Array) + expect(::JSON.parse(subject.body)).to be_a(Array) end end end diff --git a/spec/controllers/admin/site_customizations_controller_spec.rb b/spec/controllers/admin/site_customizations_controller_spec.rb index 944c1e374e..f8b598b9f3 100644 --- a/spec/controllers/admin/site_customizations_controller_spec.rb +++ b/spec/controllers/admin/site_customizations_controller_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Admin::SiteCustomizationsController do it "is a subclass of AdminController" do - (Admin::UsersController < Admin::AdminController).should == true + expect(Admin::UsersController < Admin::AdminController).to eq(true) end context 'while logged in as an admin' do @@ -15,24 +15,24 @@ describe Admin::SiteCustomizationsController do it 'returns success' do SiteCustomization.create!(name: 'my name', user_id: Fabricate(:user).id, header: "my awesome header", stylesheet: "my awesome css") xhr :get, :index - response.should be_success + expect(response).to be_success end it 'returns JSON' do xhr :get, :index - ::JSON.parse(response.body).should be_present + expect(::JSON.parse(response.body)).to be_present end end context ' .create' do it 'returns success' do xhr :post, :create, site_customization: {name: 'my test name'} - response.should be_success + expect(response).to be_success end it 'returns json' do xhr :post, :create, site_customization: {name: 'my test name'} - ::JSON.parse(response.body).should be_present + expect(::JSON.parse(response.body)).to be_present end it 'logs the change' do diff --git a/spec/controllers/admin/site_settings_controller_spec.rb b/spec/controllers/admin/site_settings_controller_spec.rb index 030e27c9bb..702598c8f5 100644 --- a/spec/controllers/admin/site_settings_controller_spec.rb +++ b/spec/controllers/admin/site_settings_controller_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Admin::SiteSettingsController do it "is a subclass of AdminController" do - (Admin::SiteSettingsController < Admin::AdminController).should == true + expect(Admin::SiteSettingsController < Admin::AdminController).to eq(true) end context 'while logged in as an admin' do @@ -14,12 +14,12 @@ describe Admin::SiteSettingsController do context 'index' do it 'returns success' do xhr :get, :index - response.should be_success + expect(response).to be_success end it 'returns JSON' do xhr :get, :index - ::JSON.parse(response.body).should be_present + expect(::JSON.parse(response.body)).to be_present end end diff --git a/spec/controllers/admin/site_text_controller_spec.rb b/spec/controllers/admin/site_text_controller_spec.rb index d6eb57b91d..615710564d 100644 --- a/spec/controllers/admin/site_text_controller_spec.rb +++ b/spec/controllers/admin/site_text_controller_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Admin::SiteTextController do it "is a subclass of AdminController" do - (Admin::SiteTextController < Admin::AdminController).should == true + expect(Admin::SiteTextController < Admin::AdminController).to eq(true) end context 'while logged in as an admin' do @@ -16,12 +16,12 @@ describe Admin::SiteTextController do it 'returns success' do xhr :get, :show, id: text_type - response.should be_success + expect(response).to be_success end it 'returns JSON' do xhr :get, :show, id: text_type - ::JSON.parse(response.body).should be_present + expect(::JSON.parse(response.body)).to be_present end end end diff --git a/spec/controllers/admin/site_text_types_controller_spec.rb b/spec/controllers/admin/site_text_types_controller_spec.rb index eb1c349e02..d2431f60c8 100644 --- a/spec/controllers/admin/site_text_types_controller_spec.rb +++ b/spec/controllers/admin/site_text_types_controller_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Admin::SiteTextTypesController do it "is a subclass of AdminController" do - (Admin::SiteTextTypesController < Admin::AdminController).should == true + expect(Admin::SiteTextTypesController < Admin::AdminController).to eq(true) end context 'while logged in as an admin' do @@ -14,12 +14,12 @@ describe Admin::SiteTextTypesController do context ' .index' do it 'returns success' do xhr :get, :index - response.should be_success + expect(response).to be_success end it 'returns JSON' do xhr :get, :index - ::JSON.parse(response.body).should be_present + expect(::JSON.parse(response.body)).to be_present end end end diff --git a/spec/controllers/admin/staff_action_logs_controller_spec.rb b/spec/controllers/admin/staff_action_logs_controller_spec.rb index 8decc8b6a7..8f638a33d7 100644 --- a/spec/controllers/admin/staff_action_logs_controller_spec.rb +++ b/spec/controllers/admin/staff_action_logs_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Admin::StaffActionLogsController do it "is a subclass of AdminController" do - (Admin::StaffActionLogsController < Admin::AdminController).should == true + expect(Admin::StaffActionLogsController < Admin::AdminController).to eq(true) end let!(:user) { log_in(:admin) } @@ -13,10 +13,10 @@ describe Admin::StaffActionLogsController do end subject { response } - it { should be_success } + it { is_expected.to be_success } it 'returns JSON' do - ::JSON.parse(subject.body).should be_a(Array) + expect(::JSON.parse(subject.body)).to be_a(Array) end end end diff --git a/spec/controllers/admin/user_fields_controller_spec.rb b/spec/controllers/admin/user_fields_controller_spec.rb index 5e6165f5a2..6b173cb91d 100644 --- a/spec/controllers/admin/user_fields_controller_spec.rb +++ b/spec/controllers/admin/user_fields_controller_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Admin::UserFieldsController do it "is a subclass of AdminController" do - (Admin::ApiController < Admin::AdminController).should == true + expect(Admin::UserFieldsController < Admin::AdminController).to eq(true) end context "when logged in" do @@ -11,10 +11,10 @@ describe Admin::UserFieldsController do context '.create' do it "creates a user field" do - -> { + expect { xhr :post, :create, {user_field: {name: 'hello', description: 'hello desc', field_type: 'text'} } - response.should be_success - }.should change(UserField, :count).by(1) + expect(response).to be_success + }.to change(UserField, :count).by(1) end end @@ -23,9 +23,9 @@ describe Admin::UserFieldsController do it "returns a list of user fields" do xhr :get, :index - response.should be_success + expect(response).to be_success json = ::JSON.parse(response.body) - json['user_fields'].should be_present + expect(json['user_fields']).to be_present end end @@ -33,10 +33,10 @@ describe Admin::UserFieldsController do let!(:user_field) { Fabricate(:user_field) } it "deletes the user field" do - -> { + expect { xhr :delete, :destroy, id: user_field.id - response.should be_success - }.should change(UserField, :count).by(-1) + expect(response).to be_success + }.to change(UserField, :count).by(-1) end end @@ -45,10 +45,10 @@ describe Admin::UserFieldsController do it "updates the user field" do xhr :put, :update, id: user_field.id, user_field: {name: 'fraggle', field_type: 'confirm', description: 'muppet'} - response.should be_success + expect(response).to be_success user_field.reload - user_field.name.should == 'fraggle' - user_field.field_type.should == 'confirm' + expect(user_field.name).to eq('fraggle') + expect(user_field.field_type).to eq('confirm') end end end diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb index 767dc021f1..97434b3241 100644 --- a/spec/controllers/admin/users_controller_spec.rb +++ b/spec/controllers/admin/users_controller_spec.rb @@ -4,7 +4,7 @@ require_dependency 'single_sign_on' describe Admin::UsersController do it 'is a subclass of AdminController' do - (Admin::UsersController < Admin::AdminController).should == true + expect(Admin::UsersController < Admin::AdminController).to eq(true) end context 'while logged in as an admin' do @@ -15,12 +15,12 @@ describe Admin::UsersController do context '.index' do it 'returns success' do xhr :get, :index - response.should be_success + expect(response).to be_success end it 'returns JSON' do xhr :get, :index - ::JSON.parse(response.body).should be_present + expect(::JSON.parse(response.body)).to be_present end context 'when showing emails' do @@ -29,17 +29,17 @@ describe Admin::UsersController do xhr :get, :index, show_emails: "true" data = ::JSON.parse(response.body) data.each do |user| - user["email"].should be_present + expect(user["email"]).to be_present end end - it "logs an enty for all email shown" do - UserHistory.where(action: UserHistory.actions[:check_email], acting_user_id: @user.id).count.should == 0 + it "logs only 1 enty" do + expect(UserHistory.where(action: UserHistory.actions[:check_email], acting_user_id: @user.id).count).to eq(0) xhr :get, :index, show_emails: "true" data = ::JSON.parse(response.body) - UserHistory.where(action: UserHistory.actions[:check_email], acting_user_id: @user.id).count.should == data.length + expect(UserHistory.where(action: UserHistory.actions[:check_email], acting_user_id: @user.id).count).to eq(1) end end @@ -49,14 +49,14 @@ describe Admin::UsersController do context 'an existing user' do it 'returns success' do xhr :get, :show, id: @user.username - response.should be_success + expect(response).to be_success end end context 'an existing user' do it 'returns success' do xhr :get, :show, id: 'foobar' - response.should_not be_success + expect(response).not_to be_success end end end @@ -111,7 +111,7 @@ describe Admin::UsersController do it "raises an error when the user doesn't have permission" do Guardian.any_instance.expects(:can_approve?).with(evil_trout).returns(false) xhr :put, :approve, user_id: evil_trout.id - response.should be_forbidden + expect(response).to be_forbidden end it 'calls approve' do @@ -129,13 +129,13 @@ describe Admin::UsersController do it 'raises an error unless the user can revoke access' do Guardian.any_instance.expects(:can_revoke_admin?).with(@another_admin).returns(false) xhr :put, :revoke_admin, user_id: @another_admin.id - response.should be_forbidden + expect(response).to be_forbidden end it 'updates the admin flag' do xhr :put, :revoke_admin, user_id: @another_admin.id @another_admin.reload - @another_admin.should_not be_admin + expect(@another_admin).not_to be_admin end end @@ -147,18 +147,18 @@ describe Admin::UsersController do it "raises an error when the user doesn't have permission" do Guardian.any_instance.expects(:can_grant_admin?).with(@another_user).returns(false) xhr :put, :grant_admin, user_id: @another_user.id - response.should be_forbidden + expect(response).to be_forbidden end it "returns a 404 if the username doesn't exist" do xhr :put, :grant_admin, user_id: 123123 - response.should be_forbidden + expect(response).to be_forbidden end it 'updates the admin flag' do xhr :put, :grant_admin, user_id: @another_user.id @another_user.reload - @another_user.should be_admin + expect(@another_user).to be_admin end end @@ -170,18 +170,18 @@ describe Admin::UsersController do it "raises an error when the user doesn't have permission" do Guardian.any_instance.expects(:can_change_primary_group?).with(@another_user).returns(false) xhr :put, :primary_group, user_id: @another_user.id - response.should be_forbidden + expect(response).to be_forbidden end it "returns a 404 if the user doesn't exist" do xhr :put, :primary_group, user_id: 123123 - response.should be_forbidden + expect(response).to be_forbidden end it "changes the user's primary group" do xhr :put, :primary_group, user_id: @another_user.id, primary_group_id: 2 @another_user.reload - @another_user.primary_group_id.should == 2 + expect(@another_user.primary_group_id).to eq(2) end end @@ -193,20 +193,20 @@ describe Admin::UsersController do it "raises an error when the user doesn't have permission" do Guardian.any_instance.expects(:can_change_trust_level?).with(@another_user).returns(false) xhr :put, :trust_level, user_id: @another_user.id - response.should_not be_success + expect(response).not_to be_success end it "returns a 404 if the username doesn't exist" do xhr :put, :trust_level, user_id: 123123 - response.should_not be_success + expect(response).not_to be_success end it "upgrades the user's trust level" do StaffActionLogger.any_instance.expects(:log_trust_level_change).with(@another_user, @another_user.trust_level, 2).once xhr :put, :trust_level, user_id: @another_user.id, level: 2 @another_user.reload - @another_user.trust_level.should == 2 - response.should be_success + expect(@another_user.trust_level).to eq(2) + expect(response).to be_success end it "raises no error when demoting a user below their current trust level (locks trust level)" do @@ -217,9 +217,9 @@ describe Admin::UsersController do stat.save! @another_user.update_attributes(trust_level: TrustLevel[1]) xhr :put, :trust_level, user_id: @another_user.id, level: TrustLevel[0] - response.should be_success + expect(response).to be_success @another_user.reload - @another_user.trust_level_locked.should == true + expect(@another_user.trust_level_locked).to eq(true) end end @@ -231,13 +231,13 @@ describe Admin::UsersController do it 'raises an error unless the user can revoke access' do Guardian.any_instance.expects(:can_revoke_moderation?).with(@moderator).returns(false) xhr :put, :revoke_moderation, user_id: @moderator.id - response.should be_forbidden + expect(response).to be_forbidden end it 'updates the moderator flag' do xhr :put, :revoke_moderation, user_id: @moderator.id @moderator.reload - @moderator.moderator.should_not == true + expect(@moderator.moderator).not_to eq(true) end end @@ -249,18 +249,18 @@ describe Admin::UsersController do it "raises an error when the user doesn't have permission" do Guardian.any_instance.expects(:can_grant_moderation?).with(@another_user).returns(false) xhr :put, :grant_moderation, user_id: @another_user.id - response.should be_forbidden + expect(response).to be_forbidden end it "returns a 404 if the username doesn't exist" do xhr :put, :grant_moderation, user_id: 123123 - response.should be_forbidden + expect(response).to be_forbidden end it 'updates the moderator flag' do xhr :put, :grant_moderation, user_id: @another_user.id @another_user.reload - @another_user.moderator.should == true + expect(@another_user.moderator).to eq(true) end end @@ -283,10 +283,10 @@ describe Admin::UsersController do Guardian.any_instance.stubs(:can_delete_user?).returns(true) UserDestroyer.any_instance.stubs(:destroy).returns(true) xhr :delete, :reject_bulk, users: [reject_me.id, reject_me_too.id] - response.should be_success + expect(response).to be_success json = ::JSON.parse(response.body) - json['success'].to_i.should == 2 - json['failed'].to_i.should == 0 + expect(json['success'].to_i).to eq(2) + expect(json['failed'].to_i).to eq(0) end context 'failures' do @@ -298,19 +298,19 @@ describe Admin::UsersController do UserDestroyer.any_instance.stubs(:destroy).with(reject_me, anything).returns(false) UserDestroyer.any_instance.stubs(:destroy).with(reject_me_too, anything).returns(true) xhr :delete, :reject_bulk, users: [reject_me.id, reject_me_too.id] - response.should be_success + expect(response).to be_success json = ::JSON.parse(response.body) - json['success'].to_i.should == 1 - json['failed'].to_i.should == 1 + expect(json['success'].to_i).to eq(1) + expect(json['failed'].to_i).to eq(1) end it 'reports failure due to a user still having posts' do UserDestroyer.any_instance.expects(:destroy).with(reject_me, anything).raises(UserDestroyer::PostsExistError) xhr :delete, :reject_bulk, users: [reject_me.id] - response.should be_success + expect(response).to be_success json = ::JSON.parse(response.body) - json['success'].to_i.should == 0 - json['failed'].to_i.should == 1 + expect(json['success'].to_i).to eq(0) + expect(json['failed'].to_i).to eq(1) end end end @@ -323,12 +323,12 @@ describe Admin::UsersController do it "raises an error when the user doesn't have permission" do Guardian.any_instance.expects(:can_delete_user?).with(@delete_me).returns(false) xhr :delete, :destroy, id: @delete_me.id - response.should be_forbidden + expect(response).to be_forbidden end it "returns a 403 if the user doesn't exist" do xhr :delete, :destroy, id: 123123 - response.should be_forbidden + expect(response).to be_forbidden end context "user has post" do @@ -343,13 +343,13 @@ describe Admin::UsersController do it "returns an error" do xhr :delete, :destroy, id: @delete_me.id - response.should be_forbidden + expect(response).to be_forbidden end it "doesn't return an error if delete_posts == true" do UserDestroyer.any_instance.expects(:destroy).with(@user, has_entry('delete_posts' => true)).returns(true) xhr :delete, :destroy, id: @delete_me.id, delete_posts: true - response.should be_success + expect(response).to be_success end end @@ -360,6 +360,37 @@ describe Admin::UsersController do end end + context 'activate' do + before do + @reg_user = Fabricate(:inactive_user) + end + + it "returns success" do + xhr :put, :activate, user_id: @reg_user.id + expect(response).to be_success + json = ::JSON.parse(response.body) + expect(json['success']).to eq("OK") + end + end + + context 'log_out' do + before do + @reg_user = Fabricate(:user) + end + + it "returns success" do + xhr :put, :log_out, user_id: @reg_user.id + expect(response).to be_success + json = ::JSON.parse(response.body) + expect(json['success']).to eq("OK") + end + + it "returns 404 when user_id does not exist" do + xhr :put, :log_out, user_id: 123123 + expect(response).not_to be_success + end + end + context 'block' do before do @reg_user = Fabricate(:user) @@ -369,12 +400,12 @@ describe Admin::UsersController do Guardian.any_instance.expects(:can_block_user?).with(@reg_user).returns(false) UserBlocker.expects(:block).never xhr :put, :block, user_id: @reg_user.id - response.should be_forbidden + expect(response).to be_forbidden end it "returns a 403 if the user doesn't exist" do xhr :put, :block, user_id: 123123 - response.should be_forbidden + expect(response).to be_forbidden end it "punishes the user for spamming" do @@ -391,12 +422,12 @@ describe Admin::UsersController do it "raises an error when the user doesn't have permission" do Guardian.any_instance.expects(:can_unblock_user?).with(@reg_user).returns(false) xhr :put, :unblock, user_id: @reg_user.id - response.should be_forbidden + expect(response).to be_forbidden end it "returns a 403 if the user doesn't exist" do xhr :put, :unblock, user_id: 123123 - response.should be_forbidden + expect(response).to be_forbidden end it "punishes the user for spamming" do @@ -414,6 +445,40 @@ describe Admin::UsersController do end + context "delete_other_accounts_with_same_ip" do + + it "works" do + Fabricate(:user, ip_address: "42.42.42.42") + Fabricate(:user, ip_address: "42.42.42.42") + + UserDestroyer.any_instance.expects(:destroy).twice + + xhr :delete, :delete_other_accounts_with_same_ip, ip: "42.42.42.42", exclude: -1, order: "trust_level DESC" + end + + end + + context ".invite_admin" do + it 'should invite admin' do + Jobs.expects(:enqueue).with(:user_email, anything).returns(true) + xhr :post, :invite_admin, name: 'Bill', username: 'bill22', email: 'bill@bill.com' + expect(response).to be_success + + u = User.find_by(email: 'bill@bill.com') + expect(u.name).to eq("Bill") + expect(u.username).to eq("bill22") + expect(u.admin).to eq(true) + end + + it "doesn't send the email with send_email falsy" do + Jobs.expects(:enqueue).with(:user_email, anything).never + xhr :post, :invite_admin, name: 'Bill', username: 'bill22', email: 'bill@bill.com', send_email: '0' + expect(response).to be_success + json = ::JSON.parse(response.body) + expect(json["password_url"]).to be_present + end + end + end it 'can sync up sso' do @@ -442,12 +507,12 @@ describe Admin::UsersController do sso.email = "bob2@bob.com" xhr :post, :sync_sso, Rack::Utils.parse_query(sso.payload) - response.should be_success + expect(response).to be_success user.reload - user.email.should == "bob2@bob.com" - user.name.should == "Bill" - user.username.should == "Hokli" + expect(user.email).to eq("bob2@bob.com") + expect(user.name).to eq("Bill") + expect(user.username).to eq("Hokli") end diff --git a/spec/controllers/admin/versions_controller_spec.rb b/spec/controllers/admin/versions_controller_spec.rb index a6ba9fc9c6..deca11ecc4 100644 --- a/spec/controllers/admin/versions_controller_spec.rb +++ b/spec/controllers/admin/versions_controller_spec.rb @@ -11,7 +11,7 @@ describe Admin::VersionsController do end it "is a subclass of AdminController" do - (Admin::VersionsController < Admin::AdminController).should == true + expect(Admin::VersionsController < Admin::AdminController).to eq(true) end context 'while logged in as an admin' do @@ -21,16 +21,16 @@ describe Admin::VersionsController do describe 'show' do subject { xhr :get, :show } - it { should be_success } + it { is_expected.to be_success } it 'should return the currently available version' do json = JSON.parse(subject.body) - json['latest_version'].should == '1.2.33' + expect(json['latest_version']).to eq('1.2.33') end it "should return the installed version" do json = JSON.parse(subject.body) - json['installed_version'].should == Discourse::VERSION::STRING + expect(json['installed_version']).to eq(Discourse::VERSION::STRING) end end end diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index c927de399a..b42d1fa3a2 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -14,14 +14,14 @@ describe TopicsController do end it "doesn't store an incoming link when there's no referer" do - lambda { + expect { get :show, id: topic.id - }.should_not change(IncomingLink, :count) + }.not_to change(IncomingLink, :count) end it "doesn't raise an error on a very long link" do set_referer("http://#{'a' * 2000}.com") - lambda { get :show, {id: topic.id} }.should_not raise_error + expect { get :show, {id: topic.id} }.not_to raise_error end describe "has_escaped_fragment?" do @@ -34,7 +34,7 @@ describe TopicsController do it "uses the application layout even with an escaped fragment param" do get :show, {'topic_id' => topic.id, 'slug' => topic.slug, '_escaped_fragment_' => 'true'} - response.should render_template(layout: 'application') + expect(response).to render_template(layout: 'application') assert_select "meta[name=fragment]", false, "it doesn't have the meta tag" end end @@ -46,13 +46,13 @@ describe TopicsController do it "uses the application layout when there's no param" do get :show, topic_id: topic.id, slug: topic.slug - response.should render_template(layout: 'application') + expect(response).to render_template(layout: 'application') assert_select "meta[name=fragment]", true, "it has the meta tag" end it "uses the crawler layout when there's an _escaped_fragment_ param" do get :show, topic_id: topic.id, slug: topic.slug, _escaped_fragment_: 'true' - response.should render_template(layout: 'crawler') + expect(response).to render_template(layout: 'crawler') assert_select "meta[name=fragment]", false, "it doesn't have the meta tag" end end @@ -67,7 +67,7 @@ describe TopicsController do end it "renders with the application layout" do get :show, topic_id: topic.id, slug: topic.slug - response.should render_template(layout: 'application') + expect(response).to render_template(layout: 'application') assert_select "meta[name=fragment]", true, "it has the meta tag" end end @@ -78,7 +78,7 @@ describe TopicsController do end it "renders with the crawler layout" do get :show, topic_id: topic.id, slug: topic.slug - response.should render_template(layout: 'crawler') + expect(response).to render_template(layout: 'crawler') assert_select "meta[name=fragment]", false, "it doesn't have the meta tag" end end @@ -94,7 +94,7 @@ describe TopicsController do get :show, {topic_id: topic.id} - I18n.locale.should == :fr + expect(I18n.locale).to eq(:fr) end it 'is sets the default locale when the setting not enabled' do @@ -103,7 +103,7 @@ describe TopicsController do get :show, {topic_id: topic.id} - I18n.locale.should == :en + expect(I18n.locale).to eq(:en) end end @@ -135,31 +135,31 @@ describe 'api' do it 'allows users with api key to bookmark posts' do PostAction.expects(:act).with(user, post, PostActionType.types[:bookmark]).once put :bookmark, bookmarked: "true", post_id: post.id, api_key: api_key.key, format: :json - response.should be_success + expect(response).to be_success end it 'raises an error with a user key that does not match an optionally specified username' do PostAction.expects(:act).with(user, post, PostActionType.types[:bookmark]).never put :bookmark, bookmarked: "true", post_id: post.id, api_key: api_key.key, api_username: 'made_up', format: :json - response.should_not be_success + expect(response).not_to be_success end it 'allows users with a master api key to bookmark posts' do PostAction.expects(:act).with(user, post, PostActionType.types[:bookmark]).once put :bookmark, bookmarked: "true", post_id: post.id, api_key: master_key.key, api_username: user.username, format: :json - response.should be_success + expect(response).to be_success end it 'disallows phonies to bookmark posts' do PostAction.expects(:act).with(user, post, PostActionType.types[:bookmark]).never put :bookmark, bookmarked: "true", post_id: post.id, api_key: SecureRandom.hex(32), api_username: user.username, format: :json - response.code.to_i.should == 403 + expect(response.code.to_i).to eq(403) end it 'disallows blank api' do PostAction.expects(:act).with(user, post, PostActionType.types[:bookmark]).never put :bookmark, bookmarked: "true", post_id: post.id, api_key: "", api_username: user.username, format: :json - response.code.to_i.should == 403 + expect(response.code.to_i).to eq(403) end end end diff --git a/spec/controllers/badges_controller_spec.rb b/spec/controllers/badges_controller_spec.rb index f45ffd5078..ab98f35cf8 100644 --- a/spec/controllers/badges_controller_spec.rb +++ b/spec/controllers/badges_controller_spec.rb @@ -12,26 +12,26 @@ describe BadgesController do it 'should return a list of all badges' do get :index, format: :json - response.status.should == 200 + expect(response.status).to eq(200) parsed = JSON.parse(response.body) - parsed["badges"].length.should == Badge.count + expect(parsed["badges"].length).to eq(Badge.count) end end context 'show' do it "should return a badge" do get :show, id: badge.id, format: :json - response.status.should == 200 + expect(response.status).to eq(200) parsed = JSON.parse(response.body) - parsed["badge"].should be_present + expect(parsed["badge"]).to be_present end it "should mark the notification as viewed" do log_in_user(user) user_badge = BadgeGranter.grant(badge, user) - user_badge.notification.read.should == false + expect(user_badge.notification.read).to eq(false) get :show, id: badge.id, format: :json - user_badge.notification.reload.read.should == true + expect(user_badge.notification.reload.read).to eq(true) end end end diff --git a/spec/controllers/categories_controller_spec.rb b/spec/controllers/categories_controller_spec.rb index 08b731d6df..9a7d342ae6 100644 --- a/spec/controllers/categories_controller_spec.rb +++ b/spec/controllers/categories_controller_spec.rb @@ -4,7 +4,7 @@ describe CategoriesController do describe "create" do it "requires the user to be logged in" do - lambda { xhr :post, :create }.should raise_error(Discourse::NotLoggedIn) + expect { xhr :post, :create }.to raise_error(Discourse::NotLoggedIn) end describe "logged in" do @@ -15,19 +15,19 @@ describe CategoriesController do it "raises an exception when they don't have permission to create it" do Guardian.any_instance.expects(:can_create?).with(Category, nil).returns(false) xhr :post, :create, name: 'hello', color: 'ff0', text_color: 'fff' - response.should be_forbidden + expect(response).to be_forbidden end it "raises an exception when the name is missing" do - lambda { xhr :post, :create, color: "ff0", text_color: "fff" }.should raise_error(ActionController::ParameterMissing) + expect { xhr :post, :create, color: "ff0", text_color: "fff" }.to raise_error(ActionController::ParameterMissing) end it "raises an exception when the color is missing" do - lambda { xhr :post, :create, name: "hello", text_color: "fff" }.should raise_error(ActionController::ParameterMissing) + expect { xhr :post, :create, name: "hello", text_color: "fff" }.to raise_error(ActionController::ParameterMissing) end it "raises an exception when the text color is missing" do - lambda { xhr :post, :create, name: "hello", color: "ff0" }.should raise_error(ActionController::ParameterMissing) + expect { xhr :post, :create, name: "hello", color: "ff0" }.to raise_error(ActionController::ParameterMissing) end describe "failure" do @@ -36,34 +36,34 @@ describe CategoriesController do xhr :post, :create, name: @category.name, color: "ff0", text_color: "fff" end - it { should_not respond_with(:success) } + it { is_expected.not_to respond_with(:success) } it "returns errors on a duplicate category name" do - response.status.should == 422 + expect(response.status).to eq(422) end end - describe "success" do it "works" do readonly = CategoryGroup.permission_types[:readonly] create_post = CategoryGroup.permission_types[:create_post] - xhr :post, :create, name: "hello", color: "ff0", text_color: "fff", + xhr :post, :create, name: "hello", color: "ff0", text_color: "fff", slug: "hello-cat", auto_close_hours: 72, permissions: { "everyone" => readonly, "staff" => create_post } - response.status.should == 200 + expect(response.status).to eq(200) category = Category.find_by(name: "hello") - category.category_groups.map{|g| [g.group_id, g.permission_type]}.sort.should == [ + expect(category.category_groups.map{|g| [g.group_id, g.permission_type]}.sort).to eq([ [Group[:everyone].id, readonly],[Group[:staff].id,create_post] - ] - category.name.should == "hello" - category.color.should == "ff0" - category.auto_close_hours.should == 72 + ]) + expect(category.name).to eq("hello") + expect(category.slug).to eq("hello-cat") + expect(category.color).to eq("ff0") + expect(category.auto_close_hours).to eq(72) end end end @@ -72,7 +72,7 @@ describe CategoriesController do describe "destroy" do it "requires the user to be logged in" do - lambda { xhr :delete, :destroy, id: "category"}.should raise_error(Discourse::NotLoggedIn) + expect { xhr :delete, :destroy, id: "category"}.to raise_error(Discourse::NotLoggedIn) end describe "logged in" do @@ -84,12 +84,12 @@ describe CategoriesController do it "raises an exception if they don't have permission to delete it" do Guardian.any_instance.expects(:can_delete_category?).returns(false) xhr :delete, :destroy, id: @category.slug - response.should be_forbidden + expect(response).to be_forbidden end it "deletes the record" do Guardian.any_instance.expects(:can_delete_category?).returns(true) - lambda { xhr :delete, :destroy, id: @category.slug}.should change(Category, :count).by(-1) + expect { xhr :delete, :destroy, id: @category.slug}.to change(Category, :count).by(-1) end end @@ -97,7 +97,7 @@ describe CategoriesController do describe "upload" do it "requires the user to be logged in" do - lambda { xhr :post, :upload, image_type: 'logo'}.should raise_error(Discourse::NotLoggedIn) + expect { xhr :post, :upload, image_type: 'logo'}.to raise_error(Discourse::NotLoggedIn) end describe "logged in" do @@ -111,17 +111,17 @@ describe CategoriesController do it "raises an error when you don't have permission to upload" do Guardian.any_instance.expects(:can_create?).with(Category).returns(false) xhr :post, :upload, image_type: 'logo', file: upload - response.should be_forbidden + expect(response).to be_forbidden end it "requires the `image_type` param" do - -> { xhr :post, :upload }.should raise_error(ActionController::ParameterMissing) + expect { xhr :post, :upload }.to raise_error(ActionController::ParameterMissing) end it "calls Upload.create_for" do Upload.expects(:create_for).returns(Upload.new) xhr :post, :upload, image_type: 'logo', file: upload - response.should be_success + expect(response).to be_success end end end @@ -129,7 +129,7 @@ describe CategoriesController do describe "update" do it "requires the user to be logged in" do - lambda { xhr :put, :update, id: 'category'}.should raise_error(Discourse::NotLoggedIn) + expect { xhr :put, :update, id: 'category'}.to raise_error(Discourse::NotLoggedIn) end @@ -144,19 +144,19 @@ describe CategoriesController do it "raises an exception if they don't have permission to edit it" do Guardian.any_instance.expects(:can_edit?).returns(false) xhr :put, :update, id: @category.slug, name: 'hello', color: 'ff0', text_color: 'fff' - response.should be_forbidden + expect(response).to be_forbidden end it "requires a name" do - lambda { xhr :put, :update, id: @category.slug, color: 'fff', text_color: '0ff' }.should raise_error(ActionController::ParameterMissing) + expect { xhr :put, :update, id: @category.slug, color: 'fff', text_color: '0ff' }.to raise_error(ActionController::ParameterMissing) end it "requires a color" do - lambda { xhr :put, :update, id: @category.slug, name: 'asdf', text_color: '0ff' }.should raise_error(ActionController::ParameterMissing) + expect { xhr :put, :update, id: @category.slug, name: 'asdf', text_color: '0ff' }.to raise_error(ActionController::ParameterMissing) end it "requires a text color" do - lambda { xhr :put, :update, id: @category.slug, name: 'asdf', color: 'fff' }.should raise_error(ActionController::ParameterMissing) + expect { xhr :put, :update, id: @category.slug, name: 'asdf', color: 'fff' }.to raise_error(ActionController::ParameterMissing) end describe "failure" do @@ -166,11 +166,11 @@ describe CategoriesController do end it "returns errors on a duplicate category name" do - response.should_not be_success + expect(response).not_to be_success end it "returns errors on a duplicate category name" do - response.code.to_i.should == 422 + expect(response.code.to_i).to eq(422) end end @@ -180,21 +180,22 @@ describe CategoriesController do readonly = CategoryGroup.permission_types[:readonly] create_post = CategoryGroup.permission_types[:create_post] - xhr :put, :update, id: @category.id, name: "hello", color: "ff0", text_color: "fff", + xhr :put, :update, id: @category.id, name: "hello", color: "ff0", text_color: "fff", slug: "hello-category", auto_close_hours: 72, permissions: { "everyone" => readonly, "staff" => create_post } - response.status.should == 200 + expect(response.status).to eq(200) @category.reload - @category.category_groups.map{|g| [g.group_id, g.permission_type]}.sort.should == [ + expect(@category.category_groups.map{|g| [g.group_id, g.permission_type]}.sort).to eq([ [Group[:everyone].id, readonly],[Group[:staff].id,create_post] - ] - @category.name.should == "hello" - @category.color.should == "ff0" - @category.auto_close_hours.should == 72 + ]) + expect(@category.name).to eq("hello") + expect(@category.slug).to eq("hello-category") + expect(@category.color).to eq("ff0") + expect(@category.auto_close_hours).to eq(72) end end end @@ -202,4 +203,42 @@ describe CategoriesController do end + describe 'update_slug' do + it 'requires the user to be logged in' do + expect { xhr :put, :update_slug, category_id: 'category'}.to raise_error(Discourse::NotLoggedIn) + end + + describe 'logged in' do + let(:valid_attrs) { {id: @category.id, slug: 'fff'} } + + before do + @user = log_in(:admin) + @category = Fabricate(:happy_category, user: @user) + end + + it 'rejects blank' do + xhr :put, :update_slug, category_id: @category.id, slug: nil + expect(response.status).to eq(422) + end + + it 'accepts valid custom slug' do + xhr :put, :update_slug, category_id: @category.id, slug: 'valid-slug' + expect(response).to be_success + category = Category.find(@category.id) + expect(category.slug).to eq('valid-slug') + end + + it 'accepts not well formed custom slug' do + xhr :put, :update_slug, category_id: @category.id, slug: ' valid slug' + expect(response).to be_success + category = Category.find(@category.id) + expect(category.slug).to eq('valid-slug') + end + + it 'rejects invalid custom slug' do + xhr :put, :update_slug, category_id: @category.id, slug: ' ' + expect(response.status).to eq(422) + end + end + end end diff --git a/spec/controllers/clicks_controller_spec.rb b/spec/controllers/clicks_controller_spec.rb index 6d88633d74..c86b1af45d 100644 --- a/spec/controllers/clicks_controller_spec.rb +++ b/spec/controllers/clicks_controller_spec.rb @@ -6,12 +6,12 @@ describe ClicksController do context 'missing params' do it 'raises an error without the url param' do - lambda { xhr :get, :track, post_id: 123 }.should raise_error(ActionController::ParameterMissing) + expect { xhr :get, :track, post_id: 123 }.to raise_error(ActionController::ParameterMissing) end it "redirects to the url even without the topic_id or post_id params" do xhr :get, :track, url: 'http://google.com' - response.should_not be_redirect + expect(response).not_to be_redirect end end @@ -26,7 +26,7 @@ describe ClicksController do it "doesn't redirect" do TopicLinkClick.expects(:create_from).returns(nil) xhr :get, :track, url: 'http://discourse.org', post_id: 123 - response.should_not be_redirect + expect(response).not_to be_redirect end end @@ -42,25 +42,25 @@ describe ClicksController do it 'calls create_from' do TopicLinkClick.expects(:create_from).with('url' => url, 'post_id' => '123', 'ip' => '192.168.0.1').returns(url) xhr :get, :track, url: url, post_id: 123 - response.should redirect_to(url) + expect(response).to redirect_to(url) end it 'redirects to the url' do TopicLinkClick.stubs(:create_from).returns(url) xhr :get, :track, url: url, post_id: 123 - response.should redirect_to(url) + expect(response).to redirect_to(url) end it 'will pass the user_id to create_from' do TopicLinkClick.expects(:create_from).with('url' => url, 'post_id' => '123', 'ip' => '192.168.0.1').returns(url) xhr :get, :track, url: url, post_id: 123 - response.should redirect_to(url) + expect(response).to redirect_to(url) end it "doesn't redirect with the redirect=false param" do TopicLinkClick.expects(:create_from).with('url' => url, 'post_id' => '123', 'ip' => '192.168.0.1', 'redirect' => 'false').returns(url) xhr :get, :track, url: url, post_id: 123, redirect: 'false' - response.should_not be_redirect + expect(response).not_to be_redirect end end @@ -69,7 +69,7 @@ describe ClicksController do it 'calls create_from' do TopicLinkClick.expects(:create_from).with('url' => url, 'topic_id' => '789', 'ip' => '192.168.0.1').returns(url) xhr :get, :track, url: url, topic_id: 789 - response.should redirect_to(url) + expect(response).to redirect_to(url) end end diff --git a/spec/controllers/composer_messages_controller_spec.rb b/spec/controllers/composer_messages_controller_spec.rb index 45aafd94eb..4c7b77fe2d 100644 --- a/spec/controllers/composer_messages_controller_spec.rb +++ b/spec/controllers/composer_messages_controller_spec.rb @@ -5,7 +5,7 @@ describe ComposerMessagesController do context '.index' do it 'requires you to be logged in' do - lambda { xhr :get, :index }.should raise_error(Discourse::NotLoggedIn) + expect { xhr :get, :index }.to raise_error(Discourse::NotLoggedIn) end context 'when logged in' do @@ -14,7 +14,7 @@ describe ComposerMessagesController do it 'redirects to your user preferences' do xhr :get, :index - response.should be_success + expect(response).to be_success end it 'delegates args to the finder' do diff --git a/spec/controllers/draft_controller_spec.rb b/spec/controllers/draft_controller_spec.rb index de5b2ea308..86bc5a9b67 100644 --- a/spec/controllers/draft_controller_spec.rb +++ b/spec/controllers/draft_controller_spec.rb @@ -3,20 +3,20 @@ require 'spec_helper' describe DraftController do it 'requires you to be logged in' do - lambda { post :update }.should raise_error(Discourse::NotLoggedIn) + expect { post :update }.to raise_error(Discourse::NotLoggedIn) end it 'saves a draft on update' do user = log_in post :update, draft_key: 'xyz', data: 'my data', sequence: 0 - Draft.get(user, 'xyz', 0).should == 'my data' + expect(Draft.get(user, 'xyz', 0)).to eq('my data') end it 'destroys drafts when required' do user = log_in Draft.set(user, 'xxx', 0, 'hi') delete :destroy, draft_key: 'xxx', sequence: 0 - Draft.get(user, 'xxx', 0).should == nil + expect(Draft.get(user, 'xxx', 0)).to eq(nil) end end diff --git a/spec/controllers/email_controller_spec.rb b/spec/controllers/email_controller_spec.rb index e67ce633f1..c1635c5313 100644 --- a/spec/controllers/email_controller_spec.rb +++ b/spec/controllers/email_controller_spec.rb @@ -5,7 +5,7 @@ describe EmailController do context '.preferences_redirect' do it 'requires you to be logged in' do - lambda { get :preferences_redirect }.should raise_error(Discourse::NotLoggedIn) + expect { get :preferences_redirect }.to raise_error(Discourse::NotLoggedIn) end context 'when logged in' do @@ -13,7 +13,7 @@ describe EmailController do it 'redirects to your user preferences' do get :preferences_redirect - response.should redirect_to("/users/#{user.username}/preferences") + expect(response).to redirect_to("/users/#{user.username}/preferences") end end @@ -22,15 +22,16 @@ describe EmailController do context '.resubscribe' do let(:user) { Fabricate(:user, email_digests: false) } + let(:key) { DigestUnsubscribeKey.create_key_for(user) } context 'with a valid key' do before do - get :resubscribe, key: user.temporary_key + get :resubscribe, key: key user.reload end it 'subscribes the user' do - user.email_digests.should == true + expect(user.email_digests).to eq(true) end end @@ -39,19 +40,20 @@ describe EmailController do context '.unsubscribe' do let(:user) { Fabricate(:user) } + let(:key) { DigestUnsubscribeKey.create_key_for(user) } context 'with a valid key' do before do - get :unsubscribe, key: user.temporary_key + get :unsubscribe, key: key user.reload end it 'unsubscribes the user' do - user.email_digests.should == false + expect(user.email_digests).to eq(false) end it "sets the appropriate instance variables" do - assigns(:success).should be_present + expect(assigns(:success)).to be_present end end @@ -61,7 +63,7 @@ describe EmailController do end it "sets the appropriate instance variables" do - assigns(:success).should be_blank + expect(assigns(:success)).to be_blank end end @@ -69,17 +71,17 @@ describe EmailController do let!(:logged_in_user) { log_in(:coding_horror) } before do - get :unsubscribe, key: user.temporary_key + get :unsubscribe, key: key user.reload end it 'does not unsubscribe the user' do - user.email_digests.should == true + expect(user.email_digests).to eq(true) end it 'sets the appropriate instance variables' do - assigns(:success).should be_blank - assigns(:different_user).should be_present + expect(assigns(:success)).to be_blank + expect(assigns(:different_user)).to be_present end end @@ -87,22 +89,22 @@ describe EmailController do before do log_in_user(user) - get :unsubscribe, key: user.temporary_key + get :unsubscribe, key: DigestUnsubscribeKey.create_key_for(user) user.reload end it 'unsubscribes the user' do - user.email_digests.should == false + expect(user.email_digests).to eq(false) end it 'sets the appropriate instance variables' do - assigns(:success).should be_present + expect(assigns(:success)).to be_present end end it "sets not_found when the key didn't match anything" do get :unsubscribe, key: 'asdfasdf' - assigns(:not_found).should == true + expect(assigns(:not_found)).to eq(true) end end diff --git a/spec/controllers/embed_controller_spec.rb b/spec/controllers/embed_controller_spec.rb index 02448a1b52..e68965d9bd 100644 --- a/spec/controllers/embed_controller_spec.rb +++ b/spec/controllers/embed_controller_spec.rb @@ -7,13 +7,13 @@ describe EmbedController do it "is 404 without an embed_url" do get :comments - response.should_not be_success + expect(response).not_to be_success end it "raises an error with a missing host" do SiteSetting.stubs(:embeddable_host).returns(nil) get :comments, embed_url: embed_url - response.should_not be_success + expect(response).not_to be_success end context "with a host" do @@ -23,7 +23,7 @@ describe EmbedController do it "raises an error with no referer" do get :comments, embed_url: embed_url - response.should_not be_success + expect(response).not_to be_success end context "success" do @@ -33,8 +33,8 @@ describe EmbedController do end after do - response.should be_success - response.headers['X-Frame-Options'].should == "ALLOWALL" + expect(response).to be_success + expect(response.headers['X-Frame-Options']).to eq("ALLOWALL") end it "tells the topic retriever to work when no previous embed is found" do diff --git a/spec/controllers/export_csv_controller_spec.rb b/spec/controllers/export_csv_controller_spec.rb new file mode 100644 index 0000000000..9975b2cf29 --- /dev/null +++ b/spec/controllers/export_csv_controller_spec.rb @@ -0,0 +1,94 @@ +require "spec_helper" + +describe ExportCsvController do + let(:export_filename) { "user-archive-codinghorror-150115-234817-999.csv.gz" } + + + context "while logged in as normal user" do + before { @user = log_in(:user) } + + describe ".export_entity" do + it "enqueues export job" do + Jobs.expects(:enqueue).with(:export_csv_file, has_entries(entity: "user_archive", user_id: @user.id)) + xhr :post, :export_entity, entity: "user_archive", entity_type: "user" + expect(response).to be_success + end + + it "should not enqueue export job if rate limit is reached" do + Jobs::ExportCsvFile.any_instance.expects(:execute).never + UserExport.create(file_name: "user-archive-codinghorror-150116-003249", user_id: @user.id) + xhr :post, :export_entity, entity: "user_archive", entity_type: "user" + expect(response).not_to be_success + end + + it "returns 404 when normal user tries to export admin entity" do + xhr :post, :export_entity, entity: "staff_action", entity_type: "admin" + expect(response).not_to be_success + end + end + + describe ".download" do + it "uses send_file to transmit the export file" do + file = UserExport.create(file_name: "user-archive-codinghorror-150116-003249", user_id: @user.id) + file_name = "user-archive-codinghorror-150116-003249-#{file.id}.csv.gz" + controller.stubs(:render) + export = UserExport.new() + UserExport.expects(:get_download_path).with(file_name).returns(export) + subject.expects(:send_file).with(export) + get :show, id: file_name + expect(response).to be_success + end + + it "returns 404 when the user tries to export another user's csv file" do + get :show, id: export_filename + expect(response).to be_not_found + end + + it "returns 404 when the export file does not exist" do + UserExport.expects(:get_download_path).returns(nil) + get :show, id: export_filename + expect(response).to be_not_found + end + end + end + + + context "while logged in as an admin" do + before { @admin = log_in(:admin) } + + describe ".export_entity" do + it "enqueues export job" do + Jobs.expects(:enqueue).with(:export_csv_file, has_entries(entity: "staff_action", user_id: @admin.id)) + xhr :post, :export_entity, entity: "staff_action", entity_type: "admin" + expect(response).to be_success + end + + it "should not rate limit export for staff" do + Jobs.expects(:enqueue).with(:export_csv_file, has_entries(entity: "staff_action", user_id: @admin.id)) + UserExport.create(file_name: "screened-email-150116-010145", user_id: @admin.id) + xhr :post, :export_entity, entity: "staff_action", entity_type: "admin" + expect(response).to be_success + end + end + + describe ".download" do + it "uses send_file to transmit the export file" do + file = UserExport.create(file_name: "screened-email-150116-010145", user_id: @admin.id) + file_name = "screened-email-150116-010145-#{file.id}.csv.gz" + controller.stubs(:render) + export = UserExport.new() + UserExport.expects(:get_download_path).with(file_name).returns(export) + subject.expects(:send_file).with(export) + get :show, id: file_name + expect(response).to be_success + end + + it "returns 404 when the export file does not exist" do + UserExport.expects(:get_download_path).returns(nil) + get :show, id: export_filename + expect(response).to be_not_found + end + end + end + +end diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb index bbaa136d20..3973449cbf 100644 --- a/spec/controllers/groups_controller_spec.rb +++ b/spec/controllers/groups_controller_spec.rb @@ -7,21 +7,21 @@ describe GroupsController do it "ensures the group can be seen" do Guardian.any_instance.expects(:can_see?).with(group).returns(false) xhr :get, :show, id: group.name - response.should_not be_success + expect(response).not_to be_success end it "responds with JSON" do Guardian.any_instance.expects(:can_see?).with(group).returns(true) xhr :get, :show, id: group.name - response.should be_success - ::JSON.parse(response.body)['basic_group']['id'].should == group.id + expect(response).to be_success + expect(::JSON.parse(response.body)['basic_group']['id']).to eq(group.id) end it "works even with an upper case group name" do Guardian.any_instance.expects(:can_see?).with(group).returns(true) xhr :get, :show, id: group.name.upcase - response.should be_success - ::JSON.parse(response.body)['basic_group']['id'].should == group.id + expect(response).to be_success + expect(::JSON.parse(response.body)['basic_group']['id']).to eq(group.id) end end @@ -29,14 +29,14 @@ describe GroupsController do it "ensures the group can be seen" do Guardian.any_instance.expects(:can_see?).with(group).returns(false) xhr :get, :counts, group_id: group.name - response.should_not be_success + expect(response).not_to be_success end it "performs the query and responds with JSON" do Guardian.any_instance.expects(:can_see?).with(group).returns(true) Group.any_instance.expects(:posts_for).returns(Group.none) xhr :get, :counts, group_id: group.name - response.should be_success + expect(response).to be_success end end @@ -44,14 +44,14 @@ describe GroupsController do it "ensures the group can be seen" do Guardian.any_instance.expects(:can_see?).with(group).returns(false) xhr :get, :posts, group_id: group.name - response.should_not be_success + expect(response).not_to be_success end it "calls `posts_for` and responds with JSON" do Guardian.any_instance.expects(:can_see?).with(group).returns(true) Group.any_instance.expects(:posts_for).returns(Group.none) xhr :get, :posts, group_id: group.name - response.should be_success + expect(response).to be_success end end @@ -59,13 +59,103 @@ describe GroupsController do it "ensures the group can be seen" do Guardian.any_instance.expects(:can_see?).with(group).returns(false) xhr :get, :members, group_id: group.name - response.should_not be_success + expect(response).not_to be_success end it "calls `posts_for` and responds with JSON" do Guardian.any_instance.expects(:can_see?).with(group).returns(true) xhr :get, :posts, group_id: group.name - response.should be_success + expect(response).to be_success + end + + # Pending until we fix group truncation + skip "ensures that membership can be paginated" do + 5.times { group.add(Fabricate(:user)) } + usernames = group.users.map{ |m| m['username'] }.sort + + xhr :get, :members, group_id: group.name, limit: 3 + expect(response).to be_success + members = JSON.parse(response.body) + expect(members.map{ |m| m['username'] }).to eq(usernames[0..2]) + + xhr :get, :members, group_id: group.name, limit: 3, offset: 3 + expect(response).to be_success + members = JSON.parse(response.body) + expect(members.map{ |m| m['username'] }).to eq(usernames[3..4]) end end + + + describe "membership edit permission" do + it "refuses membership changes to unauthorized users" do + Guardian.any_instance.stubs(:can_edit?).with(group).returns(false) + + xhr :put, :add_members, group_id: group.name, usernames: "bob" + response.should be_forbidden + + xhr :delete, :remove_member, group_id: group.name, username: "bob" + response.should be_forbidden + end + + it "cannot add members to automatic groups" do + Guardian.any_instance.stubs(:is_admin?).returns(true) + auto_group = Fabricate(:group, name: "auto_group", automatic: true) + + xhr :put, :add_members, group_id: group.name, usernames: "bob" + response.should be_forbidden + end + end + + describe "membership edits" do + before do + @user1 = Fabricate(:user) + group.add(@user1) + group.reload + + Guardian.any_instance.stubs(:can_edit?).with(group).returns(true) + end + + it "can make incremental adds" do + user2 = Fabricate(:user) + xhr :put, :add_members, group_id: group.name, usernames: user2.username + + response.should be_success + group.reload + group.users.count.should eq(2) + end + + it "succeeds silently when adding non-existent users" do + xhr :put, :add_members, group_id: group.name, usernames: "nosuchperson" + + response.should be_success + group.reload + group.users.count.should eq(1) + end + + it "succeeds silently when adding duplicate users" do + xhr :put, :add_members, group_id: group.name, usernames: @user1.username + + response.should be_success + group.reload + group.users.should eq([@user1]) + end + + it "can make incremental deletes" do + xhr :delete, :remove_member, group_id: group.name, username: @user1.username + + response.should be_success + group.reload + group.users.count.should eq(0) + end + + it "succeeds silently when removing non-members" do + user2 = Fabricate(:user) + xhr :delete, :remove_member, group_id: group.name, username: user2.username + + response.should be_success + group.reload + group.users.count.should eq(1) + end + end + end diff --git a/spec/controllers/invites_controller_spec.rb b/spec/controllers/invites_controller_spec.rb index 980dadea28..f5ce32a6ff 100644 --- a/spec/controllers/invites_controller_spec.rb +++ b/spec/controllers/invites_controller_spec.rb @@ -5,9 +5,9 @@ describe InvitesController do context '.destroy' do it 'requires you to be logged in' do - lambda { + expect { delete :destroy, email: 'jake@adventuretime.ooo' - }.should raise_error(Discourse::NotLoggedIn) + }.to raise_error(Discourse::NotLoggedIn) end context 'while logged in' do @@ -17,15 +17,15 @@ describe InvitesController do it 'raises an error when the email is missing' do - lambda { delete :destroy }.should raise_error(ActionController::ParameterMissing) + expect { delete :destroy }.to raise_error(ActionController::ParameterMissing) end it "raises an error when the email cannot be found" do - lambda { delete :destroy, email: 'finn@adventuretime.ooo' }.should raise_error(Discourse::InvalidParameters) + expect { delete :destroy, email: 'finn@adventuretime.ooo' }.to raise_error(Discourse::InvalidParameters) end it 'raises an error when the invite is not yours' do - lambda { delete :destroy, email: another_invite.email }.should raise_error(Discourse::InvalidParameters) + expect { delete :destroy, email: another_invite.email }.to raise_error(Discourse::InvalidParameters) end it "destroys the invite" do @@ -39,9 +39,9 @@ describe InvitesController do context '.create' do it 'requires you to be logged in' do - lambda { + expect { post :create, email: 'jake@adventuretime.ooo' - }.should raise_error(Discourse::NotLoggedIn) + }.to raise_error(Discourse::NotLoggedIn) end context 'while logged in' do @@ -50,7 +50,7 @@ describe InvitesController do it "fails if you can't invite to the forum" do log_in post :create, email: email - response.should_not be_success + expect(response).not_to be_success end it "fails for normal user if invite email already exists" do @@ -58,15 +58,15 @@ describe InvitesController do invite = Invite.invite_by_email("invite@example.com", user) invite.reload post :create, email: invite.email - response.should_not be_success + expect(response).not_to be_success end it "allows admins to invite to groups" do group = Fabricate(:group) log_in(:admin) post :create, email: email, group_names: group.name - response.should be_success - Invite.find_by(email: email).invited_groups.count.should == 1 + expect(response).to be_success + expect(Invite.find_by(email: email).invited_groups.count).to eq(1) end it "allows admin to send multiple invites to same email" do @@ -74,7 +74,7 @@ describe InvitesController do invite = Invite.invite_by_email("invite@example.com", user) invite.reload post :create, email: invite.email - response.should be_success + expect(response).to be_success end end @@ -88,11 +88,11 @@ describe InvitesController do end it "redirects to the root" do - response.should redirect_to("/") + expect(response).to redirect_to("/") end it "should not change the session" do - session[:current_user_id].should be_blank + expect(session[:current_user_id]).to be_blank end end @@ -105,11 +105,11 @@ describe InvitesController do end it "redirects to the root" do - response.should redirect_to("/") + expect(response).to redirect_to("/") end it "should not change the session" do - session[:current_user_id].should be_blank + expect(session[:current_user_id]).to be_blank end end @@ -133,11 +133,11 @@ describe InvitesController do end it 'logs in the user' do - session[:current_user_id].should == user.id + expect(session[:current_user_id]).to eq(user.id) end it 'redirects to the first topic the user was invited to' do - response.should redirect_to(topic.relative_url) + expect(response).to redirect_to(topic.relative_url) end end @@ -179,9 +179,9 @@ describe InvitesController do context '.create_disposable_invite' do it 'requires you to be logged in' do - lambda { + expect { post :create, email: 'jake@adventuretime.ooo' - }.should raise_error(Discourse::NotLoggedIn) + }.to raise_error(Discourse::NotLoggedIn) end context 'while logged in as normal user' do @@ -190,7 +190,7 @@ describe InvitesController do it "does not create disposable invitation" do log_in post :create_disposable_invite, email: user.email - response.should_not be_success + expect(response).not_to be_success end end @@ -202,29 +202,29 @@ describe InvitesController do it "creates disposable invitation" do post :create_disposable_invite, email: user.email - response.should be_success - Invite.where(invited_by_id: user.id).count.should == 1 + expect(response).to be_success + expect(Invite.where(invited_by_id: user.id).count).to eq(1) end it "creates multiple disposable invitations" do post :create_disposable_invite, email: user.email, quantity: 10 - response.should be_success - Invite.where(invited_by_id: user.id).count.should == 10 + expect(response).to be_success + expect(Invite.where(invited_by_id: user.id).count).to eq(10) end it "allows group invite" do group = Fabricate(:group) post :create_disposable_invite, email: user.email, group_names: group.name - response.should be_success - Invite.find_by(invited_by_id: user.id).invited_groups.count.should == 1 + expect(response).to be_success + expect(Invite.find_by(invited_by_id: user.id).invited_groups.count).to eq(1) end it "allows multiple group invite" do group_1 = Fabricate(:group, name: "security") group_2 = Fabricate(:group, name: "support") post :create_disposable_invite, email: user.email, group_names: "security,support" - response.should be_success - Invite.find_by(invited_by_id: user.id).invited_groups.count.should == 2 + expect(response).to be_success + expect(Invite.find_by(invited_by_id: user.id).invited_groups.count).to eq(2) end end @@ -239,11 +239,11 @@ describe InvitesController do end it "redirects to the root" do - response.should redirect_to("/") + expect(response).to redirect_to("/") end it "should not change the session" do - session[:current_user_id].should be_blank + expect(session[:current_user_id]).to be_blank end end @@ -271,7 +271,7 @@ describe InvitesController do end it 'logs in user' do - session[:current_user_id].should == user.id + expect(session[:current_user_id]).to eq(user.id) end end @@ -283,9 +283,9 @@ describe InvitesController do context '.resend_invite' do it 'requires you to be logged in' do - lambda { + expect { delete :resend_invite, email: 'first_name@example.com' - }.should raise_error(Discourse::NotLoggedIn) + }.to raise_error(Discourse::NotLoggedIn) end context 'while logged in' do @@ -294,15 +294,15 @@ describe InvitesController do let(:another_invite) { Fabricate(:invite, email: 'last_name@example.com') } it 'raises an error when the email is missing' do - lambda { post :resend_invite }.should raise_error(ActionController::ParameterMissing) + expect { post :resend_invite }.to raise_error(ActionController::ParameterMissing) end it "raises an error when the email cannot be found" do - lambda { post :resend_invite, email: 'first_name@example.com' }.should raise_error(Discourse::InvalidParameters) + expect { post :resend_invite, email: 'first_name@example.com' }.to raise_error(Discourse::InvalidParameters) end it 'raises an error when the invite is not yours' do - lambda { post :resend_invite, email: another_invite.email }.should raise_error(Discourse::InvalidParameters) + expect { post :resend_invite, email: another_invite.email }.to raise_error(Discourse::InvalidParameters) end it "resends the invite" do @@ -316,9 +316,9 @@ describe InvitesController do context '.check_csv_chunk' do it 'requires you to be logged in' do - lambda { + expect { post :check_csv_chunk - }.should raise_error(Discourse::NotLoggedIn) + }.to raise_error(Discourse::NotLoggedIn) end context 'while logged in' do @@ -330,7 +330,7 @@ describe InvitesController do it "fails if you can't bulk invite to the forum" do log_in post :check_csv_chunk, resumableChunkNumber: resumableChunkNumber, resumableCurrentChunkSize: resumableCurrentChunkSize.to_i, resumableIdentifier: resumableIdentifier, resumableFilename: resumableFilename - response.should_not be_success + expect(response).not_to be_success end end @@ -339,9 +339,9 @@ describe InvitesController do context '.upload_csv_chunk' do it 'requires you to be logged in' do - lambda { + expect { post :upload_csv_chunk - }.should raise_error(Discourse::NotLoggedIn) + }.to raise_error(Discourse::NotLoggedIn) end context 'while logged in' do @@ -361,13 +361,13 @@ describe InvitesController do it "fails if you can't bulk invite to the forum" do log_in post :upload_csv_chunk, file: file, resumableChunkNumber: resumableChunkNumber.to_i, resumableChunkSize: resumableChunkSize.to_i, resumableCurrentChunkSize: resumableCurrentChunkSize.to_i, resumableTotalSize: resumableTotalSize.to_i, resumableType: resumableType, resumableIdentifier: resumableIdentifier, resumableFilename: resumableFilename - response.should_not be_success + expect(response).not_to be_success end it "allows admins to bulk invite" do log_in(:admin) post :upload_csv_chunk, file: file, resumableChunkNumber: resumableChunkNumber.to_i, resumableChunkSize: resumableChunkSize.to_i, resumableCurrentChunkSize: resumableCurrentChunkSize.to_i, resumableTotalSize: resumableTotalSize.to_i, resumableType: resumableType, resumableIdentifier: resumableIdentifier, resumableFilename: resumableFilename - response.should be_success + expect(response).to be_success end end diff --git a/spec/controllers/list_controller_spec.rb b/spec/controllers/list_controller_spec.rb index 8a707d2c61..05ec93c3a8 100644 --- a/spec/controllers/list_controller_spec.rb +++ b/spec/controllers/list_controller_spec.rb @@ -8,7 +8,7 @@ describe ListController do @post = Fabricate(:post, user: @user) # forces tests down some code paths - SiteSetting.stubs(:top_menu).returns('latest,-video|new|unread|starred|categories|category/beer') + SiteSetting.stubs(:top_menu).returns('latest,-video|new|unread|categories|category/beer') end describe 'indexes' do @@ -16,7 +16,7 @@ describe ListController do (Discourse.anonymous_filters - [:categories]).each do |filter| context "#{filter}" do before { xhr :get, filter } - it { should respond_with(:success) } + it { is_expected.to respond_with(:success) } end end @@ -30,9 +30,9 @@ describe ListController do p = create_post xhr :get, :latest, format: :json, topic_ids: "#{p.topic_id}" - response.should be_success + expect(response).to be_success parsed = JSON.parse(response.body) - parsed["topic_list"]["topics"].length.should == 1 + expect(parsed["topic_list"]["topics"].length).to eq(1) end end @@ -43,8 +43,8 @@ describe ListController do it 'renders RSS' do get "#{filter}_feed", format: :rss - response.should be_success - response.content_type.should == 'application/rss+xml' + expect(response).to be_success + expect(response.content_type).to eq('application/rss+xml') end end @@ -62,7 +62,7 @@ describe ListController do xhr :get, :category_latest, category: category.slug end - it { should_not respond_with(:success) } + it { is_expected.not_to respond_with(:success) } end context 'with access to see the category' do @@ -70,7 +70,7 @@ describe ListController do xhr :get, :category_latest, category: category.slug end - it { should respond_with(:success) } + it { is_expected.to respond_with(:success) } end context 'with a link that includes an id' do @@ -78,7 +78,7 @@ describe ListController do xhr :get, :category_latest, category: "#{category.id}-#{category.slug}" end - it { should respond_with(:success) } + it { is_expected.to respond_with(:success) } end context 'another category exists with a number at the beginning of its name' do @@ -89,10 +89,10 @@ describe ListController do xhr :get, :category_latest, category: other_category.slug end - it { should respond_with(:success) } + it { is_expected.to respond_with(:success) } it 'uses the correct category' do - assigns(:category).should == other_category + expect(assigns(:category)).to eq(other_category) end end @@ -104,7 +104,7 @@ describe ListController do xhr :get, :category_latest, parent_category: category.slug, category: sub_category.slug end - it { should respond_with(:success) } + it { is_expected.to respond_with(:success) } end context 'when child is requested with the wrong parent' do @@ -112,7 +112,7 @@ describe ListController do xhr :get, :category_latest, parent_category: 'not_the_right_slug', category: sub_category.slug end - it { should_not respond_with(:success) } + it { is_expected.not_to respond_with(:success) } end end @@ -120,8 +120,8 @@ describe ListController do describe 'feed' do it 'renders RSS' do get :category_feed, category: category.slug, format: :rss - response.should be_success - response.content_type.should == 'application/rss+xml' + expect(response).to be_success + expect(response.content_type).to eq('application/rss+xml') end end end @@ -132,7 +132,7 @@ describe ListController do it "should respond with a list" do xhr :get, :topics_by, username: @user.username - response.should be_success + expect(response).to be_success end end @@ -142,13 +142,13 @@ describe ListController do it "raises an error when can_see_private_messages? is false " do Guardian.any_instance.expects(:can_see_private_messages?).returns(false) xhr :get, :private_messages, username: @user.username - response.should be_forbidden + expect(response).to be_forbidden end it "succeeds when can_see_private_messages? is false " do Guardian.any_instance.expects(:can_see_private_messages?).returns(true) xhr :get, :private_messages, username: @user.username - response.should be_success + expect(response).to be_success end end @@ -158,13 +158,13 @@ describe ListController do it "raises an error when can_see_private_messages? is false " do Guardian.any_instance.expects(:can_see_private_messages?).returns(false) xhr :get, :private_messages_sent, username: @user.username - response.should be_forbidden + expect(response).to be_forbidden end it "succeeds when can_see_private_messages? is false " do Guardian.any_instance.expects(:can_see_private_messages?).returns(true) xhr :get, :private_messages_sent, username: @user.username - response.should be_success + expect(response).to be_success end end @@ -174,35 +174,19 @@ describe ListController do it "raises an error when can_see_private_messages? is false " do Guardian.any_instance.expects(:can_see_private_messages?).returns(false) xhr :get, :private_messages_unread, username: @user.username - response.should be_forbidden + expect(response).to be_forbidden end it "succeeds when can_see_private_messages? is false " do Guardian.any_instance.expects(:can_see_private_messages?).returns(true) xhr :get, :private_messages_unread, username: @user.username - response.should be_success + expect(response).to be_success end end - context 'starred' do - it 'raises an error when not logged in' do - lambda { xhr :get, :starred }.should raise_error(Discourse::NotLoggedIn) - end - - context 'when logged in' do - before do - log_in_user(@user) - xhr :get, :starred - end - - it { should respond_with(:success) } - end - end - - context 'read' do it 'raises an error when not logged in' do - lambda { xhr :get, :read }.should raise_error(Discourse::NotLoggedIn) + expect { xhr :get, :read }.to raise_error(Discourse::NotLoggedIn) end context 'when logged in' do @@ -211,32 +195,32 @@ describe ListController do xhr :get, :read end - it { should respond_with(:success) } + it { is_expected.to respond_with(:success) } end end describe "best_periods_for" do it "returns yearly for more than 180 days" do - ListController.best_periods_for(nil).should == [:yearly] - ListController.best_periods_for(180.days.ago).should == [:yearly] + expect(ListController.best_periods_for(nil)).to eq([:yearly]) + expect(ListController.best_periods_for(180.days.ago)).to eq([:yearly]) end it "includes monthly when less than 180 days and more than 35 days" do (35...180).each do |date| - ListController.best_periods_for(date.days.ago).should == [:monthly, :yearly] + expect(ListController.best_periods_for(date.days.ago)).to eq([:monthly, :yearly]) end end it "includes weekly when less than 35 days and more than 8 days" do (8...35).each do |date| - ListController.best_periods_for(date.days.ago).should == [:weekly, :monthly, :yearly] + expect(ListController.best_periods_for(date.days.ago)).to eq([:weekly, :monthly, :yearly]) end end it "includes daily when less than 8 days" do (0...8).each do |date| - ListController.best_periods_for(date.days.ago).should == [:daily, :weekly, :monthly, :yearly] + expect(ListController.best_periods_for(date.days.ago)).to eq([:daily, :weekly, :monthly, :yearly]) end end diff --git a/spec/controllers/notifications_controller_spec.rb b/spec/controllers/notifications_controller_spec.rb index d326c8c8e9..cb9771447d 100644 --- a/spec/controllers/notifications_controller_spec.rb +++ b/spec/controllers/notifications_controller_spec.rb @@ -7,51 +7,51 @@ describe NotificationsController do it 'should succeed for recent' do xhr :get, :recent - response.should be_success + expect(response).to be_success end it 'should succeed for history' do xhr :get, :history - response.should be_success + expect(response).to be_success end it 'should succeed for history' do xhr :get, :reset_new - response.should be_success + expect(response).to be_success end it 'should mark notifications as viewed' do notification = Fabricate(:notification, user: user) - user.reload.unread_notifications.should == 1 - user.reload.total_unread_notifications.should == 1 + expect(user.reload.unread_notifications).to eq(1) + expect(user.reload.total_unread_notifications).to eq(1) xhr :get, :recent - user.reload.unread_notifications.should == 0 - user.reload.total_unread_notifications.should == 1 + expect(user.reload.unread_notifications).to eq(0) + expect(user.reload.total_unread_notifications).to eq(1) end it 'should not mark notifications as viewed if silent param is present' do notification = Fabricate(:notification, user: user) - user.reload.unread_notifications.should == 1 - user.reload.total_unread_notifications.should == 1 + expect(user.reload.unread_notifications).to eq(1) + expect(user.reload.total_unread_notifications).to eq(1) xhr :get, :recent, silent: true - user.reload.unread_notifications.should == 1 - user.reload.total_unread_notifications.should == 1 + expect(user.reload.unread_notifications).to eq(1) + expect(user.reload.total_unread_notifications).to eq(1) end it "updates the `read` status" do notification = Fabricate(:notification, user: user) - user.reload.unread_notifications.should == 1 - user.reload.total_unread_notifications.should == 1 + expect(user.reload.unread_notifications).to eq(1) + expect(user.reload.total_unread_notifications).to eq(1) xhr :put, :reset_new user.reload - user.reload.unread_notifications.should == 0 - user.reload.total_unread_notifications.should == 0 + expect(user.reload.unread_notifications).to eq(0) + expect(user.reload.total_unread_notifications).to eq(0) end end context 'when not logged in' do it 'should raise an error' do - lambda { xhr :get, :recent }.should raise_error(Discourse::NotLoggedIn) + expect { xhr :get, :recent }.to raise_error(Discourse::NotLoggedIn) end end diff --git a/spec/controllers/onebox_controller_spec.rb b/spec/controllers/onebox_controller_spec.rb index 94fda6fd44..12eaa3103a 100644 --- a/spec/controllers/onebox_controller_spec.rb +++ b/spec/controllers/onebox_controller_spec.rb @@ -19,11 +19,11 @@ describe OneboxController do end it 'returns success' do - response.should be_success + expect(response).to be_success end it 'returns the onebox response in the body' do - response.body.should == body + expect(response.body).to eq(body) end end @@ -33,13 +33,13 @@ describe OneboxController do it "returns 404 if the onebox is nil" do Oneboxer.expects(:preview).with(url, invalidate_oneboxes: false).returns(nil) xhr :get, :show, url: url - response.response_code.should == 404 + expect(response.response_code).to eq(404) end it "returns 404 if the onebox is an empty string" do Oneboxer.expects(:preview).with(url, invalidate_oneboxes: false).returns(" \t ") xhr :get, :show, url: url - response.response_code.should == 404 + expect(response.response_code).to eq(404) end end diff --git a/spec/controllers/permalinks_controller_spec.rb b/spec/controllers/permalinks_controller_spec.rb index afc50e364b..2cf79a93c4 100644 --- a/spec/controllers/permalinks_controller_spec.rb +++ b/spec/controllers/permalinks_controller_spec.rb @@ -6,13 +6,13 @@ describe PermalinksController do permalink = Fabricate(:permalink) Permalink.any_instance.stubs(:target_url).returns('/t/the-topic-slug/42') get :show, url: permalink.url - response.should redirect_to('/t/the-topic-slug/42') - response.status.should == 301 + expect(response).to redirect_to('/t/the-topic-slug/42') + expect(response.status).to eq(301) end it 'return 404 if permalink record does not exist' do get :show, url: '/not/a/valid/url' - response.status.should == 404 + expect(response.status).to eq(404) end end diff --git a/spec/controllers/post_actions_controller_spec.rb b/spec/controllers/post_actions_controller_spec.rb index f87a21958c..09e00b3b5f 100644 --- a/spec/controllers/post_actions_controller_spec.rb +++ b/spec/controllers/post_actions_controller_spec.rb @@ -4,7 +4,7 @@ describe PostActionsController do describe 'create' do it 'requires you to be logged in' do - lambda { xhr :post, :create }.should raise_error(Discourse::NotLoggedIn) + expect { xhr :post, :create }.to raise_error(Discourse::NotLoggedIn) end describe 'logged in' do @@ -14,23 +14,23 @@ describe PostActionsController do end it 'raises an error when the id is missing' do - lambda { xhr :post, :create, post_action_type_id: PostActionType.types[:like] }.should raise_error(ActionController::ParameterMissing) + expect { xhr :post, :create, post_action_type_id: PostActionType.types[:like] }.to raise_error(ActionController::ParameterMissing) end it 'raises an error when the post_action_type_id index is missing' do - lambda { xhr :post, :create, id: @post.id }.should raise_error(ActionController::ParameterMissing) + expect { xhr :post, :create, id: @post.id }.to raise_error(ActionController::ParameterMissing) end it "fails when the user doesn't have permission to see the post" do Guardian.any_instance.expects(:can_see?).with(@post).returns(false) xhr :post, :create, id: @post.id, post_action_type_id: PostActionType.types[:like] - response.should be_forbidden + expect(response).to be_forbidden end it "fails when the user doesn't have permission to perform that action" do Guardian.any_instance.expects(:post_can_act?).with(@post, :like, taken_actions: nil).returns(false) xhr :post, :create, id: @post.id, post_action_type_id: PostActionType.types[:like] - response.should be_forbidden + expect(response).to be_forbidden end it 'allows us to create an post action on a post' do @@ -69,19 +69,19 @@ describe PostActionsController do let(:post) { Fabricate(:post, user: Fabricate(:coding_horror)) } it 'requires you to be logged in' do - lambda { xhr :delete, :destroy, id: post.id }.should raise_error(Discourse::NotLoggedIn) + expect { xhr :delete, :destroy, id: post.id }.to raise_error(Discourse::NotLoggedIn) end context 'logged in' do let!(:user) { log_in } it 'raises an error when the post_action_type_id is missing' do - lambda { xhr :delete, :destroy, id: post.id }.should raise_error(ActionController::ParameterMissing) + expect { xhr :delete, :destroy, id: post.id }.to raise_error(ActionController::ParameterMissing) end it "returns 404 when the post action type doesn't exist for that user" do xhr :delete, :destroy, id: post.id, post_action_type_id: 1 - response.code.should == '404' + expect(response.code).to eq('404') end context 'with a post_action record ' do @@ -89,18 +89,18 @@ describe PostActionsController do it 'returns success' do xhr :delete, :destroy, id: post.id, post_action_type_id: 1 - response.should be_success + expect(response).to be_success end it 'deletes the action' do xhr :delete, :destroy, id: post.id, post_action_type_id: 1 - PostAction.exists?(user_id: user.id, post_id: post.id, post_action_type_id: 1, deleted_at: nil).should == false + expect(PostAction.exists?(user_id: user.id, post_id: post.id, post_action_type_id: 1, deleted_at: nil)).to eq(false) end it 'ensures it can be deleted' do Guardian.any_instance.expects(:can_delete?).with(post_action).returns(false) xhr :delete, :destroy, id: post.id, post_action_type_id: 1 - response.should be_forbidden + expect(response).to be_forbidden end end @@ -114,7 +114,7 @@ describe PostActionsController do context "not logged in" do it "should not allow them to clear flags" do - lambda { xhr :post, :defer_flags }.should raise_error(Discourse::NotLoggedIn) + expect { xhr :post, :defer_flags }.to raise_error(Discourse::NotLoggedIn) end end @@ -122,13 +122,13 @@ describe PostActionsController do let!(:user) { log_in(:moderator) } it "raises an error without a post_action_type_id" do - -> { xhr :post, :defer_flags, id: flagged_post.id }.should raise_error(ActionController::ParameterMissing) + expect { xhr :post, :defer_flags, id: flagged_post.id }.to raise_error(ActionController::ParameterMissing) end it "raises an error when the user doesn't have access" do Guardian.any_instance.expects(:can_defer_flags?).returns(false) xhr :post, :defer_flags, id: flagged_post.id, post_action_type_id: PostActionType.types[:spam] - response.should be_forbidden + expect(response).to be_forbidden end context "success" do @@ -139,13 +139,13 @@ describe PostActionsController do it "delegates to defer_flags" do xhr :post, :defer_flags, id: flagged_post.id, post_action_type_id: PostActionType.types[:spam] - response.should be_success + expect(response).to be_success end it "works with a deleted post" do flagged_post.trash!(user) xhr :post, :defer_flags, id: flagged_post.id, post_action_type_id: PostActionType.types[:spam] - response.should be_success + expect(response).to be_success end end @@ -159,32 +159,32 @@ describe PostActionsController do let!(:post) { Fabricate(:post, user: log_in) } it 'raises an error without an id' do - lambda { + expect { xhr :get, :users, post_action_type_id: PostActionType.types[:like] - }.should raise_error(ActionController::ParameterMissing) + }.to raise_error(ActionController::ParameterMissing) end it 'raises an error without a post action type' do - lambda { + expect { xhr :get, :users, id: post.id - }.should raise_error(ActionController::ParameterMissing) + }.to raise_error(ActionController::ParameterMissing) end it "fails when the user doesn't have permission to see the post" do Guardian.any_instance.expects(:can_see?).with(post).returns(false) xhr :get, :users, id: post.id, post_action_type_id: PostActionType.types[:like] - response.should be_forbidden + expect(response).to be_forbidden end it 'raises an error when the post action type cannot be seen' do Guardian.any_instance.expects(:can_see_post_actors?).with(instance_of(Topic), PostActionType.types[:like]).returns(false) xhr :get, :users, id: post.id, post_action_type_id: PostActionType.types[:like] - response.should be_forbidden + expect(response).to be_forbidden end it 'succeeds' do xhr :get, :users, id: post.id, post_action_type_id: PostActionType.types[:like] - response.should be_success + expect(response).to be_success end end diff --git a/spec/controllers/posts_controller_spec.rb b/spec/controllers/posts_controller_spec.rb index 92a2ba3d80..14210d0a64 100644 --- a/spec/controllers/posts_controller_spec.rb +++ b/spec/controllers/posts_controller_spec.rb @@ -7,12 +7,12 @@ shared_examples 'finding and showing post' do it 'ensures the user can see the post' do Guardian.any_instance.expects(:can_see?).with(post).returns(false) xhr :get, action, params - response.should be_forbidden + expect(response).to be_forbidden end it 'succeeds' do xhr :get, action, params - response.should be_success + expect(response).to be_success end context "deleted post" do @@ -22,32 +22,32 @@ shared_examples 'finding and showing post' do it "can't find deleted posts as an anonymous user" do xhr :get, action, params - response.status.should == 404 + expect(response.status).to eq(404) end it "can't find deleted posts as a regular user" do log_in(:user) xhr :get, action, params - response.status.should == 404 + expect(response.status).to eq(404) end it "can find posts as a moderator" do log_in(:moderator) xhr :get, action, params - response.should be_success + expect(response).to be_success end it "can find posts as a admin" do log_in(:admin) xhr :get, action, params - response.should be_success + expect(response).to be_success end end end shared_examples 'action requires login' do |method, action, params| it 'raises an exception when not logged in' do - lambda { xhr method, action, params }.should raise_error(Discourse::NotLoggedIn) + expect { xhr method, action, params }.to raise_error(Discourse::NotLoggedIn) end end @@ -61,10 +61,10 @@ describe PostsController do it 'returns the cooked conent' do xhr :get, :cooked, id: 1234 - response.should be_success + expect(response).to be_success json = ::JSON.parse(response.body) - json.should be_present - json['cooked'].should == 'wat' + expect(json).to be_present + expect(json['cooked']).to eq('wat') end end @@ -80,7 +80,7 @@ describe PostsController do xhr :get, :raw_email, id: post.id - response.should be_forbidden + expect(response).to be_forbidden end it "can view raw email" do @@ -88,9 +88,9 @@ describe PostsController do xhr :get, :raw_email, id: post.id - response.should be_success + expect(response).to be_success json = ::JSON.parse(response.body) - json['raw_email'].should == 'email_content' + expect(json['raw_email']).to eq('email_content') end end @@ -108,10 +108,10 @@ describe PostsController do new_post = create_post xhr :get, :show, {id: new_post.id} parsed = JSON.parse(response.body) - parsed["topic_slug"].should == new_post.topic.slug - parsed["moderator"].should == false - parsed["username"].should == new_post.user.username - parsed["cooked"].should == new_post.cooked + expect(parsed["topic_slug"]).to eq(new_post.topic.slug) + expect(parsed["moderator"]).to eq(false) + expect(parsed["username"]).to eq(new_post.user.username) + expect(parsed["cooked"]).to eq(new_post.cooked) end end @@ -160,14 +160,14 @@ describe PostsController do xhr :delete, :destroy, id: post.id - response.status.should == 422 - JSON.parse(response.body)['errors'].should include(I18n.t('too_late_to_edit')) + expect(response.status).to eq(422) + expect(JSON.parse(response.body)['errors']).to include(I18n.t('too_late_to_edit')) end it "raises an error when the user doesn't have permission to see the post" do Guardian.any_instance.expects(:can_delete?).with(post).returns(false) xhr :delete, :destroy, id: post.id - response.should be_forbidden + expect(response).to be_forbidden end it "uses a PostDestroyer" do @@ -191,7 +191,7 @@ describe PostsController do it "raises an error when the user doesn't have permission to see the post" do Guardian.any_instance.expects(:can_recover_post?).with(post).returns(false) xhr :put, :recover, post_id: post.id - response.should be_forbidden + expect(response).to be_forbidden end it "recovers a post correctly" do @@ -201,7 +201,7 @@ describe PostsController do PostDestroyer.new(user, post).destroy xhr :put, :recover, post_id: post.id post.reload - post.deleted_at.should == nil + expect(post.deleted_at).to eq(nil) end end @@ -217,17 +217,17 @@ describe PostsController do let!(:post2) { Fabricate(:post, topic_id: post1.topic_id, user: poster, post_number: 3, reply_to_post_number: post1.post_number) } it "raises invalid parameters no post_ids" do - lambda { xhr :delete, :destroy_many }.should raise_error(ActionController::ParameterMissing) + expect { xhr :delete, :destroy_many }.to raise_error(ActionController::ParameterMissing) end it "raises invalid parameters with missing ids" do - lambda { xhr :delete, :destroy_many, post_ids: [12345] }.should raise_error(Discourse::InvalidParameters) + expect { xhr :delete, :destroy_many, post_ids: [12345] }.to raise_error(Discourse::InvalidParameters) end it "raises an error when the user doesn't have permission to delete the posts" do Guardian.any_instance.expects(:can_delete?).with(instance_of(Post)).returns(false) xhr :delete, :destroy_many, post_ids: [post1.id, post2.id] - response.should be_forbidden + expect(response).to be_forbidden end it "deletes the post" do @@ -278,8 +278,8 @@ describe PostsController do xhr :put, :update, update_params - response.status.should == 422 - JSON.parse(response.body)['errors'].should include(I18n.t('too_late_to_edit')) + expect(response.status).to eq(422) + expect(JSON.parse(response.body)['errors']).to include(I18n.t('too_late_to_edit')) end it 'passes the image sizes through' do @@ -294,15 +294,15 @@ describe PostsController do it "raises an error when the post parameter is missing" do update_params.delete(:post) - lambda { + expect { xhr :put, :update, update_params - }.should raise_error(ActionController::ParameterMissing) + }.to raise_error(ActionController::ParameterMissing) end it "raises an error when the user doesn't have permission to see the post" do Guardian.any_instance.expects(:can_edit?).with(post).at_least_once.returns(false) xhr :put, :update, update_params - response.should be_forbidden + expect(response).to be_forbidden end it "calls revise with valid parameters" do @@ -331,7 +331,7 @@ describe PostsController do Guardian.any_instance.expects(:can_see?).with(post).returns(false).once xhr :put, :bookmark, post_id: post.id, bookmarked: 'true' - response.should be_forbidden + expect(response).to be_forbidden end it 'creates a bookmark' do @@ -361,7 +361,7 @@ describe PostsController do xhr :put, :wiki, post_id: post.id, wiki: 'true' - response.should be_forbidden + expect(response).to be_forbidden end it "can wiki a post" do @@ -370,7 +370,7 @@ describe PostsController do xhr :put, :wiki, post_id: post.id, wiki: 'true' post.reload - post.wiki.should == true + expect(post.wiki).to eq(true) end it "can unwiki a post" do @@ -380,7 +380,7 @@ describe PostsController do xhr :put, :wiki, post_id: wikied_post.id, wiki: 'false' wikied_post.reload - wikied_post.wiki.should == false + expect(wikied_post.wiki).to eq(false) end end @@ -400,7 +400,7 @@ describe PostsController do xhr :put, :post_type, post_id: post.id, post_type: 2 - response.should be_forbidden + expect(response).to be_forbidden end it "can change the post type" do @@ -409,7 +409,7 @@ describe PostsController do xhr :put, :post_type, post_id: post.id, post_type: 2 post.reload - post.post_type.should == 2 + expect(post.post_type).to eq(2) end end @@ -429,7 +429,7 @@ describe PostsController do xhr :put, :rebake, post_id: post.id - response.should be_forbidden + expect(response).to be_forbidden end it "can rebake the post" do @@ -437,7 +437,7 @@ describe PostsController do xhr :put, :rebake, post_id: post.id - response.should be_success + expect(response).to be_success end end @@ -457,13 +457,13 @@ describe PostsController do master_key = ApiKey.create_master_key.key xhr :post, :create, {api_username: user.username, api_key: master_key, raw: raw, title: title, wpid: 1} - response.should be_success + expect(response).to be_success original = response.body xhr :post, :create, {api_username: user.username_lower, api_key: master_key, raw: raw, title: title, wpid: 2} - response.should be_success + expect(response).to be_success - response.body.should == original + expect(response.body).to eq(original) end end @@ -474,19 +474,20 @@ describe PostsController do let(:new_post) { Fabricate.build(:post, user: user) } it "raises an exception without a raw parameter" do - lambda { xhr :post, :create }.should raise_error(ActionController::ParameterMissing) + expect { xhr :post, :create }.to raise_error(ActionController::ParameterMissing) end - it 'calls the post creator' do + it 'creates the post' do PostCreator.any_instance.expects(:create).returns(new_post) - xhr :post, :create, {raw: 'test'} - response.should be_success - end - it 'returns JSON of the post' do - PostCreator.any_instance.expects(:create).returns(new_post) + # Make sure our extensibility points are triggered + DiscourseEvent.expects(:trigger).with(:topic_created, new_post.topic, anything, user).once + DiscourseEvent.expects(:trigger).with(:post_created, new_post, anything, user).once + xhr :post, :create, {raw: 'test'} - ::JSON.parse(response.body).should be_present + + expect(response).to be_success + expect(::JSON.parse(response.body)).to be_present end it 'protects against dupes' do @@ -494,10 +495,10 @@ describe PostsController do title = "this is a title #{SecureRandom.hash}" xhr :post, :create, {raw: raw, title: title, wpid: 1} - response.should be_success + expect(response).to be_success xhr :post, :create, {raw: raw, title: title, wpid: 2} - response.should_not be_success + expect(response).not_to be_success end context "errors" do @@ -513,7 +514,7 @@ describe PostsController do it "does not succeed" do xhr :post, :create, {raw: 'test'} User.any_instance.expects(:flag_linked_posts_as_spam).never - response.should_not be_success + expect(response).not_to be_success end it "it triggers flag_linked_posts_as_spam when the post creator returns spam" do @@ -619,19 +620,19 @@ describe PostsController do it "ensures anonymous cannot see the revisions" do xhr :get, :revisions, post_id: post_revision.post_id, revision: post_revision.number - response.should be_forbidden + expect(response).to be_forbidden end it "ensures regular user cannot see the revisions" do u = log_in(:user) xhr :get, :revisions, post_id: post_revision.post_id, revision: post_revision.number - response.should be_forbidden + expect(response).to be_forbidden end it "ensures staff can see the revisions" do log_in(:admin) xhr :get, :revisions, post_id: post_revision.post_id, revision: post_revision.number - response.should be_success + expect(response).to be_success end it "ensures poster can see the revisions" do @@ -639,13 +640,13 @@ describe PostsController do post = Fabricate(:post, user: user, version: 3) pr = Fabricate(:post_revision, user: user, post: post) xhr :get, :revisions, post_id: pr.post_id, revision: pr.number - response.should be_success + expect(response).to be_success end it "ensures trust level 4 can see the revisions" do log_in(:trust_level_4) xhr :get, :revisions, post_id: post_revision.post_id, revision: post_revision.number - response.should be_success + expect(response).to be_success end end @@ -656,7 +657,7 @@ describe PostsController do it "ensures anyone can see the revisions" do xhr :get, :revisions, post_id: post_revision.post_id, revision: post_revision.number - response.should be_success + expect(response).to be_success end end @@ -670,7 +671,7 @@ describe PostsController do it "also work on deleted post" do xhr :get, :revisions, post_id: deleted_post_revision.post_id, revision: deleted_post_revision.number - response.should be_success + expect(response).to be_success end end @@ -684,7 +685,7 @@ describe PostsController do it "also work on deleted topic" do xhr :get, :revisions, post_id: post_revision.post_id, revision: post_revision.number - response.should be_success + expect(response).to be_success end end @@ -696,15 +697,15 @@ describe PostsController do it "raises an error when you can't see the post" do Guardian.any_instance.expects(:can_see?).with(post).returns(false) xhr :get, :expand_embed, id: post.id - response.should_not be_success + expect(response).not_to be_success end it "retrieves the body when you can see the post" do Guardian.any_instance.expects(:can_see?).with(post).returns(true) TopicEmbed.expects(:expanded_for).with(post).returns("full content") xhr :get, :expand_embed, id: post.id - response.should be_success - ::JSON.parse(response.body)['cooked'].should == "full content" + expect(response).to be_success + expect(::JSON.parse(response.body)['cooked']).to eq("full content") end end @@ -718,13 +719,13 @@ describe PostsController do it "raises an error if the user doesn't have permission to see the flagged posts" do Guardian.any_instance.expects(:can_see_flagged_posts?).returns(false) xhr :get, :flagged_posts, username: "system" - response.should be_forbidden + expect(response).to be_forbidden end it "can see the flagged posts when authorized" do Guardian.any_instance.expects(:can_see_flagged_posts?).returns(true) xhr :get, :flagged_posts, username: "system" - response.should be_success + expect(response).to be_success end it "only shows agreed and deferred flags" do @@ -745,9 +746,9 @@ describe PostsController do Guardian.any_instance.expects(:can_see_flagged_posts?).returns(true) xhr :get, :flagged_posts, username: user.username - response.should be_success + expect(response).to be_success - JSON.parse(response.body).length.should == 2 + expect(JSON.parse(response.body).length).to eq(2) end end @@ -764,13 +765,13 @@ describe PostsController do it "raises an error if the user doesn't have permission to see the deleted posts" do Guardian.any_instance.expects(:can_see_deleted_posts?).returns(false) xhr :get, :deleted_posts, username: "system" - response.should be_forbidden + expect(response).to be_forbidden end it "can see the deleted posts when authorized" do Guardian.any_instance.expects(:can_see_deleted_posts?).returns(true) xhr :get, :deleted_posts, username: "system" - response.should be_success + expect(response).to be_success end it "only shows posts deleted by other users" do @@ -786,16 +787,53 @@ describe PostsController do Guardian.any_instance.expects(:can_see_deleted_posts?).returns(true) xhr :get, :deleted_posts, username: user.username - response.should be_success + expect(response).to be_success data = JSON.parse(response.body) - data.length.should == 1 - data[0]["id"].should == post_deleted_by_admin.id - data[0]["deleted_by"]["id"].should == admin.id + expect(data.length).to eq(1) + expect(data[0]["id"]).to eq(post_deleted_by_admin.id) + expect(data[0]["deleted_by"]["id"]).to eq(admin.id) end end end + describe "view raw" do + describe "by ID" do + it "can be viewed by anonymous" do + post = Fabricate(:post, raw: "123456789") + xhr :get, :markdown_id, id: post.id + response.should be_success + response.body.should == "123456789" + end + end + + describe "by post number" do + it "can be viewed by anonymous" do + topic = Fabricate(:topic) + post = Fabricate(:post, topic: topic, post_number: 1, raw: "123456789") + post.save + xhr :get, :markdown_num, topic_id: topic.id, post_number: 1 + response.should be_success + response.body.should == "123456789" + end + end + end + + describe "short link" do + let(:topic) { Fabricate(:topic) } + let(:post) { Fabricate(:post, topic: topic) } + + it "redirects to the topic" do + xhr :get, :short_link, post_id: post.id + response.should be_redirect + end + + it "returns a 403 when access is denied" do + Guardian.any_instance.stubs(:can_see?).returns(false) + xhr :get, :short_link, post_id: post.id + response.should be_forbidden + end + end end diff --git a/spec/controllers/robots_txt_controller_spec.rb b/spec/controllers/robots_txt_controller_spec.rb index 4b034f9dfd..9a612c0e4b 100644 --- a/spec/controllers/robots_txt_controller_spec.rb +++ b/spec/controllers/robots_txt_controller_spec.rb @@ -7,13 +7,13 @@ describe RobotsTxtController do it "returns index when indexing is allowed" do SiteSetting.stubs(:allow_index_in_robots_txt).returns(true) get :index - response.should render_template :index + expect(response).to render_template :index end it "returns noindex when indexing is disallowed" do SiteSetting.stubs(:allow_index_in_robots_txt).returns(false) get :index - response.should render_template :no_index + expect(response).to render_template :no_index end end diff --git a/spec/controllers/search_controller_spec.rb b/spec/controllers/search_controller_spec.rb index 5d4d9e2930..d44f311880 100644 --- a/spec/controllers/search_controller_spec.rb +++ b/spec/controllers/search_controller_spec.rb @@ -12,11 +12,11 @@ describe SearchController do my_post = Fabricate(:post, raw: 'this is my really awesome post') xhr :get, :query, term: 'awesome', include_blurb: true - response.should be_success + expect(response).to be_success data = JSON.parse(response.body) - data['posts'][0]['id'].should == my_post.id - data['posts'][0]['blurb'].should == 'this is my really awesome post' - data['topics'][0]['id'].should == my_post.topic_id + expect(data['posts'][0]['id']).to eq(my_post.id) + expect(data['posts'][0]['blurb']).to eq('this is my really awesome post') + expect(data['topics'][0]['id']).to eq(my_post.topic_id) end end @@ -64,15 +64,15 @@ describe SearchController do context "search context" do it "raises an error with an invalid context type" do - lambda { + expect { xhr :get, :query, term: 'test', search_context: {type: 'security', id: 'hole'} - }.should raise_error(Discourse::InvalidParameters) + }.to raise_error(Discourse::InvalidParameters) end it "raises an error with a missing id" do - lambda { + expect { xhr :get, :query, term: 'test', search_context: {type: 'user'} - }.should raise_error(Discourse::InvalidParameters) + }.to raise_error(Discourse::InvalidParameters) end context "with a user" do @@ -82,7 +82,7 @@ describe SearchController do it "raises an error if the user can't see the context" do Guardian.any_instance.expects(:can_see?).with(user).returns(false) xhr :get, :query, term: 'test', search_context: {type: 'user', id: user.username} - response.should_not be_success + expect(response).not_to be_success end diff --git a/spec/controllers/session_controller_spec.rb b/spec/controllers/session_controller_spec.rb index f88d2a21c1..2a6dccae2a 100644 --- a/spec/controllers/session_controller_spec.rb +++ b/spec/controllers/session_controller_spec.rb @@ -8,15 +8,15 @@ describe SessionController do it "does not work when not in development mode" do Rails.env.stubs(:development?).returns(false) get :become, session_id: user.username - response.should_not be_redirect - session[:current_user_id].should be_blank + expect(response).not_to be_redirect + expect(session[:current_user_id]).to be_blank end it "works in developmenet mode" do Rails.env.stubs(:development?).returns(true) get :become, session_id: user.username - response.should be_redirect - session[:current_user_id].should == user.id + expect(response).to be_redirect + expect(session[:current_user_id]).to eq(user.id) end end @@ -60,13 +60,29 @@ describe SessionController do get :sso_login, Rack::Utils.parse_query(sso.payload) - response.should redirect_to('/') + expect(response).to redirect_to('/') logged_on_user = Discourse.current_user_provider.new(request.env).current_user expect(logged_on_user.email).to eq(user.email) expect(logged_on_user.single_sign_on_record.external_id).to eq("abc") expect(logged_on_user.single_sign_on_record.external_username).to eq('sam') end + it 'allows you to create an admin account' do + sso = get_sso('/a/') + sso.external_id = '666' # the number of the beast + sso.email = 'bob@bob.com' + sso.name = 'Sam Saffron' + sso.username = 'sam' + sso.custom_fields["shop_url"] = "http://my_shop.com" + sso.custom_fields["shop_name"] = "Sam" + sso.admin = true + + get :sso_login, Rack::Utils.parse_query(sso.payload) + + logged_on_user = Discourse.current_user_provider.new(request.env).current_user + expect(logged_on_user.admin).to eq(true) + end + it 'redirects to a non-relative url' do sso = get_sso("#{Discourse.base_url}/b/") sso.external_id = '666' # the number of the beast @@ -110,28 +126,30 @@ describe SessionController do sso.custom_fields["shop_name"] = "Sam" get :sso_login, Rack::Utils.parse_query(sso.payload) - response.should redirect_to('/a/') + expect(response).to redirect_to('/a/') logged_on_user = Discourse.current_user_provider.new(request.env).current_user # ensure nothing is transient logged_on_user = User.find(logged_on_user.id) - logged_on_user.email.should == 'bob@bob.com' - logged_on_user.name.should == 'Sam Saffron' - logged_on_user.username.should == 'sam' + expect(logged_on_user.admin).to eq(false) + expect(logged_on_user.email).to eq('bob@bob.com') + expect(logged_on_user.name).to eq('Sam Saffron') + expect(logged_on_user.username).to eq('sam') - logged_on_user.single_sign_on_record.external_id.should == "666" - logged_on_user.single_sign_on_record.external_username.should == 'sam' - logged_on_user.active.should == true - logged_on_user.custom_fields["shop_url"].should == "http://my_shop.com" - logged_on_user.custom_fields["shop_name"].should == "Sam" - logged_on_user.custom_fields["bla"].should == nil + expect(logged_on_user.single_sign_on_record.external_id).to eq("666") + expect(logged_on_user.single_sign_on_record.external_username).to eq('sam') + expect(logged_on_user.active).to eq(true) + expect(logged_on_user.custom_fields["shop_url"]).to eq("http://my_shop.com") + expect(logged_on_user.custom_fields["shop_name"]).to eq("Sam") + expect(logged_on_user.custom_fields["bla"]).to eq(nil) end it 'allows login to existing account with valid nonce' do sso = get_sso('/hello/world') sso.external_id = '997' + sso.sso_url = "http://somewhere.over.com/sso_login" user = Fabricate(:user) user.create_single_sign_on_record(external_id: '997', last_payload: '') @@ -139,16 +157,52 @@ describe SessionController do get :sso_login, Rack::Utils.parse_query(sso.payload) user.single_sign_on_record.reload - user.single_sign_on_record.last_payload.should == sso.unsigned_payload + expect(user.single_sign_on_record.last_payload).to eq(sso.unsigned_payload) - response.should redirect_to('/hello/world') + expect(response).to redirect_to('/hello/world') logged_on_user = Discourse.current_user_provider.new(request.env).current_user - user.id.should == logged_on_user.id + expect(user.id).to eq(logged_on_user.id) # nonce is bad now get :sso_login, Rack::Utils.parse_query(sso.payload) - response.code.should == '500' + expect(response.code).to eq('500') + end + + it 'can act as an SSO provider' do + SiteSetting.enable_sso_provider = true + SiteSetting.enable_sso = false + SiteSetting.enable_local_logins = true + SiteSetting.sso_secret = "topsecret" + + sso = SingleSignOn.new + sso.nonce = "mynonce" + sso.sso_secret = SiteSetting.sso_secret + sso.return_sso_url = "http://somewhere.over.rainbow/sso" + + get :sso_provider, Rack::Utils.parse_query(sso.payload) + + expect(response).to redirect_to("/login") + + user = Fabricate(:user, password: "frogs", active: true, admin: true) + EmailToken.update_all(confirmed: true) + + xhr :post, :create, login: user.username, password: "frogs", format: :json + + location = response.header["Location"] + expect(location).to match(/^http:\/\/somewhere.over.rainbow\/sso/) + + payload = location.split("?")[1] + + sso2 = SingleSignOn.parse(payload, "topsecret") + + expect(sso2.email).to eq(user.email) + expect(sso2.name).to eq(user.name) + expect(sso2.username).to eq(user.username) + expect(sso2.external_id).to eq(user.id.to_s) + expect(sso2.admin).to eq(true) + expect(sso2.moderator).to eq(false) + end describe 'local attribute override from SSO payload' do @@ -169,25 +223,25 @@ describe SessionController do @sso.name = @reversed_name @suggested_username = UserNameSuggester.suggest(@sso.username || @sso.name || @sso.email) - @suggested_name = User.suggest_name(@sso.name || @sso.username || @sso.email) + @suggested_name = User.suggest_name(@sso.name || @sso.username || @sso.email) @user.create_single_sign_on_record(external_id: '997', last_payload: '') end it 'stores the external attributes' do get :sso_login, Rack::Utils.parse_query(@sso.payload) @user.single_sign_on_record.reload - @user.single_sign_on_record.external_username.should == @sso.username - @user.single_sign_on_record.external_email.should == @sso.email - @user.single_sign_on_record.external_name.should == @sso.name + expect(@user.single_sign_on_record.external_username).to eq(@sso.username) + expect(@user.single_sign_on_record.external_email).to eq(@sso.email) + expect(@user.single_sign_on_record.external_name).to eq(@sso.name) end it 'overrides attributes' do get :sso_login, Rack::Utils.parse_query(@sso.payload) logged_on_user = Discourse.current_user_provider.new(request.env).current_user - logged_on_user.username.should == @suggested_username - logged_on_user.email.should == "#{@reversed_username}@garbage.org" - logged_on_user.name.should == @suggested_name + expect(logged_on_user.username).to eq(@suggested_username) + expect(logged_on_user.email).to eq("#{@reversed_username}@garbage.org") + expect(logged_on_user.name).to eq(@suggested_name) end it 'does not change matching attributes for an existing account' do @@ -198,9 +252,9 @@ describe SessionController do get :sso_login, Rack::Utils.parse_query(@sso.payload) logged_on_user = Discourse.current_user_provider.new(request.env).current_user - logged_on_user.username.should == @user.username - logged_on_user.name.should == @user.name - logged_on_user.email.should == @user.email + expect(logged_on_user.username).to eq(@user.username) + expect(logged_on_user.name).to eq(@user.name) + expect(logged_on_user.email).to eq(@user.email) end it 'does not change attributes for unchanged external attributes' do @@ -211,9 +265,9 @@ describe SessionController do get :sso_login, Rack::Utils.parse_query(@sso.payload) logged_on_user = Discourse.current_user_provider.new(request.env).current_user - logged_on_user.username.should == @user.username - logged_on_user.email.should == @user.email - logged_on_user.name.should == @user.name + expect(logged_on_user.username).to eq(@user.username) + expect(logged_on_user.email).to eq(@user.email) + expect(logged_on_user.name).to eq(@user.name) end end end @@ -229,13 +283,13 @@ describe SessionController do end it "raises an error when the login isn't present" do - lambda { xhr :post, :create }.should raise_error(ActionController::ParameterMissing) + expect { xhr :post, :create }.to raise_error(ActionController::ParameterMissing) end describe 'invalid password' do it "should return an error with an invalid password" do xhr :post, :create, login: user.username, password: 'sssss' - ::JSON.parse(response.body)['error'].should be_present + expect(::JSON.parse(response.body)['error']).to be_present end end @@ -243,7 +297,7 @@ describe SessionController do it "should return an error with an invalid password if too long" do User.any_instance.expects(:confirm_password?).never xhr :post, :create, login: user.username, password: ('s' * (User.max_password_length + 1)) - ::JSON.parse(response.body)['error'].should be_present + expect(::JSON.parse(response.body)['error']).to be_present end end @@ -252,7 +306,7 @@ describe SessionController do User.any_instance.stubs(:suspended?).returns(true) User.any_instance.stubs(:suspended_till).returns(2.days.from_now) xhr :post, :create, login: user.username, password: 'myawesomepassword' - ::JSON.parse(response.body)['error'].should be_present + expect(::JSON.parse(response.body)['error']).to be_present end end @@ -270,9 +324,9 @@ describe SessionController do user.reload - session[:current_user_id].should == user.id - user.auth_token.should be_present - cookies[:_t].should == user.auth_token + expect(session[:current_user_id]).to eq(user.id) + expect(user.auth_token).to be_present + expect(cookies[:_t]).to eq(user.auth_token) end end @@ -280,7 +334,7 @@ describe SessionController do it 'fails' do SiteSetting.stubs(:enable_local_logins).returns(false) xhr :post, :create, login: user.username, password: 'myawesomepassword' - response.status.to_i.should == 500 + expect(response.status.to_i).to eq(500) end end @@ -291,7 +345,7 @@ describe SessionController do end it 'sets a session id' do - session[:current_user_id].should == user.id + expect(session[:current_user_id]).to eq(user.id) end end @@ -301,7 +355,7 @@ describe SessionController do end it 'sets a session id' do - session[:current_user_id].should == user.id + expect(session[:current_user_id]).to eq(user.id) end end @@ -311,12 +365,12 @@ describe SessionController do it "strips spaces from the username" do xhr :post, :create, login: username, password: 'myawesomepassword' - ::JSON.parse(response.body)['error'].should_not be_present + expect(::JSON.parse(response.body)['error']).not_to be_present end it "strips spaces from the email" do xhr :post, :create, login: email, password: 'myawesomepassword' - ::JSON.parse(response.body)['error'].should_not be_present + expect(::JSON.parse(response.body)['error']).not_to be_present end end @@ -331,7 +385,7 @@ describe SessionController do end it "doesn't log in the user" do - session[:current_user_id].should be_blank + expect(session[:current_user_id]).to be_blank end it "shows the 'not approved' error message" do @@ -348,7 +402,7 @@ describe SessionController do end it 'sets a session id' do - session[:current_user_id].should == user.id + expect(session[:current_user_id]).to eq(user.id) end end end @@ -364,22 +418,22 @@ describe SessionController do User.any_instance.stubs(:admin?).returns(true) ActionDispatch::Request.any_instance.stubs(:remote_ip).returns(permitted_ip_address) xhr :post, :create, login: user.username, password: 'myawesomepassword' - session[:current_user_id].should == user.id + expect(session[:current_user_id]).to eq(user.id) end it 'returns an error for admin not at the ip address' do User.any_instance.stubs(:admin?).returns(true) ActionDispatch::Request.any_instance.stubs(:remote_ip).returns("111.234.23.12") xhr :post, :create, login: user.username, password: 'myawesomepassword' - JSON.parse(response.body)['error'].should be_present - session[:current_user_id].should_not == user.id + expect(JSON.parse(response.body)['error']).to be_present + expect(session[:current_user_id]).not_to eq(user.id) end it 'is successful for non-admin not at the ip address' do User.any_instance.stubs(:admin?).returns(false) ActionDispatch::Request.any_instance.stubs(:remote_ip).returns("111.234.23.12") xhr :post, :create, login: user.username, password: 'myawesomepassword' - session[:current_user_id].should == user.id + expect(session[:current_user_id]).to eq(user.id) end end end @@ -391,7 +445,7 @@ describe SessionController do it "doesn't log in the user" do post_login - session[:current_user_id].should be_blank + expect(session[:current_user_id]).to be_blank end it "shows the 'not activated' error message" do @@ -421,24 +475,24 @@ describe SessionController do end it 'removes the session variable' do - session[:current_user_id].should be_blank + expect(session[:current_user_id]).to be_blank end it 'removes the auth token cookie' do - cookies[:_t].should be_blank + expect(cookies[:_t]).to be_blank end end describe '.forgot_password' do it 'raises an error without a username parameter' do - lambda { xhr :post, :forgot_password }.should raise_error(ActionController::ParameterMissing) + expect { xhr :post, :forgot_password }.to raise_error(ActionController::ParameterMissing) end context 'for a non existant username' do it "doesn't generate a new token for a made up username" do - lambda { xhr :post, :forgot_password, login: 'made_up'}.should_not change(EmailToken, :count) + expect { xhr :post, :forgot_password, login: 'made_up'}.not_to change(EmailToken, :count) end it "doesn't enqueue an email" do @@ -453,11 +507,11 @@ describe SessionController do it "returns a 500 if local logins are disabled" do SiteSetting.enable_local_logins = false xhr :post, :forgot_password, login: user.username - response.code.to_i.should == 500 + expect(response.code.to_i).to eq(500) end it "generates a new token for a made up username" do - lambda { xhr :post, :forgot_password, login: user.username}.should change(EmailToken, :count) + expect { xhr :post, :forgot_password, login: user.username}.to change(EmailToken, :count) end it "enqueues an email" do @@ -466,13 +520,25 @@ describe SessionController do end end + context 'do nothing to system username' do + let(:user) { Discourse.system_user } + + it 'generates no token for system username' do + expect { xhr :post, :forgot_password, login: user.username}.not_to change(EmailToken, :count) + end + + it 'enqueues no email' do + Jobs.expects(:enqueue).never + xhr :post, :forgot_password, login: user.username + end + end end describe '.current' do context "when not logged in" do it "retuns 404" do xhr :get, :current - response.should_not be_success + expect(response).not_to be_success end end @@ -481,10 +547,10 @@ describe SessionController do it "returns the JSON for the user" do xhr :get, :current - response.should be_success + expect(response).to be_success json = ::JSON.parse(response.body) - json['current_user'].should be_present - json['current_user']['id'].should == user.id + expect(json['current_user']).to be_present + expect(json['current_user']['id']).to eq(user.id) end end end diff --git a/spec/controllers/site_customizations_controller_spec.rb b/spec/controllers/site_customizations_controller_spec.rb new file mode 100644 index 0000000000..d9948d5950 --- /dev/null +++ b/spec/controllers/site_customizations_controller_spec.rb @@ -0,0 +1,45 @@ +require 'spec_helper' + +describe SiteCustomizationsController do + + before do + SiteCustomization.clear_cache! + end + + it 'can deliver enabled css' do + SiteCustomization.create!(name: '1', + user_id: -1, + enabled: true, + mobile_stylesheet: '.a1{margin: 1px;}', + stylesheet: '.b1{margin: 1px;}' + ) + + SiteCustomization.create!(name: '2', + user_id: -1, + enabled: true, + mobile_stylesheet: '.a2{margin: 1px;}', + stylesheet: '.b2{margin: 1px;}' + ) + + get :show, key: SiteCustomization::ENABLED_KEY, format: :css, target: 'mobile' + expect(response.body).to match(/\.a1.*\.a2/m) + + get :show, key: SiteCustomization::ENABLED_KEY, format: :css + expect(response.body).to match(/\.b1.*\.b2/m) + end + + it 'can deliver specific css' do + c = SiteCustomization.create!(name: '1', + user_id: -1, + enabled: true, + mobile_stylesheet: '.a1{margin: 1px;}', + stylesheet: '.b1{margin: 1px;}' + ) + + get :show, key: c.key, format: :css, target: 'mobile' + expect(response.body).to match(/\.a1/) + + get :show, key: c.key, format: :css + expect(response.body).to match(/\.b1/) + end +end diff --git a/spec/controllers/static_controller_spec.rb b/spec/controllers/static_controller_spec.rb index 9621593d9c..786445e16b 100644 --- a/spec/controllers/static_controller_spec.rb +++ b/spec/controllers/static_controller_spec.rb @@ -17,12 +17,12 @@ describe StaticController do end it 'renders the static file if present' do - response.should be_success + expect(response).to be_success end it "renders the file" do - response.should render_template('static/show') - assigns(:page).should == 'faq' + expect(response).to render_template('static/show') + expect(assigns(:page)).to eq('faq') end end @@ -33,7 +33,7 @@ describe StaticController do context "when #{setting_name} site setting is NOT set" do it "renders the #{id} page" do expect(subject).to render_template("static/show") - assigns(:page).should == id + expect(assigns(:page)).to eq(id) end end @@ -50,20 +50,20 @@ describe StaticController do context "with a missing file" do it "should respond 404" do xhr :get, :show, id: 'does-not-exist' - response.response_code.should == 404 + expect(response.response_code).to eq(404) end end it 'should redirect to / when logged in and path is /login' do log_in xhr :get, :show, id: 'login' - response.should redirect_to '/' + expect(response).to redirect_to '/' end it "should display the login template when login is required" do SiteSetting.stubs(:login_required).returns(true) xhr :get, :show, id: 'login' - response.should be_success + expect(response).to be_success end end diff --git a/spec/controllers/topics_controller_spec.rb b/spec/controllers/topics_controller_spec.rb index d8500e82d0..4432b25b49 100644 --- a/spec/controllers/topics_controller_spec.rb +++ b/spec/controllers/topics_controller_spec.rb @@ -10,37 +10,37 @@ describe TopicsController do it "returns the JSON in the format our wordpress plugin needs" do xhr :get, :wordpress, topic_id: topic.id, best: 3 - response.should be_success + expect(response).to be_success json = ::JSON.parse(response.body) - json.should be_present + expect(json).to be_present # The JSON has the data the wordpress plugin needs - json['id'].should == topic.id - json['posts_count'].should == 2 - json['filtered_posts_count'].should == 2 + expect(json['id']).to eq(topic.id) + expect(json['posts_count']).to eq(2) + expect(json['filtered_posts_count']).to eq(2) # Posts - json['posts'].size.should == 1 + expect(json['posts'].size).to eq(1) post = json['posts'][0] - post['id'].should == p2.id - post['username'].should == user.username - post['avatar_template'].should == "#{Discourse.base_url_no_prefix}#{user.avatar_template}" - post['name'].should == user.name - post['created_at'].should be_present - post['cooked'].should == p2.cooked + expect(post['id']).to eq(p2.id) + expect(post['username']).to eq(user.username) + expect(post['avatar_template']).to eq("#{Discourse.base_url_no_prefix}#{user.avatar_template}") + expect(post['name']).to eq(user.name) + expect(post['created_at']).to be_present + expect(post['cooked']).to eq(p2.cooked) # Participants - json['participants'].size.should == 1 + expect(json['participants'].size).to eq(1) participant = json['participants'][0] - participant['id'].should == user.id - participant['username'].should == user.username - participant['avatar_template'].should == "#{Discourse.base_url_no_prefix}#{user.avatar_template}" + expect(participant['id']).to eq(user.id) + expect(participant['username']).to eq(user.username) + expect(participant['avatar_template']).to eq("#{Discourse.base_url_no_prefix}#{user.avatar_template}") end end context 'move_posts' do it 'needs you to be logged in' do - lambda { xhr :post, :move_posts, topic_id: 111, title: 'blah', post_ids: [1,2,3] }.should raise_error(Discourse::NotLoggedIn) + expect { xhr :post, :move_posts, topic_id: 111, title: 'blah', post_ids: [1,2,3] }.to raise_error(Discourse::NotLoggedIn) end describe 'moving to a new topic' do @@ -49,13 +49,13 @@ describe TopicsController do let(:topic) { p1.topic } it "raises an error without postIds" do - lambda { xhr :post, :move_posts, topic_id: topic.id, title: 'blah' }.should raise_error(ActionController::ParameterMissing) + expect { xhr :post, :move_posts, topic_id: topic.id, title: 'blah' }.to raise_error(ActionController::ParameterMissing) end it "raises an error when the user doesn't have permission to move the posts" do Guardian.any_instance.expects(:can_move_posts?).returns(false) xhr :post, :move_posts, topic_id: topic.id, title: 'blah', post_ids: [1,2,3] - response.should be_forbidden + expect(response).to be_forbidden end context 'success' do @@ -67,10 +67,10 @@ describe TopicsController do end it "returns success" do - response.should be_success + expect(response).to be_success result = ::JSON.parse(response.body) - result['success'].should == true - result['url'].should be_present + expect(result['success']).to eq(true) + expect(result['url']).to be_present end end @@ -83,10 +83,10 @@ describe TopicsController do end it "returns JSON with a false success" do - response.should be_success + expect(response).to be_success result = ::JSON.parse(response.body) - result['success'].should == false - result['url'].should be_blank + expect(result['success']).to eq(false) + expect(result['url']).to be_blank end end end @@ -127,10 +127,10 @@ describe TopicsController do end it "returns success" do - response.should be_success + expect(response).to be_success result = ::JSON.parse(response.body) - result['success'].should == true - result['url'].should be_present + expect(result['success']).to eq(true) + expect(result['url']).to be_present end end @@ -143,10 +143,10 @@ describe TopicsController do end it "returns JSON with a false success" do - response.should be_success + expect(response).to be_success result = ::JSON.parse(response.body) - result['success'].should == false - result['url'].should be_blank + expect(result['success']).to eq(false) + expect(result['url']).to be_blank end end end @@ -154,7 +154,7 @@ describe TopicsController do context "merge_topic" do it 'needs you to be logged in' do - lambda { xhr :post, :merge_topic, topic_id: 111, destination_topic_id: 345 }.should raise_error(Discourse::NotLoggedIn) + expect { xhr :post, :merge_topic, topic_id: 111, destination_topic_id: 345 }.to raise_error(Discourse::NotLoggedIn) end describe 'moving to a new topic' do @@ -163,13 +163,13 @@ describe TopicsController do let(:topic) { p1.topic } it "raises an error without destination_topic_id" do - lambda { xhr :post, :merge_topic, topic_id: topic.id }.should raise_error(ActionController::ParameterMissing) + expect { xhr :post, :merge_topic, topic_id: topic.id }.to raise_error(ActionController::ParameterMissing) end it "raises an error when the user doesn't have permission to merge" do Guardian.any_instance.expects(:can_move_posts?).returns(false) xhr :post, :merge_topic, topic_id: 111, destination_topic_id: 345 - response.should be_forbidden + expect(response).to be_forbidden end let(:dest_topic) { Fabricate(:topic) } @@ -183,10 +183,10 @@ describe TopicsController do end it "returns success" do - response.should be_success + expect(response).to be_success result = ::JSON.parse(response.body) - result['success'].should == true - result['url'].should be_present + expect(result['success']).to eq(true) + expect(result['url']).to be_present end end @@ -197,14 +197,14 @@ describe TopicsController do context 'change_post_owners' do it 'needs you to be logged in' do - lambda { xhr :post, :change_post_owners, topic_id: 111, username: 'user_a', post_ids: [1,2,3] }.should raise_error(Discourse::NotLoggedIn) + expect { xhr :post, :change_post_owners, topic_id: 111, username: 'user_a', post_ids: [1,2,3] }.to raise_error(Discourse::NotLoggedIn) end describe 'forbidden to moderators' do let!(:moderator) { log_in(:moderator) } it 'correctly denies' do xhr :post, :change_post_owners, topic_id: 111, username: 'user_a', post_ids: [1,2,3] - response.should be_forbidden + expect(response).to be_forbidden end end @@ -213,7 +213,7 @@ describe TopicsController do it 'correctly denies' do xhr :post, :change_post_owners, topic_id: 111, username: 'user_a', post_ids: [1,2,3] - response.should be_forbidden + expect(response).to be_forbidden end end @@ -224,21 +224,21 @@ describe TopicsController do let(:p1) { Fabricate(:post, topic_id: topic.id) } it "raises an error with a parameter missing" do - lambda { xhr :post, :change_post_owners, topic_id: 111, post_ids: [1,2,3] }.should raise_error(ActionController::ParameterMissing) - lambda { xhr :post, :change_post_owners, topic_id: 111, username: 'user_a' }.should raise_error(ActionController::ParameterMissing) + expect { xhr :post, :change_post_owners, topic_id: 111, post_ids: [1,2,3] }.to raise_error(ActionController::ParameterMissing) + expect { xhr :post, :change_post_owners, topic_id: 111, username: 'user_a' }.to raise_error(ActionController::ParameterMissing) end it "calls PostRevisor" do PostRevisor.any_instance.expects(:revise!) xhr :post, :change_post_owners, topic_id: topic.id, username: user_a.username_lower, post_ids: [p1.id] - response.should be_success + expect(response).to be_success end it "changes the user" do old_user = p1.user xhr :post, :change_post_owners, topic_id: topic.id, username: user_a.username_lower, post_ids: [p1.id] p1.reload - old_user.should_not == p1.user + expect(old_user).not_to eq(p1.user) end # Make sure that p1.reload isn't changing the user for us @@ -246,8 +246,8 @@ describe TopicsController do old_user = p1.user # xhr :post, :change_post_owners, topic_id: topic.id, username: user_a.username_lower, post_ids: [p1.id] p1.reload - p1.user.should_not == nil - old_user.should == p1.user + expect(p1.user).not_to eq(nil) + expect(old_user).to eq(p1.user) end let(:p2) { Fabricate(:post, topic_id: topic.id) } @@ -256,8 +256,8 @@ describe TopicsController do xhr :post, :change_post_owners, topic_id: topic.id, username: user_a.username_lower, post_ids: [p1.id, p2.id] p1.reload p2.reload - p1.user.should_not == nil - p1.user.should == p2.user + expect(p1.user).not_to eq(nil) + expect(p1.user).to eq(p2.user) end end end @@ -268,21 +268,21 @@ describe TopicsController do let(:raw) { 'this body is long enough to search for' } it "requires a title" do - -> { xhr :get, :similar_to, raw: raw }.should raise_error(ActionController::ParameterMissing) + expect { xhr :get, :similar_to, raw: raw }.to raise_error(ActionController::ParameterMissing) end it "requires a raw body" do - -> { xhr :get, :similar_to, title: title }.should raise_error(ActionController::ParameterMissing) + expect { xhr :get, :similar_to, title: title }.to raise_error(ActionController::ParameterMissing) end it "raises an error if the title length is below the minimum" do SiteSetting.stubs(:min_title_similar_length).returns(100) - -> { xhr :get, :similar_to, title: title, raw: raw }.should raise_error(Discourse::InvalidParameters) + expect { xhr :get, :similar_to, title: title, raw: raw }.to raise_error(Discourse::InvalidParameters) end it "raises an error if the body length is below the minimum" do SiteSetting.stubs(:min_body_similar_length).returns(100) - -> { xhr :get, :similar_to, title: title, raw: raw }.should raise_error(Discourse::InvalidParameters) + expect { xhr :get, :similar_to, title: title, raw: raw }.to raise_error(Discourse::InvalidParameters) end describe "minimum_topics_similar" do @@ -326,7 +326,7 @@ describe TopicsController do context 'clear_pin' do it 'needs you to be logged in' do - lambda { xhr :put, :clear_pin, topic_id: 1 }.should raise_error(Discourse::NotLoggedIn) + expect { xhr :put, :clear_pin, topic_id: 1 }.to raise_error(Discourse::NotLoggedIn) end context 'when logged in' do @@ -336,7 +336,7 @@ describe TopicsController do it "fails when the user can't see the topic" do Guardian.any_instance.expects(:can_see?).with(topic).returns(false) xhr :put, :clear_pin, topic_id: topic.id - response.should_not be_success + expect(response).not_to be_success end describe 'when the user can see the topic' do @@ -347,7 +347,7 @@ describe TopicsController do it "succeeds" do xhr :put, :clear_pin, topic_id: topic.id - response.should be_success + expect(response).to be_success end end @@ -357,7 +357,7 @@ describe TopicsController do context 'status' do it 'needs you to be logged in' do - lambda { xhr :put, :status, topic_id: 1, status: 'visible', enabled: true }.should raise_error(Discourse::NotLoggedIn) + expect { xhr :put, :status, topic_id: 1, status: 'visible', enabled: true }.to raise_error(Discourse::NotLoggedIn) end describe 'when logged in' do @@ -369,19 +369,19 @@ describe TopicsController do it "raises an exception if you can't change it" do Guardian.any_instance.expects(:can_moderate?).with(@topic).returns(false) xhr :put, :status, topic_id: @topic.id, status: 'visible', enabled: 'true' - response.should be_forbidden + expect(response).to be_forbidden end it 'requires the status parameter' do - lambda { xhr :put, :status, topic_id: @topic.id, enabled: true }.should raise_error(ActionController::ParameterMissing) + expect { xhr :put, :status, topic_id: @topic.id, enabled: true }.to raise_error(ActionController::ParameterMissing) end it 'requires the enabled parameter' do - lambda { xhr :put, :status, topic_id: @topic.id, status: 'visible' }.should raise_error(ActionController::ParameterMissing) + expect { xhr :put, :status, topic_id: @topic.id, status: 'visible' }.to raise_error(ActionController::ParameterMissing) end it 'raises an error with a status not in the whitelist' do - lambda { xhr :put, :status, topic_id: @topic.id, status: 'title', enabled: 'true' }.should raise_error(Discourse::InvalidParameters) + expect { xhr :put, :status, topic_id: @topic.id, status: 'title', enabled: 'true' }.to raise_error(Discourse::InvalidParameters) end it 'calls update_status on the forum topic with false' do @@ -401,7 +401,7 @@ describe TopicsController do context 'delete_timings' do it 'needs you to be logged in' do - lambda { xhr :delete, :destroy_timings, topic_id: 1 }.should raise_error(Discourse::NotLoggedIn) + expect { xhr :delete, :destroy_timings, topic_id: 1 }.to raise_error(Discourse::NotLoggedIn) end context 'when logged in' do @@ -424,11 +424,11 @@ describe TopicsController do describe 'mute/unmute' do it 'needs you to be logged in' do - lambda { xhr :put, :mute, topic_id: 99}.should raise_error(Discourse::NotLoggedIn) + expect { xhr :put, :mute, topic_id: 99}.to raise_error(Discourse::NotLoggedIn) end it 'needs you to be logged in' do - lambda { xhr :put, :unmute, topic_id: 99}.should raise_error(Discourse::NotLoggedIn) + expect { xhr :put, :unmute, topic_id: 99}.to raise_error(Discourse::NotLoggedIn) end describe 'when logged in' do @@ -436,52 +436,13 @@ describe TopicsController do @topic = Fabricate(:topic, user: log_in) end - it "changes the user's starred flag when the parameter is present" do - Topic.any_instance.expects(:toggle_mute).with(@topic.user) - xhr :put, :mute, topic_id: @topic.id, starred: 'true' - end - - it "removes the user's starred flag when the parameter is not true" do - Topic.any_instance.expects(:toggle_mute).with(@topic.user) - xhr :put, :unmute, topic_id: @topic.id, starred: 'false' - end - end end - describe 'star' do - - it 'needs you to be logged in' do - lambda { xhr :put, :star, topic_id: 1, starred: true }.should raise_error(Discourse::NotLoggedIn) - end - - describe 'when logged in' do - before do - @topic = Fabricate(:topic, user: log_in) - end - - it "ensures the user can see the topic" do - Guardian.any_instance.expects(:can_see?).with(@topic).returns(false) - xhr :put, :star, topic_id: @topic.id, starred: 'true' - response.should be_forbidden - end - - it "changes the user's starred flag when the parameter is present" do - Topic.any_instance.expects(:toggle_star).with(@topic.user, true) - xhr :put, :star, topic_id: @topic.id, starred: 'true' - end - - it "removes the user's starred flag when the parameter is not true" do - Topic.any_instance.expects(:toggle_star).with(@topic.user, false) - xhr :put, :star, topic_id: @topic.id, starred: 'false' - end - end - end - describe 'recover' do it "won't allow us to recover a topic when we're not logged in" do - lambda { xhr :put, :recover, topic_id: 1 }.should raise_error(Discourse::NotLoggedIn) + expect { xhr :put, :recover, topic_id: 1 }.to raise_error(Discourse::NotLoggedIn) end describe 'when logged in' do @@ -491,7 +452,7 @@ describe TopicsController do it "raises an exception when the user doesn't have permission to delete the topic" do Guardian.any_instance.expects(:can_recover_topic?).with(topic).returns(false) xhr :put, :recover, topic_id: topic.id - response.should be_forbidden + expect(response).to be_forbidden end end @@ -503,7 +464,7 @@ describe TopicsController do it 'succeeds' do PostDestroyer.any_instance.expects(:recover) xhr :put, :recover, topic_id: topic.id - response.should be_success + expect(response).to be_success end end end @@ -512,7 +473,7 @@ describe TopicsController do describe 'delete' do it "won't allow us to delete a topic when we're not logged in" do - lambda { xhr :delete, :destroy, id: 1 }.should raise_error(Discourse::NotLoggedIn) + expect { xhr :delete, :destroy, id: 1 }.to raise_error(Discourse::NotLoggedIn) end describe 'when logged in' do @@ -522,7 +483,7 @@ describe TopicsController do it "raises an exception when the user doesn't have permission to delete the topic" do Guardian.any_instance.expects(:can_delete?).with(topic).returns(false) xhr :delete, :destroy, id: topic.id - response.should be_forbidden + expect(response).to be_forbidden end end @@ -534,7 +495,7 @@ describe TopicsController do it 'succeeds' do PostDestroyer.any_instance.expects(:destroy) xhr :delete, :destroy, id: topic.id - response.should be_success + expect(response).to be_success end end @@ -547,18 +508,18 @@ describe TopicsController do it "returns JSON for the slug" do xhr :get, :id_for_slug, slug: topic.slug - response.should be_success + expect(response).to be_success json = ::JSON.parse(response.body) - json.should be_present - json['topic_id'].should == topic.id - json['url'].should == topic.url - json['slug'].should == topic.slug + expect(json).to be_present + expect(json['topic_id']).to eq(topic.id) + expect(json['url']).to eq(topic.url) + expect(json['slug']).to eq(topic.slug) end it "returns invalid access if the user can't see the topic" do Guardian.any_instance.expects(:can_see?).with(topic).returns(false) xhr :get, :id_for_slug, slug: topic.slug - response.should_not be_success + expect(response).not_to be_success end end @@ -570,12 +531,12 @@ describe TopicsController do it 'shows a topic correctly' do xhr :get, :show, topic_id: topic.id, slug: topic.slug - response.should be_success + expect(response).to be_success end it 'return 404 for an invalid page' do xhr :get, :show, topic_id: topic.id, slug: topic.slug, page: 2 - response.code.should == "404" + expect(response.code).to eq("404") end it 'can find a topic given a slug in the id param' do @@ -611,14 +572,14 @@ describe TopicsController do end it 'records a view' do - lambda { xhr :get, :show, topic_id: topic.id, slug: topic.slug }.should change(TopicViewItem, :count).by(1) + expect { xhr :get, :show, topic_id: topic.id, slug: topic.slug }.to change(TopicViewItem, :count).by(1) end it 'records incoming links' do user = Fabricate(:user) get :show, topic_id: topic.id, slug: topic.slug, u: user.username - IncomingLink.count.should == 1 + expect(IncomingLink.count).to eq(1) end it 'records redirects' do @@ -629,7 +590,7 @@ describe TopicsController do get :show, topic_id: topic.id, slug: topic.slug link = IncomingLink.first - link.referer.should == 'http://twitter.com' + expect(link.referer).to eq('http://twitter.com') end it 'tracks a visit for all html requests' do @@ -641,7 +602,7 @@ describe TopicsController do context 'consider for a promotion' do let!(:user) { log_in(:coding_horror) } let(:promotion) do - result = mock + result = double Promotion.stubs(:new).with(user).returns(result) result end @@ -656,19 +617,16 @@ describe TopicsController do context 'filters' do it 'grabs first page when no filter is provided' do - SiteSetting.stubs(:posts_chunksize).returns(20) TopicView.any_instance.expects(:filter_posts_in_range).with(0, 19) xhr :get, :show, topic_id: topic.id, slug: topic.slug end it 'grabs first page when first page is provided' do - SiteSetting.stubs(:posts_chunksize).returns(20) TopicView.any_instance.expects(:filter_posts_in_range).with(0, 19) xhr :get, :show, topic_id: topic.id, slug: topic.slug, page: 1 end it 'grabs correct range when a page number is provided' do - SiteSetting.stubs(:posts_chunksize).returns(20) TopicView.any_instance.expects(:filter_posts_in_range).with(20, 39) xhr :get, :show, topic_id: topic.id, slug: topic.slug, page: 2 end @@ -704,7 +662,7 @@ describe TopicsController do expect(response).to be_successful topic.reload # free test, only costs a reload - topic.views.should == 1 + expect(topic.views).to eq(1) end it 'returns 403 for an invalid key' do @@ -720,14 +678,14 @@ describe TopicsController do it 'renders rss of the topic' do get :feed, topic_id: topic.id, slug: 'foo', format: :rss - response.should be_success - response.content_type.should == 'application/rss+xml' + expect(response).to be_success + expect(response.content_type).to eq('application/rss+xml') end end describe 'update' do it "won't allow us to update a topic when we're not logged in" do - lambda { xhr :put, :update, topic_id: 1, slug: 'xyz' }.should raise_error(Discourse::NotLoggedIn) + expect { xhr :put, :update, topic_id: 1, slug: 'xyz' }.to raise_error(Discourse::NotLoggedIn) end describe 'when logged in' do @@ -740,7 +698,7 @@ describe TopicsController do it "raises an exception when the user doesn't have permission to update the topic" do Guardian.any_instance.expects(:can_edit?).with(@topic).returns(false) xhr :put, :update, topic_id: @topic.id, slug: @topic.title - response.should be_forbidden + expect(response).to be_forbidden end end @@ -751,14 +709,14 @@ describe TopicsController do it 'succeeds' do xhr :put, :update, topic_id: @topic.id, slug: @topic.title - response.should be_success - ::JSON.parse(response.body)['basic_topic'].should be_present + expect(response).to be_success + expect(::JSON.parse(response.body)['basic_topic']).to be_present end it 'allows a change of title' do xhr :put, :update, topic_id: @topic.id, slug: @topic.title, title: 'This is a new title for the topic' @topic.reload - @topic.title.should == 'This is a new title for the topic' + expect(@topic.title).to eq('This is a new title for the topic') end it 'triggers a change of category' do @@ -766,6 +724,11 @@ describe TopicsController do xhr :put, :update, topic_id: @topic.id, slug: @topic.title, category_id: 123 end + it 'allows to change category to "uncategorized"' do + Topic.any_instance.expects(:change_category_to_id).with(0).returns(true) + xhr :put, :update, topic_id: @topic.id, slug: @topic.title, category_id: "" + end + it "returns errors with invalid titles" do xhr :put, :update, topic_id: @topic.id, slug: @topic.title, title: 'asdf' expect(response).not_to be_success @@ -774,7 +737,7 @@ describe TopicsController do it "returns errors when the rate limit is exceeded" do EditRateLimiter.any_instance.expects(:performed!).raises(RateLimiter::LimitExceeded.new(60)) xhr :put, :update, topic_id: @topic.id, slug: @topic.title, title: 'This is a new title for the topic' - response.should_not be_success + expect(response).not_to be_success end it "returns errors with invalid categories" do @@ -783,6 +746,28 @@ describe TopicsController do expect(response).not_to be_success end + it "doesn't call the PostRevisor when there is no changes" do + PostRevisor.any_instance.expects(:revise!).never + xhr :put, :update, topic_id: @topic.id, slug: @topic.title, title: @topic.title, category_id: @topic.category_id + expect(response).to be_success + end + + context 'when topic is private' do + before do + @topic.archetype = Archetype.private_message + @topic.category = nil + @topic.save! + end + + context 'when there are no changes' do + it 'does not call the PostRevisor' do + PostRevisor.any_instance.expects(:revise!).never + xhr :put, :update, topic_id: @topic.id, slug: @topic.title, title: @topic.title, category_id: nil + expect(response).to be_success + end + end + end + context "allow_uncategorized_topics is false" do before do SiteSetting.stubs(:allow_uncategorized_topics).returns(false) @@ -791,7 +776,7 @@ describe TopicsController do it "can add a category to an uncategorized topic" do Topic.any_instance.expects(:change_category_to_id).with(456).returns(true) xhr :put, :update, topic_id: @topic.id, slug: @topic.title, category_id: 456 - response.should be_success + expect(response).to be_success end end @@ -805,21 +790,21 @@ describe TopicsController do it "works correctly" do group = Fabricate(:group) topic = Fabricate(:topic) - admin = log_in(:admin) + _admin = log_in(:admin) xhr :post, :invite, topic_id: topic.id, email: 'hiro@from.heros', group_ids: "#{group.id}" - response.should be_success + expect(response).to be_success invite = Invite.find_by(email: 'hiro@from.heros') groups = invite.groups.to_a - groups.count.should == 1 - groups[0].id.should == group.id + expect(groups.count).to eq(1) + expect(groups[0].id).to eq(group.id) end end it "won't allow us to invite toa topic when we're not logged in" do - lambda { xhr :post, :invite, topic_id: 1, email: 'jake@adventuretime.ooo' }.should raise_error(Discourse::NotLoggedIn) + expect { xhr :post, :invite, topic_id: 1, email: 'jake@adventuretime.ooo' }.to raise_error(Discourse::NotLoggedIn) end describe 'when logged in' do @@ -828,13 +813,13 @@ describe TopicsController do end it 'requires an email parameter' do - lambda { xhr :post, :invite, topic_id: @topic.id }.should raise_error(ActionController::ParameterMissing) + expect { xhr :post, :invite, topic_id: @topic.id }.to raise_error(ActionController::ParameterMissing) end describe 'without permission' do it "raises an exception when the user doesn't have permission to invite to the topic" do xhr :post, :invite, topic_id: @topic.id, user: 'jake@adventuretime.ooo' - response.should be_forbidden + expect(response).to be_forbidden end end @@ -846,15 +831,15 @@ describe TopicsController do it 'should work as expected' do xhr :post, :invite, topic_id: @topic.id, user: 'jake@adventuretime.ooo' - response.should be_success - ::JSON.parse(response.body).should == {'success' => 'OK'} - Invite.where(invited_by_id: admin.id).count.should == 1 + expect(response).to be_success + expect(::JSON.parse(response.body)).to eq({'success' => 'OK'}) + expect(Invite.where(invited_by_id: admin.id).count).to eq(1) end it 'should fail on shoddy email' do xhr :post, :invite, topic_id: @topic.id, user: 'i_am_not_an_email' - response.should_not be_success - ::JSON.parse(response.body).should == {'failed' => 'FAILED'} + expect(response).not_to be_success + expect(::JSON.parse(response.body)).to eq({'failed' => 'FAILED'}) end end @@ -866,15 +851,15 @@ describe TopicsController do describe 'autoclose' do it 'needs you to be logged in' do - -> { + expect { xhr :put, :autoclose, topic_id: 99, auto_close_time: '24', auto_close_based_on_last_post: false - }.should raise_error(Discourse::NotLoggedIn) + }.to raise_error(Discourse::NotLoggedIn) end it 'needs you to be an admin or mod' do - user = log_in + log_in xhr :put, :autoclose, topic_id: 99, auto_close_time: '24', auto_close_based_on_last_post: false - response.should be_forbidden + expect(response).to be_forbidden end describe 'when logged in' do @@ -887,8 +872,8 @@ describe TopicsController do Topic.any_instance.expects(:set_auto_close).with("24", @admin) xhr :put, :autoclose, topic_id: @topic.id, auto_close_time: '24', auto_close_based_on_last_post: true json = ::JSON.parse(response.body) - json.should have_key('auto_close_at') - json.should have_key('auto_close_hours') + expect(json).to have_key('auto_close_at') + expect(json).to have_key('auto_close_hours') end it "can remove a topic's auto close time" do @@ -904,7 +889,7 @@ describe TopicsController do it 'needs you to be a staff member' do log_in xhr :put, :make_banner, topic_id: 99 - response.should be_forbidden + expect(response).to be_forbidden end describe 'when logged in' do @@ -914,7 +899,7 @@ describe TopicsController do Topic.any_instance.expects(:make_banner!) xhr :put, :make_banner, topic_id: topic.id - response.should be_success + expect(response).to be_success end end @@ -926,7 +911,7 @@ describe TopicsController do it 'needs you to be a staff member' do log_in xhr :put, :remove_banner, topic_id: 99 - response.should be_forbidden + expect(response).to be_forbidden end describe 'when logged in' do @@ -936,7 +921,7 @@ describe TopicsController do Topic.any_instance.expects(:remove_banner!) xhr :put, :remove_banner, topic_id: topic.id - response.should be_success + expect(response).to be_success end end @@ -945,7 +930,7 @@ describe TopicsController do describe "bulk" do it 'needs you to be logged in' do - lambda { xhr :put, :bulk }.should raise_error(Discourse::NotLoggedIn) + expect { xhr :put, :bulk }.to raise_error(Discourse::NotLoggedIn) end describe "when logged in" do @@ -954,15 +939,15 @@ describe TopicsController do let(:topic_ids) { [1,2,3] } it "requires a list of topic_ids or filter" do - lambda { xhr :put, :bulk, operation: operation }.should raise_error(ActionController::ParameterMissing) + expect { xhr :put, :bulk, operation: operation }.to raise_error(ActionController::ParameterMissing) end it "requires an operation param" do - lambda { xhr :put, :bulk, topic_ids: topic_ids}.should raise_error(ActionController::ParameterMissing) + expect { xhr :put, :bulk, topic_ids: topic_ids}.to raise_error(ActionController::ParameterMissing) end it "requires a type field for the operation param" do - lambda { xhr :put, :bulk, topic_ids: topic_ids, operation: {}}.should raise_error(ActionController::ParameterMissing) + expect { xhr :put, :bulk, topic_ids: topic_ids, operation: {}}.to raise_error(ActionController::ParameterMissing) end it "delegates work to `TopicsBulkAction`" do @@ -974,10 +959,29 @@ describe TopicsController do end end + describe 'remove_bookmarks' do + it "should remove bookmarks properly from non first post" do + bookmark = PostActionType.types[:bookmark] + user = log_in + + post = create_post + post2 = create_post(topic_id: post.topic_id) + + PostAction.act(user, post2, bookmark) + + xhr :put, :bookmark, topic_id: post.topic_id + PostAction.where(user_id: user.id, post_action_type: bookmark).count.should == 2 + + xhr :put, :remove_bookmarks, topic_id: post.topic_id + PostAction.where(user_id: user.id, post_action_type: bookmark).count.should == 0 + + end + end + describe 'reset_new' do it 'needs you to be logged in' do - lambda { xhr :put, :reset_new }.should raise_error(Discourse::NotLoggedIn) + expect { xhr :put, :reset_new }.to raise_error(Discourse::NotLoggedIn) end let(:user) { log_in(:user) } @@ -989,7 +993,7 @@ describe TopicsController do xhr :put, :reset_new user.reload - user.user_stat.new_since.to_date.should_not == old_date.to_date + expect(user.user_stat.new_since.to_date).not_to eq(old_date.to_date) end diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb index 4591b2dae2..feef3a60a4 100644 --- a/spec/controllers/uploads_controller_spec.rb +++ b/spec/controllers/uploads_controller_spec.rb @@ -5,7 +5,7 @@ describe UploadsController do context '.create' do it 'requires you to be logged in' do - -> { xhr :post, :create }.should raise_error(Discourse::NotLoggedIn) + expect { xhr :post, :create }.to raise_error(Discourse::NotLoggedIn) end context 'logged in' do @@ -43,12 +43,12 @@ describe UploadsController do it 'is successful with an image' do xhr :post, :create, file: logo - response.status.should eq 200 + expect(response.status).to eq 200 end it 'is successful with an attachment' do xhr :post, :create, file: text_file - response.status.should eq 200 + expect(response.status).to eq 200 end it 'correctly sets retain_hours for admins' do @@ -56,7 +56,7 @@ describe UploadsController do xhr :post, :create, file: logo, retain_hours: 100 url = JSON.parse(response.body)["url"] id = url.split("/")[3].to_i - Upload.find(id).retain_hours.should == 100 + expect(Upload.find(id).retain_hours).to eq(100) end context 'with a big file' do @@ -65,7 +65,7 @@ describe UploadsController do it 'rejects the upload' do xhr :post, :create, file: text_file - response.status.should eq 422 + expect(response.status).to eq 422 end end @@ -78,7 +78,7 @@ describe UploadsController do it 'rejects the upload' do xhr :post, :create, file: text_file - response.status.should eq 422 + expect(response.status).to eq 422 end end @@ -89,12 +89,12 @@ describe UploadsController do it 'is successful with an image' do xhr :post, :create, file: logo - response.status.should eq 200 + expect(response.status).to eq 200 end it 'is successful with an attachment' do xhr :post, :create, file: text_file - response.status.should eq 200 + expect(response.status).to eq 200 end end @@ -105,12 +105,12 @@ describe UploadsController do it 'is successful' do xhr :post, :create, files: files - response.should be_success + expect(response).to be_success end it 'takes the first file' do xhr :post, :create, files: files - response.body.should match /logo-dev.png/ + expect(response.body).to match /logo-dev.png/ end end @@ -126,7 +126,7 @@ describe UploadsController do Discourse.stubs(:store).returns(store) Upload.expects(:find_by).never get :show, site: "default", id: 1, sha: "1234567890abcdef", extension: "pdf" - response.response_code.should == 404 + expect(response.response_code).to eq(404) end it "returns 404 when the upload doens't exist" do @@ -134,7 +134,7 @@ describe UploadsController do Upload.expects(:find_by).with(sha1: "1234567890abcdef").returns(nil) get :show, site: "default", id: 2, sha: "1234567890abcdef", extension: "pdf" - response.response_code.should == 404 + expect(response.response_code).to eq(404) end it 'uses send_file' do @@ -154,7 +154,7 @@ describe UploadsController do it "returns 404 when an anonymous user tries to download a file" do Upload.expects(:find_by).never get :show, site: "default", id: 2, sha: "1234567890abcdef", extension: "pdf" - response.response_code.should == 404 + expect(response.response_code).to eq(404) end end diff --git a/spec/controllers/user_actions_controller_spec.rb b/spec/controllers/user_actions_controller_spec.rb index b08ae17488..c119ad3bea 100644 --- a/spec/controllers/user_actions_controller_spec.rb +++ b/spec/controllers/user_actions_controller_spec.rb @@ -13,14 +13,14 @@ describe UserActionsController do xhr :get, :index, username: post.user.username - response.status.should == 200 + expect(response.status).to eq(200) parsed = JSON.parse(response.body) actions = parsed["user_actions"] - actions.length.should == 1 + expect(actions.length).to eq(1) action = actions[0] - action["acting_name"].should == post.user.name - action["email"].should == nil - action["post_number"].should == 1 + expect(action["acting_name"]).to eq(post.user.name) + expect(action["email"]).to eq(nil) + expect(action["post_number"]).to eq(1) end end end diff --git a/spec/controllers/user_badges_controller_spec.rb b/spec/controllers/user_badges_controller_spec.rb index e278ddcae9..867751b56c 100644 --- a/spec/controllers/user_badges_controller_spec.rb +++ b/spec/controllers/user_badges_controller_spec.rb @@ -11,10 +11,10 @@ describe UserBadgesController do UserBadge.create(badge: badge, user: user, post_id: p.id, granted_by_id: -1, granted_at: Time.now) xhr :get, :index, badge_id: badge.id - response.status.should == 200 + expect(response.status).to eq(200) parsed = JSON.parse(response.body) - parsed["topics"].should == nil - parsed["user_badges"][0]["post_id"].should == nil + expect(parsed["topics"]).to eq(nil) + expect(parsed["user_badges"][0]["post_id"]).to eq(nil) end end @@ -28,25 +28,25 @@ describe UserBadgesController do it 'returns user_badges for a user' do xhr :get, :username, username: user.username - response.status.should == 200 + expect(response.status).to eq(200) parsed = JSON.parse(response.body) - parsed["user_badges"].length.should == 1 + expect(parsed["user_badges"].length).to eq(1) end it 'returns user_badges for a badge' do xhr :get, :index, badge_id: badge.id - response.status.should == 200 + expect(response.status).to eq(200) parsed = JSON.parse(response.body) - parsed["user_badges"].length.should == 1 + expect(parsed["user_badges"].length).to eq(1) end it 'includes counts when passed the aggregate argument' do xhr :get, :username, username: user.username, grouped: true - response.status.should == 200 + expect(response.status).to eq(200) parsed = JSON.parse(response.body) - parsed["user_badges"].first.has_key?('count').should == true + expect(parsed["user_badges"].first.has_key?('count')).to eq(true) end end @@ -58,7 +58,7 @@ describe UserBadgesController do it 'does not allow regular users to grant badges' do log_in_user Fabricate(:user) xhr :post, :create, badge_id: badge.id, username: user.username - response.status.should == 403 + expect(response.status).to eq(403) end it 'grants badges from staff' do @@ -66,26 +66,26 @@ describe UserBadgesController do log_in_user admin StaffActionLogger.any_instance.expects(:log_badge_grant).once xhr :post, :create, badge_id: badge.id, username: user.username - response.status.should == 200 + expect(response.status).to eq(200) user_badge = UserBadge.find_by(user: user, badge: badge) - user_badge.should be_present - user_badge.granted_by.should eq(admin) + expect(user_badge).to be_present + expect(user_badge.granted_by).to eq(admin) end it 'does not grant badges from regular api calls' do Fabricate(:api_key, user: user) xhr :post, :create, badge_id: badge.id, username: user.username, api_key: user.api_key.key - response.status.should == 403 + expect(response.status).to eq(403) end it 'grants badges from master api calls' do api_key = Fabricate(:api_key) StaffActionLogger.any_instance.expects(:log_badge_grant).never xhr :post, :create, badge_id: badge.id, username: user.username, api_key: api_key.key, api_username: "system" - response.status.should == 200 + expect(response.status).to eq(200) user_badge = UserBadge.find_by(user: user, badge: badge) - user_badge.should be_present - user_badge.granted_by.should eq(Discourse.system_user) + expect(user_badge).to be_present + expect(user_badge.granted_by).to eq(Discourse.system_user) end end @@ -94,15 +94,15 @@ describe UserBadgesController do it 'checks that the user is authorized to revoke a badge' do xhr :delete, :destroy, id: user_badge.id - response.status.should == 403 + expect(response.status).to eq(403) end it 'revokes the badge' do log_in :admin StaffActionLogger.any_instance.expects(:log_badge_revoke).once xhr :delete, :destroy, id: user_badge.id - response.status.should == 200 - UserBadge.find_by(id: user_badge.id).should == nil + expect(response.status).to eq(200) + expect(UserBadge.find_by(id: user_badge.id)).to eq(nil) end end end diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 192e000cca..27c330c97a 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -7,28 +7,28 @@ describe UsersController do it 'returns success' do xhr :get, :show, username: user.username, format: :json - response.should be_success + expect(response).to be_success json = JSON.parse(response.body) - json["user"]["has_title_badges"].should == false + expect(json["user"]["has_title_badges"]).to eq(false) end it "returns not found when the username doesn't exist" do xhr :get, :show, username: 'madeuppity' - response.should_not be_success + expect(response).not_to be_success end it 'returns not found when the user is inactive' do inactive = Fabricate(:user, active: false) xhr :get, :show, username: inactive.username - response.should_not be_success + expect(response).not_to be_success end it "raises an error on invalid access" do Guardian.any_instance.expects(:can_see?).with(user).returns(false) xhr :get, :show, username: user.username - response.should be_forbidden + expect(response).to be_forbidden end context "fetching a user by external_id" do @@ -36,33 +36,33 @@ describe UsersController do it "returns fetch for a matching external_id" do xhr :get, :show, external_id: '997' - response.should be_success + expect(response).to be_success end it "returns not found when external_id doesn't match" do xhr :get, :show, external_id: '99' - response.should_not be_success + expect(response).not_to be_success end end end describe '.user_preferences_redirect' do it 'requires the user to be logged in' do - lambda { get :user_preferences_redirect }.should raise_error(Discourse::NotLoggedIn) + expect { get :user_preferences_redirect }.to raise_error(Discourse::NotLoggedIn) end it "redirects to their profile when logged in" do user = log_in get :user_preferences_redirect - response.should redirect_to("/users/#{user.username_lower}/preferences") + expect(response).to redirect_to("/users/#{user.username_lower}/preferences") end end describe '.authorize_email' do it 'errors out for invalid tokens' do get :authorize_email, token: 'asdfasdf' - response.should be_success - flash[:error].should be_present + expect(response).to be_success + expect(flash[:error]).to be_present end context 'valid token' do @@ -71,9 +71,9 @@ describe UsersController do email_token = user.email_tokens.create(email: user.email) get :authorize_email, token: email_token.token - response.should be_success - flash[:error].should be_blank - session[:current_user_id].should be_present + expect(response).to be_success + expect(flash[:error]).to be_blank + expect(session[:current_user_id]).to be_present end end end @@ -90,11 +90,11 @@ describe UsersController do end it 'return success' do - response.should be_success + expect(response).to be_success end it 'sets a flash error' do - flash[:error].should be_present + expect(flash[:error]).to be_present end end @@ -123,7 +123,7 @@ describe UsersController do it "raises an error if the honeypot is invalid" do UsersController.any_instance.stubs(:honeypot_or_challenge_fails?).returns(true) put :perform_account_activation, token: 'asdfasdf' - response.should_not be_success + expect(response).not_to be_success end end @@ -135,19 +135,19 @@ describe UsersController do end it 'returns success' do - response.should be_success + expect(response).to be_success end it "doesn't set an error" do - flash[:error].should be_blank + expect(flash[:error]).to be_blank end it 'logs in as the user' do - session[:current_user_id].should be_present + expect(session[:current_user_id]).to be_present end it "doesn't set @needs_approval" do - assigns[:needs_approval].should be_blank + expect(assigns[:needs_approval]).to be_blank end end @@ -159,19 +159,19 @@ describe UsersController do end it 'returns success' do - response.should be_success + expect(response).to be_success end it 'sets @needs_approval' do - assigns[:needs_approval].should be_present + expect(assigns[:needs_approval]).to be_present end it "doesn't set an error" do - flash[:error].should be_blank + expect(flash[:error]).to be_blank end it "doesn't log the user in" do - session[:current_user_id].should be_blank + expect(session[:current_user_id]).to be_blank end end @@ -182,30 +182,30 @@ describe UsersController do let(:new_email) { 'bubblegum@adventuretime.ooo' } it "requires you to be logged in" do - lambda { xhr :put, :change_email, username: 'asdf', email: new_email }.should raise_error(Discourse::NotLoggedIn) + expect { xhr :put, :change_email, username: 'asdf', email: new_email }.to raise_error(Discourse::NotLoggedIn) end context 'when logged in' do let!(:user) { log_in } it 'raises an error without an email parameter' do - lambda { xhr :put, :change_email, username: user.username }.should raise_error(ActionController::ParameterMissing) + expect { xhr :put, :change_email, username: user.username }.to raise_error(ActionController::ParameterMissing) end it "raises an error if you can't edit the user's email" do Guardian.any_instance.expects(:can_edit_email?).with(user).returns(false) xhr :put, :change_email, username: user.username, email: new_email - response.should be_forbidden + expect(response).to be_forbidden end context 'when the new email address is taken' do let!(:other_user) { Fabricate(:coding_horror) } it 'raises an error' do - lambda { xhr :put, :change_email, username: user.username, email: other_user.email }.should raise_error(Discourse::InvalidParameters) + expect { xhr :put, :change_email, username: user.username, email: other_user.email }.to raise_error(Discourse::InvalidParameters) end it 'raises an error if there is whitespace too' do - lambda { xhr :put, :change_email, username: user.username, email: other_user.email + ' ' }.should raise_error(Discourse::InvalidParameters) + expect { xhr :put, :change_email, username: user.username, email: other_user.email + ' ' }.to raise_error(Discourse::InvalidParameters) end end @@ -213,14 +213,14 @@ describe UsersController do let!(:other_user) { Fabricate(:user, email: 'case.insensitive@gmail.com')} it 'raises an error' do - lambda { xhr :put, :change_email, username: user.username, email: other_user.email.upcase }.should raise_error(Discourse::InvalidParameters) + expect { xhr :put, :change_email, username: user.username, email: other_user.email.upcase }.to raise_error(Discourse::InvalidParameters) end end context 'success' do it 'has an email token' do - lambda { xhr :put, :change_email, username: user.username, email: new_email }.should change(EmailToken, :count) + expect { xhr :put, :change_email, username: user.username, email: new_email }.to change(EmailToken, :count) end it 'enqueues an email authorization' do @@ -239,7 +239,7 @@ describe UsersController do it "returns success" do SiteSetting.login_required = true get :password_reset, token: 'asdfasdf' - response.should be_success + expect(response).to be_success end end @@ -249,10 +249,10 @@ describe UsersController do end it 'disallows login' do - flash[:error].should be_present - session[:current_user_id].should be_blank - assigns[:invalid_token].should == nil - response.should be_success + expect(flash[:error]).to be_present + expect(session[:current_user_id]).to be_blank + expect(assigns[:invalid_token]).to eq(nil) + expect(response).to be_success end end @@ -262,10 +262,10 @@ describe UsersController do end it 'disallows login' do - flash[:error].should be_present - session[:current_user_id].should be_blank - assigns[:invalid_token].should == true - response.should be_success + expect(flash[:error]).to be_present + expect(session[:current_user_id]).to be_blank + expect(assigns[:invalid_token]).to eq(true) + expect(response).to be_success end end @@ -276,8 +276,8 @@ describe UsersController do get :password_reset, token: token put :password_reset, token: token, password: 'newpassword' - response.should be_success - flash[:error].should be_blank + expect(response).to be_success + expect(flash[:error]).to be_blank end end @@ -289,27 +289,27 @@ describe UsersController do it "fails when the password is blank" do put :password_reset, token: token, password: '' - assigns(:user).errors.should be_present - session[:current_user_id].should be_blank + expect(assigns(:user).errors).to be_present + expect(session[:current_user_id]).to be_blank end it "fails when the password is too long" do put :password_reset, token: token, password: ('x' * (User.max_password_length + 1)) - assigns(:user).errors.should be_present - session[:current_user_id].should be_blank + expect(assigns(:user).errors).to be_present + expect(session[:current_user_id]).to be_blank end it "logs in the user" do put :password_reset, token: token, password: 'newpassword' - assigns(:user).errors.should be_blank - session[:current_user_id].should be_present + expect(assigns(:user).errors).to be_blank + expect(session[:current_user_id]).to be_present end it "doesn't log in the user when not approved" do SiteSetting.expects(:must_approve_users?).returns(true) put :password_reset, token: token, password: 'newpassword' - assigns(:user).errors.should be_blank - session[:current_user_id].should be_blank + expect(assigns(:user).errors).to be_blank + expect(session[:current_user_id]).to be_blank end end end @@ -345,8 +345,8 @@ describe UsersController do SiteSetting.stubs(:allow_new_registrations).returns(false) post_user json = JSON.parse(response.body) - json['success'].should == false - json['message'].should be_present + expect(json['success']).to eq(false) + expect(json['message']).to be_present end it 'creates a user correctly' do @@ -358,7 +358,7 @@ describe UsersController do expect(JSON.parse(response.body)['active']).to be_falsey # should save user_created_message in session - session["user_created_message"].should be_present + expect(session["user_created_message"]).to be_present end context "and 'must approve users' site setting is enabled" do @@ -394,7 +394,7 @@ describe UsersController do post_user # should save user_created_message in session - session["user_created_message"].should be_present + expect(session["user_created_message"]).to be_present end it "shows the 'active' message" do @@ -408,7 +408,7 @@ describe UsersController do it "should be logged in" do User.any_instance.expects(:enqueue_welcome_message) post_user - session[:current_user_id].should be_present + expect(session[:current_user_id]).to be_present end it 'indicates the user is active in the response' do @@ -421,8 +421,8 @@ describe UsersController do SiteSetting.stubs(:allow_new_registrations).returns(false) post_user json = JSON.parse(response.body) - json['success'].should == false - json['message'].should be_present + expect(json['success']).to eq(false) + expect(json['message']).to be_present end context 'authentication records for' do @@ -448,16 +448,16 @@ describe UsersController do before { post_user } it 'should succeed' do - should respond_with(:success) + is_expected.to respond_with(:success) end it 'has the proper JSON' do json = JSON::parse(response.body) - json["success"].should == true + expect(json["success"]).to eq(true) end it 'should not result in an active account' do - User.find_by(username: @user.username).active.should == false + expect(User.find_by(username: @user.username).active).to eq(false) end end @@ -476,10 +476,10 @@ describe UsersController do it 'should say it was successful' do xhr :post, :create, create_params json = JSON::parse(response.body) - json["success"].should == true + expect(json["success"]).to eq(true) # should not change the session - session["user_created_message"].should be_blank + expect(session["user_created_message"]).to be_blank end end @@ -520,10 +520,10 @@ describe UsersController do it 'should report failed' do xhr :post, :create, create_params json = JSON::parse(response.body) - json["success"].should_not == true + expect(json["success"]).not_to eq(true) # should not change the session - session["user_created_message"].should be_blank + expect(session["user_created_message"]).to be_blank end end @@ -580,60 +580,118 @@ describe UsersController do it "should succeed without the optional field" do xhr :post, :create, create_params - response.should be_success + expect(response).to be_success inserted = User.where(email: @user.email).first - inserted.should be_present - inserted.custom_fields.should be_present - inserted.custom_fields["user_field_#{user_field.id}"].should == 'value1' - inserted.custom_fields["user_field_#{another_field.id}"].should == 'value2' - inserted.custom_fields["user_field_#{optional_field.id}"].should be_blank + expect(inserted).to be_present + expect(inserted.custom_fields).to be_present + expect(inserted.custom_fields["user_field_#{user_field.id}"]).to eq('value1') + expect(inserted.custom_fields["user_field_#{another_field.id}"]).to eq('value2') + expect(inserted.custom_fields["user_field_#{optional_field.id}"]).to be_blank end it "should succeed with the optional field" do create_params[:user_fields][optional_field.id.to_s] = 'value3' xhr :post, :create, create_params.merge(create_params) - response.should be_success + expect(response).to be_success inserted = User.where(email: @user.email).first - inserted.should be_present - inserted.custom_fields.should be_present - inserted.custom_fields["user_field_#{user_field.id}"].should == 'value1' - inserted.custom_fields["user_field_#{another_field.id}"].should == 'value2' - inserted.custom_fields["user_field_#{optional_field.id}"].should == 'value3' + expect(inserted).to be_present + expect(inserted.custom_fields).to be_present + expect(inserted.custom_fields["user_field_#{user_field.id}"]).to eq('value1') + expect(inserted.custom_fields["user_field_#{another_field.id}"]).to eq('value2') + expect(inserted.custom_fields["user_field_#{optional_field.id}"]).to eq('value3') end end end + context "with only optional custom fields" do + let!(:user_field) { Fabricate(:user_field, required: false) } + + context "without values for the fields" do + let(:create_params) { { + name: @user.name, + password: 'watwatwat', + username: @user.username, + email: @user.email, + } } + + it "should succeed" do + xhr :post, :create, create_params + expect(response).to be_success + inserted = User.where(email: @user.email).first + expect(inserted).to be_present + expect(inserted.custom_fields).not_to be_present + expect(inserted.custom_fields["user_field_#{user_field.id}"]).to be_blank + end + end + end + end context '.username' do it 'raises an error when not logged in' do - lambda { xhr :put, :username, username: 'somename' }.should raise_error(Discourse::NotLoggedIn) + expect { xhr :put, :username, username: 'somename' }.to raise_error(Discourse::NotLoggedIn) end context 'while logged in' do - let!(:user) { log_in } - let(:new_username) { "#{user.username}1234" } + let(:old_username) { "OrigUsrname" } + let(:new_username) { "#{old_username}1234" } + let(:user) { Fabricate(:user, username: old_username) } + + before do + user.username = old_username + log_in_user(user) + end it 'raises an error without a new_username param' do - lambda { xhr :put, :username, username: user.username }.should raise_error(ActionController::ParameterMissing) + expect { xhr :put, :username, username: user.username }.to raise_error(ActionController::ParameterMissing) + user.reload.username.should == old_username end it 'raises an error when you don\'t have permission to change the username' do Guardian.any_instance.expects(:can_edit_username?).with(user).returns(false) xhr :put, :username, username: user.username, new_username: new_username - response.should be_forbidden + expect(response).to be_forbidden + user.reload.username.should == old_username end + # Bad behavior, this should give a real JSON error, not an InvalidParameters it 'raises an error when change_username fails' do - User.any_instance.expects(:change_username).with(new_username).returns(false) - lambda { xhr :put, :username, username: user.username, new_username: new_username }.should raise_error(Discourse::InvalidParameters) + User.any_instance.expects(:save).returns(false) + expect { xhr :put, :username, username: user.username, new_username: new_username }.to raise_error(Discourse::InvalidParameters) + user.reload.username.should == old_username end - it 'should succeed when the change_username returns true' do - User.any_instance.expects(:change_username).with(new_username).returns(true) + it 'should succeed in normal circumstances' do xhr :put, :username, username: user.username, new_username: new_username response.should be_success + user.reload.username.should == new_username + end + + pending 'should fail if the user is old', 'ensure_can_edit_username! is not throwing' do + # Older than the change period and >1 post + user.created_at = Time.now - (SiteSetting.username_change_period + 1).days + user.stubs(:post_count).returns(200) + Guardian.new(user).can_edit_username?(user).should == false + + xhr :put, :username, username: user.username, new_username: new_username + + response.should be_forbidden + user.reload.username.should == old_username + end + + it 'should create a staff action log when a staff member changes the username' do + acting_user = Fabricate(:admin) + log_in_user(acting_user) + xhr :put, :username, username: user.username, new_username: new_username + response.should be_success + UserHistory.where(action: UserHistory.actions[:change_username], target_user_id: user.id, acting_user_id: acting_user.id).should be_present + user.reload.username.should == new_username + end + + it 'should return a JSON response with the updated username' do + xhr :put, :username, username: user.username, new_username: new_username + expect(::JSON.parse(response.body)['username']).to eq(new_username) end end @@ -641,36 +699,36 @@ describe UsersController do context '.check_username' do it 'raises an error without any parameters' do - lambda { xhr :get, :check_username }.should raise_error(ActionController::ParameterMissing) + expect { xhr :get, :check_username }.to raise_error(ActionController::ParameterMissing) end shared_examples 'when username is unavailable' do it 'should return success' do - response.should be_success + expect(response).to be_success end it 'should return available as false in the JSON' do - ::JSON.parse(response.body)['available'].should == false + expect(::JSON.parse(response.body)['available']).to eq(false) end it 'should return a suggested username' do - ::JSON.parse(response.body)['suggestion'].should be_present + expect(::JSON.parse(response.body)['suggestion']).to be_present end end shared_examples 'when username is available' do it 'should return success' do - response.should be_success + expect(response).to be_success end it 'should return available in the JSON' do - ::JSON.parse(response.body)['available'].should == true + expect(::JSON.parse(response.body)['available']).to eq(true) end end it 'returns nothing when given an email param but no username' do xhr :get, :check_username, email: 'dood@example.com' - response.should be_success + expect(response).to be_success end context 'username is available' do @@ -690,15 +748,15 @@ describe UsersController do shared_examples 'checking an invalid username' do it 'should return success' do - response.should be_success + expect(response).to be_success end it 'should not return an available key' do - ::JSON.parse(response.body)['available'].should == nil + expect(::JSON.parse(response.body)['available']).to eq(nil) end it 'should return an error message' do - ::JSON.parse(response.body)['errors'].should_not be_empty + expect(::JSON.parse(response.body)['errors']).not_to be_empty end end @@ -709,7 +767,7 @@ describe UsersController do include_examples 'checking an invalid username' it 'should return the invalid characters message' do - ::JSON.parse(response.body)['errors'].should include(I18n.t(:'user.username.characters')) + expect(::JSON.parse(response.body)['errors']).to include(I18n.t(:'user.username.characters')) end end @@ -720,7 +778,7 @@ describe UsersController do include_examples 'checking an invalid username' it 'should return the "too long" message' do - ::JSON.parse(response.body)['errors'].should include(I18n.t(:'user.username.long', max: User.username_length.end)) + expect(::JSON.parse(response.body)['errors']).to include(I18n.t(:'user.username.long', max: User.username_length.end)) end end @@ -782,7 +840,7 @@ describe UsersController do xhr :get, :invited, username: inviter.username, filter: 'billybob' invites = JSON.parse(response.body)['invites'] - invites.size.should == 1 + expect(invites.size).to eq(1) expect(invites.first).to include('email' => 'billybob@example.com') end @@ -804,7 +862,7 @@ describe UsersController do xhr :get, :invited, username: inviter.username, filter: 'billybob' invites = JSON.parse(response.body)['invites'] - invites.size.should == 1 + expect(invites.size).to eq(1) expect(invites.first).to include('email' => 'billybob@example.com') end @@ -830,7 +888,7 @@ describe UsersController do xhr :get, :invited, username: inviter.username invites = JSON.parse(response.body)['invites'] - invites.size.should == 1 + expect(invites.size).to eq(1) expect(invites.first).to include('email' => invite.email) end end @@ -851,7 +909,7 @@ describe UsersController do xhr :get, :invited, username: inviter.username invites = JSON.parse(response.body)['invites'] - invites.size.should == 1 + expect(invites.size).to eq(1) expect(invites.first).to include("email" => invite.email) end end @@ -885,7 +943,7 @@ describe UsersController do xhr :get, :invited, username: inviter.username invites = JSON.parse(response.body)['invites'] - invites.size.should == 1 + expect(invites.size).to eq(1) expect(invites.first).to include('email' => invite.email) end end @@ -928,8 +986,8 @@ describe UsersController do it "cannot be updated to blank" do put :update, username: user.username, name: 'Jim Tom', user_fields: { user_field.id.to_s => '' } - response.should_not be_success - user.user_fields[user_field.id.to_s].should_not == 'happy' + expect(response).not_to be_success + expect(user.user_fields[user_field.id.to_s]).not_to eq('happy') end end @@ -979,15 +1037,15 @@ describe UsersController do it "sets the user's card image to the badge" do log_in_user user xhr :put, :update_card_badge, user_badge_id: user_badge.id, username: user.username - user.user_profile.reload.card_image_badge_id.should be_blank + expect(user.user_profile.reload.card_image_badge_id).to be_blank badge.update_attributes image: "wat.com/wat.jpg" xhr :put, :update_card_badge, user_badge_id: user_badge.id, username: user.username - user.user_profile.reload.card_image_badge_id.should == badge.id + expect(user.user_profile.reload.card_image_badge_id).to eq(badge.id) # Can set to nothing xhr :put, :update_card_badge, username: user.username - user.user_profile.reload.card_image_badge_id.should be_blank + expect(user.user_profile.reload.card_image_badge_id).to be_blank end end @@ -999,16 +1057,16 @@ describe UsersController do it "sets the user's title to the badge name if it is titleable" do log_in_user user xhr :put, :badge_title, user_badge_id: user_badge.id, username: user.username - user.reload.title.should_not == badge.name + expect(user.reload.title).not_to eq(badge.name) badge.update_attributes allow_title: true xhr :put, :badge_title, user_badge_id: user_badge.id, username: user.username - user.reload.title.should == badge.name - user.user_profile.badge_granted_title.should == true + expect(user.reload.title).to eq(badge.name) + expect(user.user_profile.badge_granted_title).to eq(true) user.title = "testing" user.save user.user_profile.reload - user.user_profile.badge_granted_title.should == false + expect(user.user_profile.badge_granted_title).to eq(false) end end @@ -1025,23 +1083,23 @@ describe UsersController do it "searches when provided the term only" do xhr :post, :search_users, term: user.name.split(" ").last - response.should be_success + expect(response).to be_success json = JSON.parse(response.body) - json["users"].map { |u| u["username"] }.should include(user.username) + expect(json["users"].map { |u| u["username"] }).to include(user.username) end it "searches when provided the topic only" do xhr :post, :search_users, topic_id: topic.id - response.should be_success + expect(response).to be_success json = JSON.parse(response.body) - json["users"].map { |u| u["username"] }.should include(user.username) + expect(json["users"].map { |u| u["username"] }).to include(user.username) end it "searches when provided the term and topic" do xhr :post, :search_users, term: user.name.split(" ").last, topic_id: topic.id - response.should be_success + expect(response).to be_success json = JSON.parse(response.body) - json["users"].map { |u| u["username"] }.should include(user.username) + expect(json["users"].map { |u| u["username"] }).to include(user.username) end context "when `enable_names` is true" do @@ -1052,7 +1110,7 @@ describe UsersController do it "returns names" do xhr :post, :search_users, term: user.name json = JSON.parse(response.body) - json["users"].map { |u| u["name"] }.should include(user.name) + expect(json["users"].map { |u| u["name"] }).to include(user.name) end end @@ -1064,7 +1122,7 @@ describe UsersController do it "returns names" do xhr :post, :search_users, term: user.name json = JSON.parse(response.body) - json["users"].map { |u| u["name"] }.should_not include(user.name) + expect(json["users"].map { |u| u["name"] }).not_to include(user.name) end end @@ -1111,7 +1169,7 @@ describe UsersController do describe '.upload_user_image' do it 'raises an error when not logged in' do - lambda { xhr :put, :upload_user_image, username: 'asdf' }.should raise_error(Discourse::NotLoggedIn) + expect { xhr :put, :upload_user_image, username: 'asdf' }.to raise_error(Discourse::NotLoggedIn) end context 'while logged in' do @@ -1125,7 +1183,7 @@ describe UsersController do end it 'raises an error without a image_type param' do - lambda { xhr :put, :upload_user_image, username: user.username }.should raise_error(ActionController::ParameterMissing) + expect { xhr :put, :upload_user_image, username: user.username }.to raise_error(ActionController::ParameterMissing) end describe "with uploaded file" do @@ -1133,19 +1191,19 @@ describe UsersController do it 'raises an error when you don\'t have permission to upload an user image' do Guardian.any_instance.expects(:can_edit?).with(user).returns(false) xhr :post, :upload_user_image, username: user.username, image_type: "avatar" - response.should be_forbidden + expect(response).to be_forbidden end it 'rejects large images' do SiteSetting.stubs(:max_image_size_kb).returns(1) xhr :post, :upload_user_image, username: user.username, file: user_image, image_type: "avatar" - response.status.should eq 422 + expect(response.status).to eq 422 end it 'rejects unauthorized images' do SiteSetting.stubs(:authorized_extensions).returns(".txt") xhr :post, :upload_user_image, username: user.username, file: user_image, image_type: "avatar" - response.status.should eq 422 + expect(response.status).to eq 422 end it 'is successful for avatars' do @@ -1155,10 +1213,10 @@ describe UsersController do xhr :post, :upload_user_image, username: user.username, file: user_image, image_type: "avatar" # returns the url, width and height of the uploaded image json = JSON.parse(response.body) - json['url'].should == "/uploads/default/1/1234567890123456.png" - json['width'].should == 100 - json['height'].should == 200 - json['upload_id'].should == upload.id + expect(json['url']).to eq("/uploads/default/1/1234567890123456.png") + expect(json['width']).to eq(100) + expect(json['height']).to eq(200) + expect(json['upload_id']).to eq(upload.id) end it 'is successful for profile backgrounds' do @@ -1167,13 +1225,13 @@ describe UsersController do xhr :post, :upload_user_image, username: user.username, file: user_image, image_type: "profile_background" user.reload - user.user_profile.profile_background.should == "/uploads/default/1/1234567890123456.png" + expect(user.user_profile.profile_background).to eq("/uploads/default/1/1234567890123456.png") # returns the url, width and height of the uploaded image json = JSON.parse(response.body) - json['url'].should == "/uploads/default/1/1234567890123456.png" - json['width'].should == 100 - json['height'].should == 200 + expect(json['url']).to eq("/uploads/default/1/1234567890123456.png") + expect(json['width']).to eq(100) + expect(json['height']).to eq(200) end it 'is successful for card backgrounds' do @@ -1182,13 +1240,13 @@ describe UsersController do xhr :post, :upload_user_image, username: user.username, file: user_image, image_type: "card_background" user.reload - user.user_profile.card_background.should == "/uploads/default/1/1234567890123456.png" + expect(user.user_profile.card_background).to eq("/uploads/default/1/1234567890123456.png") # returns the url, width and height of the uploaded image json = JSON.parse(response.body) - json['url'].should == "/uploads/default/1/1234567890123456.png" - json['width'].should == 100 - json['height'].should == 200 + expect(json['url']).to eq("/uploads/default/1/1234567890123456.png") + expect(json['width']).to eq(100) + expect(json['height']).to eq(200) end end @@ -1205,25 +1263,25 @@ describe UsersController do it 'rejects large images' do SiteSetting.stubs(:max_image_size_kb).returns(1) xhr :post, :upload_user_image, username: user.username, file: user_image_url, image_type: "profile_background" - response.status.should eq 422 + expect(response.status).to eq 422 end it 'rejects unauthorized images' do SiteSetting.stubs(:authorized_extensions).returns(".txt") xhr :post, :upload_user_image, username: user.username, file: user_image_url, image_type: "profile_background" - response.status.should eq 422 + expect(response.status).to eq 422 end it 'is successful for avatars' do upload = Fabricate(:upload) Upload.expects(:create_for).returns(upload) # enqueues the user_image generator job - xhr :post, :upload_avatar, username: user.username, file: user_image_url, image_type: "avatar" + xhr :post, :upload_user_image, username: user.username, file: user_image_url, image_type: "avatar" json = JSON.parse(response.body) - json['url'].should == "/uploads/default/1/1234567890123456.png" - json['width'].should == 100 - json['height'].should == 200 - json['upload_id'].should == upload.id + expect(json['url']).to eq("/uploads/default/1/1234567890123456.png") + expect(json['width']).to eq(100) + expect(json['height']).to eq(200) + expect(json['upload_id']).to eq(upload.id) end it 'is successful for profile backgrounds' do @@ -1231,13 +1289,13 @@ describe UsersController do Upload.expects(:create_for).returns(upload) xhr :post, :upload_user_image, username: user.username, file: user_image_url, image_type: "profile_background" user.reload - user.user_profile.profile_background.should == "/uploads/default/1/1234567890123456.png" + expect(user.user_profile.profile_background).to eq("/uploads/default/1/1234567890123456.png") # returns the url, width and height of the uploaded image json = JSON.parse(response.body) - json['url'].should == "/uploads/default/1/1234567890123456.png" - json['width'].should == 100 - json['height'].should == 200 + expect(json['url']).to eq("/uploads/default/1/1234567890123456.png") + expect(json['width']).to eq(100) + expect(json['height']).to eq(200) end it 'is successful for card backgrounds' do @@ -1245,19 +1303,19 @@ describe UsersController do Upload.expects(:create_for).returns(upload) xhr :post, :upload_user_image, username: user.username, file: user_image_url, image_type: "card_background" user.reload - user.user_profile.card_background.should == "/uploads/default/1/1234567890123456.png" + expect(user.user_profile.card_background).to eq("/uploads/default/1/1234567890123456.png") # returns the url, width and height of the uploaded image json = JSON.parse(response.body) - json['url'].should == "/uploads/default/1/1234567890123456.png" - json['width'].should == 100 - json['height'].should == 200 + expect(json['url']).to eq("/uploads/default/1/1234567890123456.png") + expect(json['width']).to eq(100) + expect(json['height']).to eq(200) end end it "should handle malformed urls" do xhr :post, :upload_user_image, username: user.username, file: "foobar", image_type: "profile_background" - response.status.should eq 422 + expect(response.status).to eq 422 end end @@ -1269,7 +1327,7 @@ describe UsersController do describe '.pick_avatar' do it 'raises an error when not logged in' do - lambda { xhr :put, :pick_avatar, username: 'asdf', avatar_id: 1}.should raise_error(Discourse::NotLoggedIn) + expect { xhr :put, :pick_avatar, username: 'asdf', avatar_id: 1}.to raise_error(Discourse::NotLoggedIn) end context 'while logged in' do @@ -1279,19 +1337,26 @@ describe UsersController do it 'raises an error when you don\'t have permission to toggle the avatar' do another_user = Fabricate(:user) xhr :put, :pick_avatar, username: another_user.username, upload_id: 1 - response.should be_forbidden + expect(response).to be_forbidden end it 'it successful' do xhr :put, :pick_avatar, username: user.username, upload_id: 111 - user.reload.uploaded_avatar_id.should == 111 - response.should be_success + expect(user.reload.uploaded_avatar_id).to eq(111) + expect(response).to be_success xhr :put, :pick_avatar, username: user.username - user.reload.uploaded_avatar_id.should == nil - response.should be_success + expect(user.reload.uploaded_avatar_id).to eq(nil) + expect(response).to be_success end + it 'returns success' do + xhr :put, :pick_avatar, username: user.username, upload_id: 111 + expect(user.reload.uploaded_avatar_id).to eq(111) + expect(response).to be_success + json = ::JSON.parse(response.body) + expect(json['success']).to eq("OK") + end end end @@ -1299,7 +1364,7 @@ describe UsersController do describe '.destroy_user_image' do it 'raises an error when not logged in' do - lambda { xhr :delete, :destroy_user_image, type: 'profile_background', username: 'asdf' }.should raise_error(Discourse::NotLoggedIn) + expect { xhr :delete, :destroy_user_image, type: 'profile_background', username: 'asdf' }.to raise_error(Discourse::NotLoggedIn) end context 'while logged in' do @@ -1309,21 +1374,21 @@ describe UsersController do it 'raises an error when you don\'t have permission to clear the profile background' do Guardian.any_instance.expects(:can_edit?).with(user).returns(false) xhr :delete, :destroy_user_image, username: user.username, image_type: 'profile_background' - response.should be_forbidden + expect(response).to be_forbidden end it "requires the `image_type` param" do - -> { xhr :delete, :destroy_user_image, username: user.username }.should raise_error(ActionController::ParameterMissing) + expect { xhr :delete, :destroy_user_image, username: user.username }.to raise_error(ActionController::ParameterMissing) end it "only allows certain `image_types`" do - -> { xhr :delete, :destroy_user_image, username: user.username, image_type: 'wat' }.should raise_error(Discourse::InvalidParameters) + expect { xhr :delete, :destroy_user_image, username: user.username, image_type: 'wat' }.to raise_error(Discourse::InvalidParameters) end it 'can clear the profile background' do xhr :delete, :destroy_user_image, image_type: 'profile_background', username: user.username - user.reload.user_profile.profile_background.should == "" - response.should be_success + expect(user.reload.user_profile.profile_background).to eq("") + expect(response).to be_success end end @@ -1331,7 +1396,7 @@ describe UsersController do describe '.destroy' do it 'raises an error when not logged in' do - lambda { xhr :delete, :destroy, username: 'nobody' }.should raise_error(Discourse::NotLoggedIn) + expect { xhr :delete, :destroy, username: 'nobody' }.to raise_error(Discourse::NotLoggedIn) end context 'while logged in' do @@ -1341,20 +1406,20 @@ describe UsersController do Guardian.any_instance.stubs(:can_delete_user?).returns(false) UserDestroyer.any_instance.expects(:destroy).never xhr :delete, :destroy, username: user.username - response.should be_forbidden + expect(response).to be_forbidden end it "raises an error when you try to delete someone else's account" do UserDestroyer.any_instance.expects(:destroy).never xhr :delete, :destroy, username: Fabricate(:user).username - response.should be_forbidden + expect(response).to be_forbidden end it "deletes your account when you're allowed to" do Guardian.any_instance.stubs(:can_delete_user?).returns(true) UserDestroyer.any_instance.expects(:destroy).with(user, anything).returns(user) xhr :delete, :destroy, username: user.username - response.should be_success + expect(response).to be_success end end end @@ -1363,8 +1428,8 @@ describe UsersController do it "returns 404 if the user is not logged in" do get :my_redirect, path: "wat" - response.should_not be_success - response.should_not be_redirect + expect(response).not_to be_success + expect(response).not_to be_redirect end context "when the user is logged in" do @@ -1372,17 +1437,17 @@ describe UsersController do it "will not redirect to an invalid path" do get :my_redirect, path: "wat/..password.txt" - response.should_not be_redirect + expect(response).not_to be_redirect end it "will redirect to an valid path" do get :my_redirect, path: "preferences" - response.should be_redirect + expect(response).to be_redirect end it "permits forward slashes" do get :my_redirect, path: "activity/posts" - response.should be_redirect + expect(response).to be_redirect end end end @@ -1390,7 +1455,7 @@ describe UsersController do describe '.check_emails' do it 'raises an error when not logged in' do - lambda { xhr :put, :check_emails, username: 'zogstrip' }.should raise_error(Discourse::NotLoggedIn) + expect { xhr :put, :check_emails, username: 'zogstrip' }.to raise_error(Discourse::NotLoggedIn) end context 'while logged in' do @@ -1399,26 +1464,26 @@ describe UsersController do it "raises an error when you aren't allowed to check emails" do Guardian.any_instance.expects(:can_check_emails?).returns(false) xhr :put, :check_emails, username: Fabricate(:user).username - response.should be_forbidden + expect(response).to be_forbidden end it "returns both email and associated_accounts when you're allowed to see them" do Guardian.any_instance.expects(:can_check_emails?).returns(true) xhr :put, :check_emails, username: Fabricate(:user).username - response.should be_success + expect(response).to be_success json = JSON.parse(response.body) - json["email"].should be_present - json["associated_accounts"].should be_present + expect(json["email"]).to be_present + expect(json["associated_accounts"]).to be_present end it "works on inactive users" do inactive_user = Fabricate(:user, active: false) Guardian.any_instance.expects(:can_check_emails?).returns(true) xhr :put, :check_emails, username: inactive_user.username - response.should be_success + expect(response).to be_success json = JSON.parse(response.body) - json["email"].should be_present - json["associated_accounts"].should be_present + expect(json["email"]).to be_present + expect(json["associated_accounts"]).to be_present end end diff --git a/spec/fabricators/category_fabricator.rb b/spec/fabricators/category_fabricator.rb index dae1337745..1f237b5d68 100644 --- a/spec/fabricators/category_fabricator.rb +++ b/spec/fabricators/category_fabricator.rb @@ -7,3 +7,9 @@ Fabricator(:diff_category, from: :category) do name "Different Category" user end + +Fabricator(:happy_category, from: :category) do + name 'Happy Category' + slug 'happy' + user +end diff --git a/spec/fabricators/incoming_link_fabricator.rb b/spec/fabricators/incoming_link_fabricator.rb new file mode 100644 index 0000000000..82e5245ddb --- /dev/null +++ b/spec/fabricators/incoming_link_fabricator.rb @@ -0,0 +1,5 @@ +Fabricator(:incoming_link) do + user + post + ip_address { sequence(:ip_address) { |n| "123.#{(n*3)%255}.#{(n*2)%255}.#{n%255}" } } +end diff --git a/spec/fabricators/single_sign_on_record_fabricator.rb b/spec/fabricators/single_sign_on_record_fabricator.rb new file mode 100644 index 0000000000..fcd4102a17 --- /dev/null +++ b/spec/fabricators/single_sign_on_record_fabricator.rb @@ -0,0 +1,9 @@ +Fabricator(:single_sign_on_record) do + user + external_id { sequence(:external_id) { |i| "ext_#{i}" } } + external_username { sequence(:username) { |i| "bruce#{i}" } } + external_email { sequence(:email) { |i| "bruce#{i}@wayne.com" } } + last_payload { sequence(:last_payload) { |i| "nonce=#{i}1870a940bbcbb46f06880ed338d58a07&name=" } } +end + + diff --git a/spec/fabricators/user_action_fabricator.rb b/spec/fabricators/user_action_fabricator.rb index 0763828c03..201a78852a 100644 --- a/spec/fabricators/user_action_fabricator.rb +++ b/spec/fabricators/user_action_fabricator.rb @@ -1,4 +1,4 @@ Fabricator(:user_action) do user - action_type UserAction::STAR + action_type UserAction::BOOKMARK end diff --git a/spec/fixtures/emails/android_gmail.eml b/spec/fixtures/emails/android_gmail.eml new file mode 100644 index 0000000000..21c5dde234 --- /dev/null +++ b/spec/fixtures/emails/android_gmail.eml @@ -0,0 +1,177 @@ +Delivered-To: reply@discourse.org +Return-Path: +MIME-Version: 1.0 +In-Reply-To: +References: + +Date: Fri, 28 Nov 2014 12:53:21 -0800 +Subject: Re: [Discourse Meta] [Lounge] Testing default email replies +From: Walter White +To: Discourse Meta +Content-Type: multipart/alternative; boundary=089e0149cfa485c6630508f173df + +--089e0149cfa485c6630508f173df +Content-Type: text/plain; charset=UTF-8 + +### this is a reply from Android 5 gmail + +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over +the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown +fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. +The quick brown fox jumps over the lazy dog. + +This is **bold** in Markdown. + +This is a link to http://example.com +On Nov 28, 2014 12:36 PM, "Arpit Jalan" wrote: + +> techAPJ +> November 28 +> +> Test reply. +> +> First paragraph. +> +> Second paragraph. +> +> To respond, reply to this email or visit +> https://meta.discourse.org/t/testing-default-email-replies/22638/3 in +> your browser. +> ------------------------------ +> Previous Replies codinghorror +> +> November 28 +> +> We're testing the latest GitHub email processing library which we are +> integrating now. +> +> https://github.com/github/email_reply_parser +> +> Go ahead and reply to this topic and I'll reply from various email clients +> for testing. +> ------------------------------ +> +> To respond, reply to this email or visit +> https://meta.discourse.org/t/testing-default-email-replies/22638/3 in +> your browser. +> +> To unsubscribe from these emails, visit your user preferences +> . +> + +--089e0149cfa485c6630508f173df +Content-Type: text/html; charset=UTF-8 +Content-Transfer-Encoding: quoted-printable + +

### this is a reply from Android 5 gmail

+

The quick brown fox jumps over the lazy dog. The quick brown= + fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. = +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over= + the lazy dog. The quick brown fox jumps over the lazy dog.

+

This is **bold** in Markdown.

+

This is a link to http://exam= +ple.com

+
On Nov 28, 2014 12:36 PM, "Arpit Jalan"= +; <info@discourse.org> wrot= +e:
+ + + + + + + + + + + +
+ + + techAPJ
+ November 28 +
+

Test reply.

+ +

First paragraph.

+ +

Second paragraph.

+
+ + +
+

To respond, reply to this email or visit https:/= +/meta.discourse.org/t/testing-default-email-replies/22638/3 in your bro= +wser.

+
+
+

Previous Replies

+ + + + + + + + + + + +
+ + + codinghorror + November 28 +
+

We're testing the latest GitHub emai= +l processing library which we are integrating now.

+ +

https://github.com/github/email_reply_parser

+ +

Go ahead and reply to this topic and I&#= +39;ll reply from various email clients for testing.

+
+ + +
+ +
+

To respond, reply to this email or visit https://met= +a.discourse.org/t/testing-default-email-replies/22638/3 in your browser= +.

+
+
+

To unsubscribe from these emails, visit your user preferences.

+
+
+
+ +--089e0149cfa485c6630508f173df-- diff --git a/spec/fixtures/emails/auto_reply.eml b/spec/fixtures/emails/auto_reply.eml new file mode 100644 index 0000000000..7999c8d78b --- /dev/null +++ b/spec/fixtures/emails/auto_reply.eml @@ -0,0 +1,21 @@ +Return-Path: +Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400 +Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for ; Thu, 13 Jun 2013 17:03:50 -0400 +Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for ; Thu, 13 Jun 2013 14:03:48 -0700 +Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700 +Date: Thu, 13 Jun 2013 17:03:48 -0400 +From: Jake the Dog +To: reply+636ca428858779856c226bb145ef4fad@appmail.adventuretime.ooo +Message-ID: +Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux' +Mime-Version: 1.0 +Content-Type: text/plain; + charset=ISO-8859-1 +Content-Transfer-Encoding: 7bit +Auto-Submitted: auto-generated +X-Sieve: CMU Sieve 2.2 +X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu, + 13 Jun 2013 14:03:48 -0700 (PDT) +X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1 + +Test reply to Discourse email digest diff --git a/spec/fixtures/emails/gmail_web.eml b/spec/fixtures/emails/gmail_web.eml new file mode 100644 index 0000000000..8bb8383571 --- /dev/null +++ b/spec/fixtures/emails/gmail_web.eml @@ -0,0 +1,181 @@ +Delivered-To: reply@discourse.org +Return-Path: +MIME-Version: 1.0 +In-Reply-To: +References: + +Date: Fri, 28 Nov 2014 12:36:49 -0800 +Subject: Re: [Discourse Meta] [Lounge] Testing default email replies +From: Walter White +To: Discourse Meta +Content-Type: multipart/alternative; boundary=001a11c2e04e6544f30508f138ba + +--001a11c2e04e6544f30508f138ba +Content-Type: text/plain; charset=UTF-8 + +### This is a reply from standard GMail in Google Chrome. + +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over +the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown +fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over +the lazy dog. The quick brown fox jumps over the lazy dog. + +Here's some **bold** text in Markdown. + +Here's a link http://example.com + +On Fri, Nov 28, 2014 at 12:35 PM, Arpit Jalan wrote: + +> techAPJ +> November 28 +> +> Test reply. +> +> First paragraph. +> +> Second paragraph. +> +> To respond, reply to this email or visit +> https://meta.discourse.org/t/testing-default-email-replies/22638/3 in +> your browser. +> ------------------------------ +> Previous Replies codinghorror +> +> November 28 +> +> We're testing the latest GitHub email processing library which we are +> integrating now. +> +> https://github.com/github/email_reply_parser +> +> Go ahead and reply to this topic and I'll reply from various email clients +> for testing. +> ------------------------------ +> +> To respond, reply to this email or visit +> https://meta.discourse.org/t/testing-default-email-replies/22638/3 in +> your browser. +> +> To unsubscribe from these emails, visit your user preferences +> . +> + +--001a11c2e04e6544f30508f138ba +Content-Type: text/html; charset=UTF-8 +Content-Transfer-Encoding: quoted-printable + +
### This is a reply from standard GMail in Google Chr= +ome.

The quick brown fox jumps over the lazy dog. = +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over= + the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown= + fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. = +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over= + the lazy dog.=C2=A0

Here's some **bold** text= + in Markdown.

Here's a link http://example.com
<= +br>
On Fri, Nov 28, 2014 at 12:35 PM, Arpit Jalan= + <info@discourse.org> wrote:
+ + + + + + + + + + + +
+ + + techAPJ
+ November 28 +
+

Test reply.

+ +

First paragraph.

+ +

Second paragraph.

+
+ + +
+

To respond, reply to this email or visit https:/= +/meta.discourse.org/t/testing-default-email-replies/22638/3 in your bro= +wser.

+
+
+

Previous Replies

+ + + + + + + + + + + +
+ + + codinghorror + November 28 +
+

We're testing the latest GitHub emai= +l processing library which we are integrating now.

+ +

https://github.com/github/email_reply_parser

+ +

Go ahead and reply to this topic and I&#= +39;ll reply from various email clients for testing.

+
+ + +
+ +
+

To respond, reply to this email or visit https://met= +a.discourse.org/t/testing-default-email-replies/22638/3 in your browser= +.

+
+
+

To unsubscribe from these emails, visit your user preferences.

+
+
+

+ +--001a11c2e04e6544f30508f138ba-- diff --git a/spec/fixtures/emails/inline_reply.eml b/spec/fixtures/emails/inline_reply.eml new file mode 100644 index 0000000000..39625a225d --- /dev/null +++ b/spec/fixtures/emails/inline_reply.eml @@ -0,0 +1,60 @@ + +MIME-Version: 1.0 +In-Reply-To: +References: + <5434ced4ee0f9_663fb0b5f76070593b@discourse-app.mail> +Date: Mon, 1 Dec 2014 20:48:40 +0530 +Delivered-To: someone@googlemail.com +Subject: Re: [Discourse] [Meta] Testing reply via email +From: Walter White +To: Discourse +Content-Type: multipart/alternative; boundary=20cf30363f8522466905092920a6 + +--20cf30363f8522466905092920a6 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: quoted-printable + +On Wed, Oct 8, 2014 at 11:12 AM, techAPJ +wrote: + +> techAPJ +> November 28 +> +> Test reply. +> +> First paragraph. +> +> Second paragraph. +> +> To respond, reply to this email or visit +> https://meta.discourse.org/t/testing-default-email-replies/22638/3 in +> your browser. +> ------------------------------ +> Previous Replies codinghorror +> +> November 28 +> +> We're testing the latest GitHub email processing library which we are +> integrating now. +> +> https://github.com/github/email_reply_parser +> +> Go ahead and reply to this topic and I'll reply from various email clients +> for testing. +> ------------------------------ +> +> To respond, reply to this email or visit +> https://meta.discourse.org/t/testing-default-email-replies/22638/3 in +> your browser. +> +> To unsubscribe from these emails, visit your user preferences +> . +> + +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over +the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown +fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over +the lazy dog. The quick brown fox jumps over the lazy dog. + +--20cf30363f8522466905092920a6-- diff --git a/spec/fixtures/emails/ios_default.eml b/spec/fixtures/emails/ios_default.eml new file mode 100644 index 0000000000..8d4d58feb1 --- /dev/null +++ b/spec/fixtures/emails/ios_default.eml @@ -0,0 +1,136 @@ +Delivered-To: reply@discourse.org +Return-Path: +From: Walter White +Content-Type: multipart/alternative; + boundary=Apple-Mail-B41C7F8E-3639-49B0-A5D5-440E125A7105 +Content-Transfer-Encoding: 7bit +Mime-Version: 1.0 (1.0) +Subject: Re: [Discourse Meta] [Lounge] Testing default email replies +Date: Fri, 28 Nov 2014 12:41:41 -0800 +References: +In-Reply-To: +To: Discourse Meta +X-Mailer: iPhone Mail (12B436) + + +--Apple-Mail-B41C7F8E-3639-49B0-A5D5-440E125A7105 +Content-Type: text/plain; + charset=us-ascii +Content-Transfer-Encoding: quoted-printable + +### this is a reply from iOS default mail + +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over t= +he lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fo= +x jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The q= +uick brown fox jumps over the lazy dog. The quick brown fox jumps over the l= +azy dog.=20 + +Here's some **bold** markdown text. + +Here's a link http://example.com + + +> On Nov 28, 2014, at 12:35 PM, Arpit Jalan wrote: +>=20 +>=20 +> techAPJ +> November 28 +> Test reply. +>=20 +> First paragraph. +>=20 +> Second paragraph. +>=20 +> To respond, reply to this email or visit https://meta.discourse.org/t/test= +ing-default-email-replies/22638/3 in your browser. +>=20 +> Previous Replies +>=20 +> codinghorror +> November 28 +> We're testing the latest GitHub email processing library which we are inte= +grating now. +>=20 +> https://github.com/github/email_reply_parser +>=20 +> Go ahead and reply to this topic and I'll reply from various email clients= + for testing. +>=20 +> To respond, reply to this email or visit https://meta.discourse.org/t/test= +ing-default-email-replies/22638/3 in your browser. +>=20 +> To unsubscribe from these emails, visit your user preferences. + +--Apple-Mail-B41C7F8E-3639-49B0-A5D5-440E125A7105 +Content-Type: text/html; + charset=utf-8 +Content-Transfer-Encoding: 7bit + +
### this is a reply from iOS default mail

The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. 

Here's some **bold** markdown text.

Here's a link http://example.com


On Nov 28, 2014, at 12:35 PM, Arpit Jalan <info@discourse.org> wrote:

+ + + + + + + + + + + +
+ + + techAPJ
+ November 28 +
+

Test reply.

+ +

First paragraph.

+ +

Second paragraph.

+
+ + +
+

To respond, reply to this email or visit https://meta.discourse.org/t/testing-default-email-replies/22638/3 in your browser.

+
+
+

Previous Replies

+ + + + + + + + + + + +
+ + + codinghorror
+ November 28 +
+

We're testing the latest GitHub email processing library which we are integrating now.

+ +

https://github.com/github/email_reply_parser

+ +

Go ahead and reply to this topic and I'll reply from various email clients for testing.

+
+ + +
+ +
+

To respond, reply to this email or visit https://meta.discourse.org/t/testing-default-email-replies/22638/3 in your browser.

+
+
+

To unsubscribe from these emails, visit your user preferences.

+
+
+
+--Apple-Mail-B41C7F8E-3639-49B0-A5D5-440E125A7105-- diff --git a/spec/fixtures/emails/iphone_signature.eml b/spec/fixtures/emails/iphone_signature.eml new file mode 100644 index 0000000000..d314ad1f1e --- /dev/null +++ b/spec/fixtures/emails/iphone_signature.eml @@ -0,0 +1,29 @@ +Delivered-To: test@mail.com +Return-Path: +From: Walter White +Content-Type: multipart/alternative; + boundary=Apple-Mail-8E182EEF-9DBC-41DE-A593-DF2E5EBD3975 +Content-Transfer-Encoding: 7bit +Mime-Version: 1.0 (1.0) +Subject: Re: Signature in email replies! +Date: Thu, 23 Oct 2014 14:43:49 +0530 +References: <1234@mail.gmail.com> +In-Reply-To: <1234@mail.gmail.com> +To: Arpit Jalan +X-Mailer: iPhone Mail (12A405) + + +--Apple-Mail-8E182EEF-9DBC-41DE-A593-DF2E5EBD3975 +Content-Type: text/plain; + charset=us-ascii +Content-Transfer-Encoding: 7bit + +This post should not include signature. + +Sent from my iPhone + +> On 23-Oct-2014, at 9:45 am, Arpit Jalan wrote: +> +> Signature in email replies! + +--Apple-Mail-8E182EEF-9DBC-41DE-A593-DF2E5EBD3975 diff --git a/spec/fixtures/emails/newlines.eml b/spec/fixtures/emails/newlines.eml new file mode 100644 index 0000000000..cf03b9d18b --- /dev/null +++ b/spec/fixtures/emails/newlines.eml @@ -0,0 +1,84 @@ +In-Reply-To: +Date: Wed, 8 Oct 2014 10:36:19 +0530 +Delivered-To: walter.white@googlemail.com +Subject: Re: [Discourse] Welcome to Discourse +From: Walter White +To: Discourse +Content-Type: multipart/alternative; boundary=bcaec554078cc3d0c10504e24661 + +--bcaec554078cc3d0c10504e24661 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: quoted-printable + +This is my reply. +It is my best reply. +It will also be my *only* reply. + +On Wed, Oct 8, 2014 at 10:33 AM, ajalan +wrote: + +> ajalan +> +> October 8 +> +> Awesome! Thank You! [image: +1] +> +> To respond, reply to this email or visit +> http://discourse.techapj.com/t/welcome-to-discourse/8/2 +> +> in your browser. +> ------------------------------ +> Previous Replies system +> +> October 8 +> +> The first paragraph of this pinned topic will be visible as a welcome +> message to all new visitors on your homepage. It's important! +> +> *Edit this* into a brief description of your community: +> +> - Who is it for? +> - What can they find here? +> - Why should they come here? +> - Where can they read more (links, resources, etc)? +> +> You may want to close this topic via the wrench icon at the upper right, +> so that replies don't pile up on an announcement. +> ------------------------------ +> +> To respond, reply to this email or visit +> http://discourse.techapj.com/t/welcome-to-discourse/8/2 +> +> in your browser. +> +> To unsubscribe from these emails, visit your user preferences +> +> . +> + +--bcaec554078cc3d0c10504e24661 diff --git a/spec/fixtures/emails/previous_replies.eml b/spec/fixtures/emails/previous_replies.eml new file mode 100644 index 0000000000..3fd74482c0 --- /dev/null +++ b/spec/fixtures/emails/previous_replies.eml @@ -0,0 +1,180 @@ +Delivered-To: reply@discourse.org +MIME-Version: 1.0 +In-Reply-To: +References: + +Date: Fri, 28 Nov 2014 12:55:32 -0800 +Subject: Re: [Discourse Meta] [Lounge] Testing default email replies +From: Walter White +To: Discourse Meta +Content-Type: multipart/alternative; boundary=001a1137c9285318bb0508f17bc5 + +--001a1137c9285318bb0508f17bc5 +Content-Type: text/plain; charset=UTF-8 + +### this is a reply from iOS Gmail app + +The quick brown fox jumps over the lazy dog. The quick brown fox jumps over +the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown +fox jumps over the lazy dog. The quick brown fox jumps over the lazy +dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps +over the lazy dog. + +This is **bold** text in Markdown. + +This is a link to http://example.com + +On Friday, November 28, 2014, Arpit Jalan wrote: + +> techAPJ +> November 28 +> +> Test reply. +> +> First paragraph. +> +> Second paragraph. +> +> To respond, reply to this email or visit +> https://meta.discourse.org/t/testing-default-email-replies/22638/3 in +> your browser. +> ------------------------------ +> Previous Replies codinghorror +> +> November 28 +> +> We're testing the latest GitHub email processing library which we are +> integrating now. +> +> https://github.com/github/email_reply_parser +> +> Go ahead and reply to this topic and I'll reply from various email clients +> for testing. +> ------------------------------ +> +> To respond, reply to this email or visit +> https://meta.discourse.org/t/testing-default-email-replies/22638/3 in +> your browser. +> +> To unsubscribe from these emails, visit your user preferences +> . +> + +--001a1137c9285318bb0508f17bc5 +Content-Type: text/html; charset=UTF-8 +Content-Transfer-Encoding: quoted-printable + +### this is a reply from iOS Gmail app

The quick brown f= +ox jumps over the lazy dog.=C2=A0The quick brown fox jumps over the lazy dog.=C2=A0The quic= +k brown fox jumps over the lazy dog.=C2=A0The quick brown fox jumps over th= +e lazy dog.=C2=A0The quick brown fox jumps over the lazy dog.=C2=A0The quic= +k brown fox jumps over the lazy dog.=C2=A0The quick brown fox jumps over th= +e lazy dog.=C2=A0

This is **bold** text in Markdown.

This is a link to http://example.com

On Friday, November 28, 2014, Arpit Jalan <
info@discourse.org> wrote:
+ + + + + + + + + + + +
+ + + techAPJ
+ November 28 +
+

Test reply.

+ +

First paragraph.

+ +

Second paragraph.

+
+ + +
+

To respond, reply to this email or visit https:/= +/meta.discourse.org/t/testing-default-email-replies/22638/3 in your bro= +wser.

+
+
+

Previous Replies

+ + + + + + + + + + + +
+ + + codinghorror + November 28 +
+

We're testing the latest GitHub emai= +l processing library which we are integrating now.

+ +

https://github.com/github/email_reply_parser

+ +

Go ahead and reply to this topic and I&#= +39;ll reply from various email clients for testing.

+
+ + +
+ +
+

To respond, reply to this email or visit https://met= +a.discourse.org/t/testing-default-email-replies/22638/3 in your browser= +.

+
+
+

To unsubscribe from these emails, visit your user preferences.

+
+
+
+ +--001a1137c9285318bb0508f17bc5-- diff --git a/spec/fixtures/emails/windows_8_metro.eml b/spec/fixtures/emails/windows_8_metro.eml new file mode 100644 index 0000000000..67d204af56 --- /dev/null +++ b/spec/fixtures/emails/windows_8_metro.eml @@ -0,0 +1,173 @@ +Delivered-To: reply@discourse.org +Return-Path: +MIME-Version: 1.0 +From: +To: + =?utf-8?Q?Discourse_Meta?= + +Subject: + =?utf-8?Q?Re:_[Discourse_Meta]_[Lounge]_Testing_default_email_replies?= +Importance: Normal +Date: Fri, 28 Nov 2014 21:29:10 +0000 +In-Reply-To: +References: + , +Content-Type: multipart/alternative; + boundary="_866E2678-BB4F-4DD8-BE18-81B04AD8D1BC_" + +--_866E2678-BB4F-4DD8-BE18-81B04AD8D1BC_ +Content-Transfer-Encoding: base64 +Content-Type: text/plain; charset="utf-8" + +IyMjIHJlcGx5IGZyb20gZGVmYXVsdCBtYWlsIGNsaWVudCBpbiBXaW5kb3dzIDguMSBNZXRybw0K +DQoNClRoZSBxdWljayBicm93biBmb3gganVtcHMgb3ZlciB0aGUgbGF6eSBkb2cuIFRoZSBxdWlj +ayBicm93biBmb3gganVtcHMgb3ZlciB0aGUgbGF6eSBkb2cuIFRoZSBxdWljayBicm93biBmb3gg +anVtcHMgb3ZlciB0aGUgbGF6eSBkb2cuIFRoZSBxdWljayBicm93biBmb3gganVtcHMgb3ZlciB0 +aGUgbGF6eSBkb2cuIFRoZSBxdWljayBicm93biBmb3gganVtcHMgb3ZlciB0aGUgbGF6eSBkb2cu +IFRoZSBxdWljayBicm93biBmb3gganVtcHMgb3ZlciB0aGUgbGF6eSBkb2cuIFRoZSBxdWljayBi +cm93biBmb3gganVtcHMgb3ZlciB0aGUgbGF6eSBkb2cuIFRoZSBxdWljayBicm93biBmb3gganVt +cHMgb3ZlciB0aGUgbGF6eSBkb2cuIFRoZSBxdWljayBicm93biBmb3gganVtcHMgb3ZlciB0aGUg +bGF6eSBkb2cuDQoNCg0KVGhpcyBpcyBhICoqYm9sZCoqIHdvcmQgaW4gTWFya2Rvd24NCg0KDQpU +aGlzIGlzIGEgbGluayBodHRwOi8vZXhhbXBsZS5jb20NCiANCg0KDQoNCg0KDQpGcm9tOiBBcnBp +dCBKYWxhbg0KU2VudDog4oCORnJpZGF54oCOLCDigI5Ob3ZlbWJlcuKAjiDigI4yOOKAjiwg4oCO +MjAxNCDigI4xMuKAjjrigI4zNeKAjiDigI5QTQ0KVG86IGplZmYgYXR3b29kDQoNCg0KDQoNCg0K +DQogdGVjaEFQSg0KTm92ZW1iZXIgMjggDQoNClRlc3QgcmVwbHkuDQoNCkZpcnN0IHBhcmFncmFw +aC4NCg0KU2Vjb25kIHBhcmFncmFwaC4NCg0KDQoNClRvIHJlc3BvbmQsIHJlcGx5IHRvIHRoaXMg +ZW1haWwgb3IgdmlzaXQgaHR0cHM6Ly9tZXRhLmRpc2NvdXJzZS5vcmcvdC90ZXN0aW5nLWRlZmF1 +bHQtZW1haWwtcmVwbGllcy8yMjYzOC8zIGluIHlvdXIgYnJvd3Nlci4NCg0KDQoNClByZXZpb3Vz +IFJlcGxpZXMNCg0KIGNvZGluZ2hvcnJvcg0KTm92ZW1iZXIgMjggDQoNCldlJ3JlIHRlc3Rpbmcg +dGhlIGxhdGVzdCBHaXRIdWIgZW1haWwgcHJvY2Vzc2luZyBsaWJyYXJ5IHdoaWNoIHdlIGFyZSBp +bnRlZ3JhdGluZyBub3cuDQoNCmh0dHBzOi8vZ2l0aHViLmNvbS9naXRodWIvZW1haWxfcmVwbHlf +cGFyc2VyDQoNCkdvIGFoZWFkIGFuZCByZXBseSB0byB0aGlzIHRvcGljIGFuZCBJJ2xsIHJlcGx5 +IGZyb20gdmFyaW91cyBlbWFpbCBjbGllbnRzIGZvciB0ZXN0aW5nLg0KDQoNCg0KDQoNClRvIHJl +c3BvbmQsIHJlcGx5IHRvIHRoaXMgZW1haWwgb3IgdmlzaXQgaHR0cHM6Ly9tZXRhLmRpc2NvdXJz +ZS5vcmcvdC90ZXN0aW5nLWRlZmF1bHQtZW1haWwtcmVwbGllcy8yMjYzOC8zIGluIHlvdXIgYnJv +d3Nlci4NCg0KDQpUbyB1bnN1YnNjcmliZSBmcm9tIHRoZXNlIGVtYWlscywgdmlzaXQgeW91ciB1 +c2VyIHByZWZlcmVuY2VzLg== + +--_866E2678-BB4F-4DD8-BE18-81B04AD8D1BC_ +Content-Transfer-Encoding: base64 +Content-Type: text/html; charset="utf-8" + +CjxodG1sPgo8aGVhZD4KPG1ldGEgbmFtZT0iZ2VuZXJhdG9yIiBjb250ZW50PSJXaW5kb3dzIE1h +aWwgMTcuNS45NjAwLjIwNjA1Ij4KPHN0eWxlIGRhdGEtZXh0ZXJuYWxzdHlsZT0idHJ1ZSI+PCEt +LQpwLk1zb0xpc3RQYXJhZ3JhcGgsIGxpLk1zb0xpc3RQYXJhZ3JhcGgsIGRpdi5Nc29MaXN0UGFy +YWdyYXBoIHsKbWFyZ2luLXRvcDowaW47Cm1hcmdpbi1yaWdodDowaW47Cm1hcmdpbi1ib3R0b206 +MGluOwptYXJnaW4tbGVmdDouNWluOwptYXJnaW4tYm90dG9tOi4wMDAxcHQ7Cn0KcC5Nc29Ob3Jt +YWwsIGxpLk1zb05vcm1hbCwgZGl2Lk1zb05vcm1hbCB7Cm1hcmdpbjowaW47Cm1hcmdpbi1ib3R0 +b206LjAwMDFwdDsKfQpwLk1zb0xpc3RQYXJhZ3JhcGhDeFNwRmlyc3QsIGxpLk1zb0xpc3RQYXJh +Z3JhcGhDeFNwRmlyc3QsIGRpdi5Nc29MaXN0UGFyYWdyYXBoQ3hTcEZpcnN0LCAKcC5Nc29MaXN0 +UGFyYWdyYXBoQ3hTcE1pZGRsZSwgbGkuTXNvTGlzdFBhcmFncmFwaEN4U3BNaWRkbGUsIGRpdi5N +c29MaXN0UGFyYWdyYXBoQ3hTcE1pZGRsZSwgCnAuTXNvTGlzdFBhcmFncmFwaEN4U3BMYXN0LCBs +aS5Nc29MaXN0UGFyYWdyYXBoQ3hTcExhc3QsIGRpdi5Nc29MaXN0UGFyYWdyYXBoQ3hTcExhc3Qg +ewptYXJnaW4tdG9wOjBpbjsKbWFyZ2luLXJpZ2h0OjBpbjsKbWFyZ2luLWJvdHRvbTowaW47Cm1h +cmdpbi1sZWZ0Oi41aW47Cm1hcmdpbi1ib3R0b206LjAwMDFwdDsKbGluZS1oZWlnaHQ6MTE1JTsK +fQotLT48L3N0eWxlPjwvaGVhZD4KPGJvZHkgZGlyPSJsdHIiPgo8ZGl2IGRhdGEtZXh0ZXJuYWxz +dHlsZT0iZmFsc2UiIGRpcj0ibHRyIiBzdHlsZT0iZm9udC1mYW1pbHk6ICdDYWxpYnJpJywgJ1Nl +Z29lIFVJJywgJ01laXJ5bycsICdNaWNyb3NvZnQgWWFIZWkgVUknLCAnTWljcm9zb2Z0IEpoZW5n +SGVpIFVJJywgJ01hbGd1biBHb3RoaWMnLCAnc2Fucy1zZXJpZic7Zm9udC1zaXplOjEycHQ7Ij48 +ZGl2IHN0eWxlPSJmb250LXNpemU6IDE0cHQ7Ij4jIyMgcmVwbHkgZnJvbSBkZWZhdWx0IG1haWwg +Y2xpZW50IGluIFdpbmRvd3MgOC4xIE1ldHJvPC9kaXY+PGRpdiBzdHlsZT0iZm9udC1zaXplOiAx +NHB0OyI+PGJyPjwvZGl2PjxkaXYgc3R5bGU9ImZvbnQtc2l6ZTogMTRwdDsiPlRoZSBxdWljayBi +cm93biBmb3gganVtcHMgb3ZlciB0aGUgbGF6eSBkb2cuIFRoZSBxdWljayBicm93biBmb3gganVt +cHMgb3ZlciB0aGUgbGF6eSBkb2cuIFRoZSBxdWljayBicm93biBmb3gganVtcHMgb3ZlciB0aGUg +bGF6eSBkb2cuIFRoZSBxdWljayBicm93biBmb3gganVtcHMgb3ZlciB0aGUgbGF6eSBkb2cuIFRo +ZSBxdWljayBicm93biBmb3gganVtcHMgb3ZlciB0aGUgbGF6eSBkb2cuIFRoZSBxdWljayBicm93 +biBmb3gganVtcHMgb3ZlciB0aGUgbGF6eSBkb2cuIFRoZSBxdWljayBicm93biBmb3gganVtcHMg +b3ZlciB0aGUgbGF6eSBkb2cuIFRoZSBxdWljayBicm93biBmb3gganVtcHMgb3ZlciB0aGUgbGF6 +eSBkb2cuIFRoZSBxdWljayBicm93biBmb3gganVtcHMgb3ZlciB0aGUgbGF6eSBkb2cuPC9kaXY+ +PGRpdiBzdHlsZT0iZm9udC1zaXplOiAxNHB0OyI+PGJyPjwvZGl2PjxkaXYgc3R5bGU9ImZvbnQt +c2l6ZTogMTRwdDsiPlRoaXMgaXMgYSAqKmJvbGQqKiB3b3JkIGluIE1hcmtkb3duPC9kaXY+PGRp +diBzdHlsZT0iZm9udC1zaXplOiAxNHB0OyI+PGJyPjwvZGl2PjxkaXYgc3R5bGU9ImZvbnQtc2l6 +ZTogMTRwdDsiPlRoaXMgaXMgYSBsaW5rIDxhIGhyZWY9Imh0dHA6Ly9leGFtcGxlLmNvbSI+aHR0 +cDovL2V4YW1wbGUuY29tPC9hPjxicj4mbmJzcDs8L2Rpdj48ZGl2IHN0eWxlPSJmb250LXNpemU6 +IDE0cHQ7Ij48YnI+PC9kaXY+PGRpdiBzdHlsZT0icGFkZGluZy10b3A6IDVweDsgYm9yZGVyLXRv +cC1jb2xvcjogcmdiKDIyOSwgMjI5LCAyMjkpOyBib3JkZXItdG9wLXdpZHRoOiAxcHg7IGJvcmRl +ci10b3Atc3R5bGU6IHNvbGlkOyI+PGRpdj48Zm9udCBmYWNlPSIgJ0NhbGlicmknLCAnU2Vnb2Ug +VUknLCAnTWVpcnlvJywgJ01pY3Jvc29mdCBZYUhlaSBVSScsICdNaWNyb3NvZnQgSmhlbmdIZWkg +VUknLCAnTWFsZ3VuIEdvdGhpYycsICdzYW5zLXNlcmlmJyIgc3R5bGU9J2xpbmUtaGVpZ2h0OiAx +NXB0OyBsZXR0ZXItc3BhY2luZzogMC4wMmVtOyBmb250LWZhbWlseTogIkNhbGlicmkiLCAiU2Vn +b2UgVUkiLCAiTWVpcnlvIiwgIk1pY3Jvc29mdCBZYUhlaSBVSSIsICJNaWNyb3NvZnQgSmhlbmdI +ZWkgVUkiLCAiTWFsZ3VuIEdvdGhpYyIsICJzYW5zLXNlcmlmIjsgZm9udC1zaXplOiAxMnB0Oyc+ +PGI+RnJvbTo8L2I+Jm5ic3A7PGEgaHJlZj0ibWFpbHRvOmluZm9AZGlzY291cnNlLm9yZyIgdGFy +Z2V0PSJfcGFyZW50Ij5BcnBpdCBKYWxhbjwvYT48YnI+PGI+U2VudDo8L2I+Jm5ic3A74oCORnJp +ZGF54oCOLCDigI5Ob3ZlbWJlcuKAjiDigI4yOOKAjiwg4oCOMjAxNCDigI4xMuKAjjrigI4zNeKA +jiDigI5QTTxicj48Yj5Ubzo8L2I+Jm5ic3A7PGEgaHJlZj0ibWFpbHRvOmphdHdvb2RAY29kaW5n +aG9ycm9yLmNvbSIgdGFyZ2V0PSJfcGFyZW50Ij5qZWZmIGF0d29vZDwvYT48L2ZvbnQ+PC9kaXY+ +PC9kaXY+PGRpdj48YnI+PC9kaXY+PGRpdiBkaXI9IiI+PGRpdj4KCjx0YWJsZSB0YWJpbmRleD0i +LTEiIHN0eWxlPSJtYXJnaW4tYm90dG9tOiAyNXB4OyIgYm9yZGVyPSIwIiBjZWxsc3BhY2luZz0i +MCIgY2VsbHBhZGRpbmc9IjAiPgogIDx0Ym9keT4KICAgIDx0cj4KICAgICAgPHRkIHN0eWxlPSJ3 +aWR0aDogNTVweDsgdmVydGljYWwtYWxpZ246IHRvcDsiPgogICAgICAgIDxpbWcgd2lkdGg9IjQ1 +IiBoZWlnaHQ9IjQ1IiB0YWJpbmRleD0iLTEiIHN0eWxlPSJtYXgtd2lkdGg6IDEwMCU7IiBzcmM9 +Imh0dHBzOi8vbWV0YS1kaXNjb3Vyc2UuZ2xvYmFsLnNzbC5mYXN0bHkubmV0L3VzZXJfYXZhdGFy +L21ldGEuZGlzY291cnNlLm9yZy90ZWNoYXBqLzQ1LzMyODEucG5nIiBkYXRhLW1zLWltZ3NyYz0i +aHR0cHM6Ly9tZXRhLWRpc2NvdXJzZS5nbG9iYWwuc3NsLmZhc3RseS5uZXQvdXNlcl9hdmF0YXIv +bWV0YS5kaXNjb3Vyc2Uub3JnL3RlY2hhcGovNDUvMzI4MS5wbmciPgogICAgICA8L3RkPgogICAg +ICA8dGQ+CiAgICAgICAgPGEgc3R5bGU9J2NvbG9yOiByZ2IoNTksIDg5LCAxNTIpOyBmb250LWZh +bWlseTogImx1Y2lkYSBncmFuZGUiLHRhaG9tYSx2ZXJkYW5hLGFyaWFsLHNhbnMtc2VyaWY7IGZv +bnQtc2l6ZTogMTNweDsgZm9udC13ZWlnaHQ6IGJvbGQ7IHRleHQtZGVjb3JhdGlvbjogbm9uZTsn +IGhyZWY9Imh0dHBzOi8vbWV0YS5kaXNjb3Vyc2Uub3JnL3VzZXJzL3RlY2hhcGoiIHRhcmdldD0i +X3BhcmVudCI+dGVjaEFQSjwvYT48YnI+CiAgICAgICAgPHNwYW4gc3R5bGU9J3RleHQtYWxpZ246 +IHJpZ2h0OyBjb2xvcjogcmdiKDE1MywgMTUzLCAxNTMpOyBwYWRkaW5nLXJpZ2h0OiA1cHg7IGZv +bnQtZmFtaWx5OiAibHVjaWRhIGdyYW5kZSIsdGFob21hLHZlcmRhbmEsYXJpYWwsc2Fucy1zZXJp +ZjsgZm9udC1zaXplOiAxMXB4Oyc+Tm92ZW1iZXIgMjg8L3NwYW4+CiAgICAgIDwvdGQ+CiAgICA8 +L3RyPgogICAgPHRyPgogICAgICA8dGQgc3R5bGU9InBhZGRpbmctdG9wOiA1cHg7IiBjb2xzcGFu +PSIyIj4KPHAgc3R5bGU9ImJvcmRlcjogMHB4IGJsYWNrOyBib3JkZXItaW1hZ2U6IG5vbmU7IG1h +cmdpbi10b3A6IDBweDsiPlRlc3QgcmVwbHkuPC9wPgoKPHAgc3R5bGU9ImJvcmRlcjogMHB4IGJs +YWNrOyBib3JkZXItaW1hZ2U6IG5vbmU7IG1hcmdpbi10b3A6IDBweDsiPkZpcnN0IHBhcmFncmFw +aC48L3A+Cgo8cCBzdHlsZT0iYm9yZGVyOiAwcHggYmxhY2s7IGJvcmRlci1pbWFnZTogbm9uZTsg +bWFyZ2luLXRvcDogMHB4OyI+U2Vjb25kIHBhcmFncmFwaC48L3A+CjwvdGQ+CiAgICA8L3RyPgog +IDwvdGJvZHk+CjwvdGFibGU+CgoKICA8ZGl2IHN0eWxlPSJjb2xvcjogcmdiKDEwMiwgMTAyLCAx +MDIpOyI+CiAgICA8cD5UbyByZXNwb25kLCByZXBseSB0byB0aGlzIGVtYWlsIG9yIHZpc2l0IDxh +IHN0eWxlPSJjb2xvcjogcmdiKDEwMiwgMTAyLCAxMDIpOyBmb250LXdlaWdodDogYm9sZDsgdGV4 +dC1kZWNvcmF0aW9uOiBub25lOyIgaHJlZj0iaHR0cHM6Ly9tZXRhLmRpc2NvdXJzZS5vcmcvdC90 +ZXN0aW5nLWRlZmF1bHQtZW1haWwtcmVwbGllcy8yMjYzOC8zIiB0YXJnZXQ9Il9wYXJlbnQiPmh0 +dHBzOi8vbWV0YS5kaXNjb3Vyc2Uub3JnL3QvdGVzdGluZy1kZWZhdWx0LWVtYWlsLXJlcGxpZXMv +MjI2MzgvMzwvYT4gaW4geW91ciBicm93c2VyLjwvcD4KICA8L2Rpdj4KICA8aHIgc3R5bGU9ImJv +cmRlcjogMXB4IGJsYWNrOyBib3JkZXItaW1hZ2U6IG5vbmU7IGhlaWdodDogMXB4OyBiYWNrZ3Jv +dW5kLWNvbG9yOiByZ2IoMjIxLCAyMjEsIDIyMSk7Ij4KICA8aDQ+UHJldmlvdXMgUmVwbGllczwv +aDQ+CgogIDx0YWJsZSB0YWJpbmRleD0iLTEiIHN0eWxlPSJtYXJnaW4tYm90dG9tOiAyNXB4OyIg +Ym9yZGVyPSIwIiBjZWxsc3BhY2luZz0iMCIgY2VsbHBhZGRpbmc9IjAiPgogIDx0Ym9keT4KICAg +IDx0cj4KICAgICAgPHRkIHN0eWxlPSJ3aWR0aDogNTVweDsgdmVydGljYWwtYWxpZ246IHRvcDsi +PgogICAgICAgIDxpbWcgd2lkdGg9IjQ1IiBoZWlnaHQ9IjQ1IiB0YWJpbmRleD0iLTEiIHN0eWxl +PSJtYXgtd2lkdGg6IDEwMCU7IiBzcmM9Imh0dHBzOi8vbWV0YS1kaXNjb3Vyc2UuZ2xvYmFsLnNz +bC5mYXN0bHkubmV0L3VzZXJfYXZhdGFyL21ldGEuZGlzY291cnNlLm9yZy9jb2Rpbmdob3Jyb3Iv +NDUvNTI5Ny5wbmciIGRhdGEtbXMtaW1nc3JjPSJodHRwczovL21ldGEtZGlzY291cnNlLmdsb2Jh +bC5zc2wuZmFzdGx5Lm5ldC91c2VyX2F2YXRhci9tZXRhLmRpc2NvdXJzZS5vcmcvY29kaW5naG9y +cm9yLzQ1LzUyOTcucG5nIj4KICAgICAgPC90ZD4KICAgICAgPHRkPgogICAgICAgIDxhIHN0eWxl +PSdjb2xvcjogcmdiKDU5LCA4OSwgMTUyKTsgZm9udC1mYW1pbHk6ICJsdWNpZGEgZ3JhbmRlIix0 +YWhvbWEsdmVyZGFuYSxhcmlhbCxzYW5zLXNlcmlmOyBmb250LXNpemU6IDEzcHg7IGZvbnQtd2Vp +Z2h0OiBib2xkOyB0ZXh0LWRlY29yYXRpb246IG5vbmU7JyBocmVmPSJodHRwczovL21ldGEuZGlz +Y291cnNlLm9yZy91c2Vycy9jb2Rpbmdob3Jyb3IiIHRhcmdldD0iX3BhcmVudCI+Y29kaW5naG9y +cm9yPC9hPjxicj4KICAgICAgICA8c3BhbiBzdHlsZT0ndGV4dC1hbGlnbjogcmlnaHQ7IGNvbG9y +OiByZ2IoMTUzLCAxNTMsIDE1Myk7IHBhZGRpbmctcmlnaHQ6IDVweDsgZm9udC1mYW1pbHk6ICJs +dWNpZGEgZ3JhbmRlIix0YWhvbWEsdmVyZGFuYSxhcmlhbCxzYW5zLXNlcmlmOyBmb250LXNpemU6 +IDExcHg7Jz5Ob3ZlbWJlciAyODwvc3Bhbj4KICAgICAgPC90ZD4KICAgIDwvdHI+CiAgICA8dHI+ +CiAgICAgIDx0ZCBzdHlsZT0icGFkZGluZy10b3A6IDVweDsiIGNvbHNwYW49IjIiPgo8cCBzdHls +ZT0iYm9yZGVyOiAwcHggYmxhY2s7IGJvcmRlci1pbWFnZTogbm9uZTsgbWFyZ2luLXRvcDogMHB4 +OyI+V2UncmUgdGVzdGluZyB0aGUgbGF0ZXN0IEdpdEh1YiBlbWFpbCBwcm9jZXNzaW5nIGxpYnJh +cnkgd2hpY2ggd2UgYXJlIGludGVncmF0aW5nIG5vdy48L3A+Cgo8cCBzdHlsZT0iYm9yZGVyOiAw +cHggYmxhY2s7IGJvcmRlci1pbWFnZTogbm9uZTsgbWFyZ2luLXRvcDogMHB4OyI+PGEgc3R5bGU9 +ImNvbG9yOiByZ2IoMCwgMTAyLCAxNTMpOyBmb250LXdlaWdodDogYm9sZDsgdGV4dC1kZWNvcmF0 +aW9uOiBub25lOyIgaHJlZj0iaHR0cHM6Ly9naXRodWIuY29tL2dpdGh1Yi9lbWFpbF9yZXBseV9w +YXJzZXIiIHRhcmdldD0iX3BhcmVudCI+aHR0cHM6Ly9naXRodWIuY29tL2dpdGh1Yi9lbWFpbF9y +ZXBseV9wYXJzZXI8L2E+PC9wPgoKPHAgc3R5bGU9ImJvcmRlcjogMHB4IGJsYWNrOyBib3JkZXIt +aW1hZ2U6IG5vbmU7IG1hcmdpbi10b3A6IDBweDsiPkdvIGFoZWFkIGFuZCByZXBseSB0byB0aGlz +IHRvcGljIGFuZCBJJ2xsIHJlcGx5IGZyb20gdmFyaW91cyBlbWFpbCBjbGllbnRzIGZvciB0ZXN0 +aW5nLjwvcD4KPC90ZD4KICAgIDwvdHI+CiAgPC90Ym9keT4KPC90YWJsZT4KCgo8aHIgc3R5bGU9 +ImJvcmRlcjogMXB4IGJsYWNrOyBib3JkZXItaW1hZ2U6IG5vbmU7IGhlaWdodDogMXB4OyBiYWNr +Z3JvdW5kLWNvbG9yOiByZ2IoMjIxLCAyMjEsIDIyMSk7Ij4KCjxkaXYgc3R5bGU9ImNvbG9yOiBy +Z2IoMTAyLCAxMDIsIDEwMik7Ij4KPHA+VG8gcmVzcG9uZCwgcmVwbHkgdG8gdGhpcyBlbWFpbCBv +ciB2aXNpdCA8YSBzdHlsZT0iY29sb3I6IHJnYigxMDIsIDEwMiwgMTAyKTsgZm9udC13ZWlnaHQ6 +IGJvbGQ7IHRleHQtZGVjb3JhdGlvbjogbm9uZTsiIGhyZWY9Imh0dHBzOi8vbWV0YS5kaXNjb3Vy +c2Uub3JnL3QvdGVzdGluZy1kZWZhdWx0LWVtYWlsLXJlcGxpZXMvMjI2MzgvMyIgdGFyZ2V0PSJf +cGFyZW50Ij5odHRwczovL21ldGEuZGlzY291cnNlLm9yZy90L3Rlc3RpbmctZGVmYXVsdC1lbWFp +bC1yZXBsaWVzLzIyNjM4LzM8L2E+IGluIHlvdXIgYnJvd3Nlci48L3A+CjwvZGl2Pgo8ZGl2IHN0 +eWxlPSJjb2xvcjogcmdiKDEwMiwgMTAyLCAxMDIpOyI+CjxwPlRvIHVuc3Vic2NyaWJlIGZyb20g +dGhlc2UgZW1haWxzLCB2aXNpdCB5b3VyIDxhIHN0eWxlPSJjb2xvcjogcmdiKDEwMiwgMTAyLCAx +MDIpOyBmb250LXdlaWdodDogYm9sZDsgdGV4dC1kZWNvcmF0aW9uOiBub25lOyIgaHJlZj0iaHR0 +cHM6Ly9tZXRhLmRpc2NvdXJzZS5vcmcvbXkvcHJlZmVyZW5jZXMiIHRhcmdldD0iX3BhcmVudCI+ +dXNlciBwcmVmZXJlbmNlczwvYT4uPC9wPgo8L2Rpdj4KPC9kaXY+CjwvZGl2PjxkaXYgc3R5bGU9 +ImZvbnQtc2l6ZTogMTRwdDsiPjxicj48L2Rpdj48L2Rpdj4KPC9ib2R5Pgo8L2h0bWw+Cg== + +--_866E2678-BB4F-4DD8-BE18-81B04AD8D1BC_-- diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 75efb1c867..b4fcacd5a5 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -4,10 +4,10 @@ describe ApplicationHelper do describe "escape_unicode" do it "encodes tags" do - helper.escape_unicode("").should == "\u003ctag>" + expect(helper.escape_unicode("")).to eq("\u003ctag>") end it "survives junk text" do - helper.escape_unicode("hello \xc3\x28 world").should =~ /hello.*world/ + expect(helper.escape_unicode("hello \xc3\x28 world")).to match(/hello.*world/) end end @@ -19,38 +19,38 @@ describe ApplicationHelper do it "is true if mobile_view is '1' in the session" do session[:mobile_view] = '1' - helper.mobile_view?.should == true + expect(helper.mobile_view?).to eq(true) end it "is false if mobile_view is '0' in the session" do session[:mobile_view] = '0' - helper.mobile_view?.should == false + expect(helper.mobile_view?).to eq(false) end context "mobile_view is not set" do it "is false if user agent is not mobile" do controller.request.stubs(:user_agent).returns('Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.17 Safari/537.36') - helper.mobile_view?.should be_falsey + expect(helper.mobile_view?).to be_falsey end it "is true for iPhone" do controller.request.stubs(:user_agent).returns('Mozilla/5.0 (iPhone; U; ru; CPU iPhone OS 4_2_1 like Mac OS X; ru) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148a Safari/6533.18.5') - helper.mobile_view?.should == true + expect(helper.mobile_view?).to eq(true) end it "is false for iPad" do controller.request.stubs(:user_agent).returns("Mozilla/5.0 (iPad; CPU OS 5_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9B176 Safari/7534.48.3") - helper.mobile_view?.should == false + expect(helper.mobile_view?).to eq(false) end it "is false for Nexus 10 tablet" do controller.request.stubs(:user_agent).returns("Mozilla/5.0 (Linux; Android 4.2.1; Nexus 10 Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19") - helper.mobile_view?.should be_falsey + expect(helper.mobile_view?).to be_falsey end it "is true for Nexus 7 tablet" do controller.request.stubs(:user_agent).returns("Mozilla/5.0 (Linux; Android 4.1.2; Nexus 7 Build/JZ054K) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19") - helper.mobile_view?.should == true + expect(helper.mobile_view?).to eq(true) end end end @@ -62,23 +62,23 @@ describe ApplicationHelper do it "is false if mobile_view is '1' in the session" do session[:mobile_view] = '1' - helper.mobile_view?.should == false + expect(helper.mobile_view?).to eq(false) end it "is false if mobile_view is '0' in the session" do session[:mobile_view] = '0' - helper.mobile_view?.should == false + expect(helper.mobile_view?).to eq(false) end context "mobile_view is not set" do it "is false if user agent is not mobile" do controller.request.stubs(:user_agent).returns('Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.17 Safari/537.36') - helper.mobile_view?.should == false + expect(helper.mobile_view?).to eq(false) end it "is false for iPhone" do controller.request.stubs(:user_agent).returns('Mozilla/5.0 (iPhone; U; ru; CPU iPhone OS 4_2_1 like Mac OS X; ru) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148a Safari/6533.18.5') - helper.mobile_view?.should == false + expect(helper.mobile_view?).to eq(false) end end end diff --git a/spec/jobs/automatic_group_membership_spec.rb b/spec/jobs/automatic_group_membership_spec.rb new file mode 100644 index 0000000000..fe80c04cae --- /dev/null +++ b/spec/jobs/automatic_group_membership_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' +require_dependency 'jobs/regular/automatic_group_membership' + +describe Jobs::AutomaticGroupMembership do + + it "raises an error when the group id is missing" do + expect { Jobs::AutomaticGroupMembership.new.execute({}) }.to raise_error(Discourse::InvalidParameters) + end + + it "updates the membership" do + user1 = Fabricate(:user, email: "foo@wat.com") + user2 = Fabricate(:user, email: "foo@bar.com") + group = Fabricate(:group, automatic_membership_email_domains: "wat.com", automatic_membership_retroactive: true) + + Jobs::AutomaticGroupMembership.new.execute(group_id: group.id) + + group.reload + expect(group.users.include?(user1)).to eq(true) + expect(group.users.include?(user2)).to eq(false) + end + +end diff --git a/spec/jobs/bulk_invite_spec.rb b/spec/jobs/bulk_invite_spec.rb index d6571dcc23..7f795be52c 100644 --- a/spec/jobs/bulk_invite_spec.rb +++ b/spec/jobs/bulk_invite_spec.rb @@ -5,15 +5,15 @@ describe Jobs::BulkInvite do context '.execute' do it 'raises an error when the filename is missing' do - lambda { Jobs::BulkInvite.new.execute(identifier: '46-discoursecsv', chunks: '1') }.should raise_error(Discourse::InvalidParameters) + expect { Jobs::BulkInvite.new.execute(identifier: '46-discoursecsv', chunks: '1') }.to raise_error(Discourse::InvalidParameters) end it 'raises an error when the identifier is missing' do - lambda { Jobs::BulkInvite.new.execute(filename: 'discourse.csv', chunks: '1') }.should raise_error(Discourse::InvalidParameters) + expect { Jobs::BulkInvite.new.execute(filename: 'discourse.csv', chunks: '1') }.to raise_error(Discourse::InvalidParameters) end it 'raises an error when the chunks is missing' do - lambda { Jobs::BulkInvite.new.execute(filename: 'discourse.csv', identifier: '46-discoursecsv') }.should raise_error(Discourse::InvalidParameters) + expect { Jobs::BulkInvite.new.execute(filename: 'discourse.csv', identifier: '46-discoursecsv') }.to raise_error(Discourse::InvalidParameters) end context '.read_csv_file' do @@ -24,7 +24,7 @@ describe Jobs::BulkInvite do it 'reads csv file' do bulk_invite.current_user = user bulk_invite.read_csv_file(csv_file) - Invite.where(email: "robin@outlook.com").exists?.should == true + expect(Invite.where(email: "robin@outlook.com").exists?).to eq(true) end end @@ -41,7 +41,7 @@ describe Jobs::BulkInvite do bulk_invite.current_user = user bulk_invite.send_invite(csv_info, 1) - Invite.where(email: email).exists?.should == true + expect(Invite.where(email: email).exists?).to eq(true) end it 'creates an invite with group' do @@ -51,8 +51,8 @@ describe Jobs::BulkInvite do bulk_invite.current_user = user bulk_invite.send_invite(csv_info, 1) invite = Invite.where(email: email).first - invite.should be_present - InvitedGroup.where(invite_id: invite.id, group_id: group.id).exists?.should == true + expect(invite).to be_present + expect(InvitedGroup.where(invite_id: invite.id, group_id: group.id).exists?).to eq(true) end it 'creates an invite with topic' do @@ -62,8 +62,8 @@ describe Jobs::BulkInvite do bulk_invite.current_user = user bulk_invite.send_invite(csv_info, 1) invite = Invite.where(email: email).first - invite.should be_present - TopicInvite.where(invite_id: invite.id, topic_id: topic.id).exists?.should == true + expect(invite).to be_present + expect(TopicInvite.where(invite_id: invite.id, topic_id: topic.id).exists?).to eq(true) end it 'creates an invite with group and topic' do @@ -74,9 +74,9 @@ describe Jobs::BulkInvite do bulk_invite.current_user = user bulk_invite.send_invite(csv_info, 1) invite = Invite.where(email: email).first - invite.should be_present - InvitedGroup.where(invite_id: invite.id, group_id: group.id).exists?.should == true - TopicInvite.where(invite_id: invite.id, topic_id: topic.id).exists?.should == true + expect(invite).to be_present + expect(InvitedGroup.where(invite_id: invite.id, group_id: group.id).exists?).to eq(true) + expect(TopicInvite.where(invite_id: invite.id, topic_id: topic.id).exists?).to eq(true) end end diff --git a/spec/jobs/clean_up_digest_keys.rb b/spec/jobs/clean_up_digest_keys.rb new file mode 100644 index 0000000000..d3cbcff40e --- /dev/null +++ b/spec/jobs/clean_up_digest_keys.rb @@ -0,0 +1,13 @@ +module Jobs + + class CleanUpDigestKeys < Jobs::Scheduled + every 1.day + + def execute(args) + # Remove unused digest keys + DigestUnsubscribeKey.where('created_at < ?', 2.months.ago).delete_all + end + end + +end + diff --git a/spec/jobs/crawl_topic_link_spec.rb b/spec/jobs/crawl_topic_link_spec.rb index 9224c499e4..5d7024b9eb 100644 --- a/spec/jobs/crawl_topic_link_spec.rb +++ b/spec/jobs/crawl_topic_link_spec.rb @@ -7,6 +7,6 @@ describe Jobs::CrawlTopicLink do let(:job) { Jobs::CrawlTopicLink.new } it "needs a topic_link_id" do - -> { job.execute({}) }.should raise_error(Discourse::InvalidParameters) + expect { job.execute({}) }.to raise_error(Discourse::InvalidParameters) end end diff --git a/spec/jobs/enqueue_digest_emails_spec.rb b/spec/jobs/enqueue_digest_emails_spec.rb index e27aa9922a..2c32d34318 100644 --- a/spec/jobs/enqueue_digest_emails_spec.rb +++ b/spec/jobs/enqueue_digest_emails_spec.rb @@ -10,7 +10,7 @@ describe Jobs::EnqueueDigestEmails do let!(:user_no_digests) { Fabricate(:active_user, email_digests: false, last_emailed_at: 8.days.ago, last_seen_at: 10.days.ago) } it "doesn't return users with email disabled" do - Jobs::EnqueueDigestEmails.new.target_user_ids.include?(user_no_digests.id).should == false + expect(Jobs::EnqueueDigestEmails.new.target_user_ids.include?(user_no_digests.id)).to eq(false) end end @@ -36,7 +36,7 @@ describe Jobs::EnqueueDigestEmails do let!(:user_emailed_recently) { Fabricate(:active_user, last_emailed_at: 6.days.ago) } it "doesn't return users who have been emailed recently" do - Jobs::EnqueueDigestEmails.new.target_user_ids.include?(user_emailed_recently.id).should == false + expect(Jobs::EnqueueDigestEmails.new.target_user_ids.include?(user_emailed_recently.id)).to eq(false) end end @@ -45,7 +45,15 @@ describe Jobs::EnqueueDigestEmails do let!(:inactive_user) { Fabricate(:user, active: false) } it "doesn't return users who have been emailed recently" do - Jobs::EnqueueDigestEmails.new.target_user_ids.include?(inactive_user.id).should == false + expect(Jobs::EnqueueDigestEmails.new.target_user_ids.include?(inactive_user.id)).to eq(false) + end + end + + context "suspended user" do + let!(:suspended_user) { Fabricate(:user, suspended_till: 1.week.from_now, suspended_at: 1.day.ago) } + + it "doesn't return users who are suspended" do + expect(Jobs::EnqueueDigestEmails.new.target_user_ids.include?(suspended_user.id)).to eq(false) end end @@ -55,20 +63,23 @@ describe Jobs::EnqueueDigestEmails do it "doesn't return users who have been emailed recently" do user = user_visited_this_week - Jobs::EnqueueDigestEmails.new.target_user_ids.include?(user.id).should == false + expect(Jobs::EnqueueDigestEmails.new.target_user_ids.include?(user.id)).to eq(false) end + end - it "does return users who have been emailed recently but have email_always set" do - user = user_visited_this_week_email_always - Jobs::EnqueueDigestEmails.new.target_user_ids.include?(user.id).should == true + context 'visited the site a year ago' do + let!(:user_visited_a_year_ago) { Fabricate(:active_user, last_seen_at: 370.days.ago) } + + it "doesn't return the user who have not visited the site for more than 365 days" do + expect(Jobs::EnqueueDigestEmails.new.target_user_ids.include?(user_visited_a_year_ago.id)).to eq(false) end end context 'regular users' do - let!(:user) { Fabricate(:active_user) } + let!(:user) { Fabricate(:active_user, last_seen_at: 360.days.ago) } it "returns the user" do - Jobs::EnqueueDigestEmails.new.target_user_ids.should == [user.id] + expect(Jobs::EnqueueDigestEmails.new.target_user_ids).to eq([user.id]) end end @@ -78,17 +89,31 @@ describe Jobs::EnqueueDigestEmails do let(:user) { Fabricate(:user) } - before do - Jobs::EnqueueDigestEmails.any_instance.expects(:target_user_ids).returns([user.id]) + context "digest emails are enabled" do + before do + Jobs::EnqueueDigestEmails.any_instance.expects(:target_user_ids).returns([user.id]) + end + + it "enqueues the digest email job" do + SiteSetting.stubs(:disable_digest_emails?).returns(false) + Jobs.expects(:enqueue).with(:user_email, type: :digest, user_id: user.id) + Jobs::EnqueueDigestEmails.new.execute({}) + end end - it "enqueues the digest email job" do - Jobs.expects(:enqueue).with(:user_email, type: :digest, user_id: user.id) - Jobs::EnqueueDigestEmails.new.execute({}) + context "digest emails are disabled" do + before do + Jobs::EnqueueDigestEmails.any_instance.expects(:target_user_ids).never + end + + it "does not enqueue the digest email job" do + SiteSetting.stubs(:disable_digest_emails?).returns(true) + Jobs.expects(:enqueue).with(:user_email, type: :digest, user_id: user.id).never + Jobs::EnqueueDigestEmails.new.execute({}) + end end end end - diff --git a/spec/jobs/export_csv_file_spec.rb b/spec/jobs/export_csv_file_spec.rb index 269fb12b2a..e56b56a492 100644 --- a/spec/jobs/export_csv_file_spec.rb +++ b/spec/jobs/export_csv_file_spec.rb @@ -3,11 +3,31 @@ require 'spec_helper' describe Jobs::ExportCsvFile do context '.execute' do - it 'raises an error when the entity is missing' do - lambda { Jobs::ExportCsvFile.new.execute(user_id: "1") }.should raise_error(Discourse::InvalidParameters) + expect { Jobs::ExportCsvFile.new.execute(user_id: "1") }.to raise_error(Discourse::InvalidParameters) end + end + let :user_list_header do + ['id','name','username','email','title','created_at','trust_level','active','admin','moderator','ip_address','topics_entered','posts_read_count','time_read','topic_count','post_count','likes_given','likes_received','external_id','external_email', 'external_username', 'external_name', 'external_avatar_url'] + end + + let :user_list_export do + Jobs::ExportCsvFile.new.user_list_export + end + + def to_hash(row) + Hash[*user_list_header.zip(row).flatten] + end + + it 'exports sso data' do + SiteSetting.enable_sso = true + user = Fabricate(:user) + user.create_single_sign_on_record(external_id: "123", last_payload: "xxx", external_email: 'test@test.com') + + user = to_hash(user_list_export.find{|u| u[0] == user.id}) + + expect(user["external_id"]).to eq("123") + expect(user["external_email"]).to eq("test@test.com") end end - diff --git a/spec/jobs/feature_topic_users_spec.rb b/spec/jobs/feature_topic_users_spec.rb index 2d759ce7da..be0bc69843 100644 --- a/spec/jobs/feature_topic_users_spec.rb +++ b/spec/jobs/feature_topic_users_spec.rb @@ -5,11 +5,11 @@ require 'jobs/regular/process_post' describe Jobs::FeatureTopicUsers do it "raises an error without a topic_id" do - lambda { Jobs::FeatureTopicUsers.new.execute({}) }.should raise_error(Discourse::InvalidParameters) + expect { Jobs::FeatureTopicUsers.new.execute({}) }.to raise_error(Discourse::InvalidParameters) end it "raises an error with a missing topic_id" do - lambda { Jobs::FeatureTopicUsers.new.execute(topic_id: 123) }.should raise_error(Discourse::InvalidParameters) + expect { Jobs::FeatureTopicUsers.new.execute(topic_id: 123) }.to raise_error(Discourse::InvalidParameters) end context 'with a topic' do @@ -22,22 +22,22 @@ describe Jobs::FeatureTopicUsers do it "won't feature the OP" do Jobs::FeatureTopicUsers.new.execute(topic_id: topic.id) - topic.reload.featured_user_ids.include?(topic.user_id).should == false + expect(topic.reload.featured_user_ids.include?(topic.user_id)).to eq(false) end it "features the second poster" do Jobs::FeatureTopicUsers.new.execute(topic_id: topic.id) - topic.reload.featured_user_ids.include?(coding_horror.id).should == true + expect(topic.reload.featured_user_ids.include?(coding_horror.id)).to eq(true) end it "will not feature the second poster if we supply their post to be ignored" do Jobs::FeatureTopicUsers.new.execute(topic_id: topic.id, except_post_id: second_post.id) - topic.reload.featured_user_ids.include?(coding_horror.id).should == false + expect(topic.reload.featured_user_ids.include?(coding_horror.id)).to eq(false) end it "won't feature the last poster" do Jobs::FeatureTopicUsers.new.execute(topic_id: topic.id) - topic.reload.featured_user_ids.include?(evil_trout.id).should == false + expect(topic.reload.featured_user_ids.include?(evil_trout.id)).to eq(false) end end @@ -51,21 +51,21 @@ describe Jobs::FeatureTopicUsers do it "it works as expected" do # It has 1 participant after creation - topic.participant_count.should == 1 + expect(topic.participant_count).to eq(1) # It still has 1 after featuring Jobs::FeatureTopicUsers.new.execute(topic_id: topic.id) - topic.reload.participant_count.should == 1 + expect(topic.reload.participant_count).to eq(1) # If the OP makes another post, it's still 1. create_post(topic: topic, user: post.user) Jobs::FeatureTopicUsers.new.execute(topic_id: topic.id) - topic.reload.participant_count.should == 1 + expect(topic.reload.participant_count).to eq(1) # If another users posts, it's 2. create_post(topic: topic, user: Fabricate(:evil_trout)) Jobs::FeatureTopicUsers.new.execute(topic_id: topic.id) - topic.reload.participant_count.should == 2 + expect(topic.reload.participant_count).to eq(2) end diff --git a/spec/jobs/invite_email_spec.rb b/spec/jobs/invite_email_spec.rb index 75611626e5..4b41e4223d 100644 --- a/spec/jobs/invite_email_spec.rb +++ b/spec/jobs/invite_email_spec.rb @@ -6,7 +6,7 @@ describe Jobs::InviteEmail do context '.execute' do it 'raises an error when the invite_id is missing' do - lambda { Jobs::InviteEmail.new.execute({}) }.should raise_error(Discourse::InvalidParameters) + expect { Jobs::InviteEmail.new.execute({}) }.to raise_error(Discourse::InvalidParameters) end context 'with an invite id' do diff --git a/spec/jobs/jobs_base_spec.rb b/spec/jobs/jobs_base_spec.rb index 3fceba53e6..14c7667789 100644 --- a/spec/jobs/jobs_base_spec.rb +++ b/spec/jobs/jobs_base_spec.rb @@ -23,7 +23,7 @@ describe Jobs::Base do it 'handles correct jobs' do job = GoodJob.new job.perform({}) - job.count.should == 1 + expect(job.count).to eq(1) end it 'handles errors in multisite' do @@ -33,7 +33,7 @@ describe Jobs::Base do bad = BadJob.new expect{bad.perform({})}.to raise_error - bad.fail_count.should == 3 + expect(bad.fail_count).to eq(3) end it 'delegates the process call to execute' do diff --git a/spec/jobs/jobs_spec.rb b/spec/jobs/jobs_spec.rb index 58671f9c84..2840b6c3e8 100644 --- a/spec/jobs/jobs_spec.rb +++ b/spec/jobs/jobs_spec.rb @@ -84,14 +84,14 @@ describe Jobs do job_to_keep2 = stub_everything(klass: 'Sidekiq::Extensions::DelayedClass', args: [YAML.dump(['Jobs::DrinkBeer', :delayed_perform, [{beer_id: 44}]])]) job_to_keep2.expects(:delete).never Sidekiq::ScheduledSet.stubs(:new).returns( [job_to_keep1, job_to_delete, job_to_keep2] ) - Jobs.cancel_scheduled_job(:drink_beer, {beer_id: 42}).should == true + expect(Jobs.cancel_scheduled_job(:drink_beer, {beer_id: 42})).to eq(true) end it 'returns false when no matching job is scheduled' do job_to_keep = stub_everything(klass: 'Sidekiq::Extensions::DelayedClass', args: [YAML.dump(['Jobs::DrinkBeer', :delayed_perform, [{beer_id: 43}]])]) job_to_keep.expects(:delete).never Sidekiq::ScheduledSet.stubs(:new).returns( [job_to_keep] ) - Jobs.cancel_scheduled_job(:drink_beer, {beer_id: 42}).should == false + expect(Jobs.cancel_scheduled_job(:drink_beer, {beer_id: 42})).to eq(false) end end diff --git a/spec/jobs/notify_mailing_list_subscribers_spec.rb b/spec/jobs/notify_mailing_list_subscribers_spec.rb new file mode 100644 index 0000000000..c7b9923b3c --- /dev/null +++ b/spec/jobs/notify_mailing_list_subscribers_spec.rb @@ -0,0 +1,60 @@ +require "spec_helper" + +describe Jobs::NotifyMailingListSubscribers do + + context "with mailing list on" do + let(:user) { Fabricate(:user, mailing_list_mode: true) } + + context "with a valid post" do + let!(:post) { Fabricate(:post, user: user) } + + it "sends the email to the user" do + UserNotifications.expects(:mailing_list_notify).with(user, post).once + Jobs::NotifyMailingListSubscribers.new.execute(post_id: post.id) + end + end + + context "with a deleted post" do + let!(:post) { Fabricate(:post, user: user, deleted_at: Time.now) } + + it "doesn't send the email to the user" do + UserNotifications.expects(:mailing_list_notify).with(user, post).never + Jobs::NotifyMailingListSubscribers.new.execute(post_id: post.id) + end + end + + context "with a user_deleted post" do + let!(:post) { Fabricate(:post, user: user, user_deleted: true) } + + it "doesn't send the email to the user" do + UserNotifications.expects(:mailing_list_notify).with(user, post).never + Jobs::NotifyMailingListSubscribers.new.execute(post_id: post.id) + end + end + + context "with a deleted topic" do + let!(:post) { Fabricate(:post, user: user) } + + before do + post.topic.update_column(:deleted_at, Time.now) + end + + it "doesn't send the email to the user" do + UserNotifications.expects(:mailing_list_notify).with(user, post).never + Jobs::NotifyMailingListSubscribers.new.execute(post_id: post.id) + end + end + + end + + context "with mailing list off" do + let(:user) { Fabricate(:user, mailing_list_mode: false) } + let!(:post) { Fabricate(:post, user: user) } + + it "doesn't send the email to the user" do + UserNotifications.expects(:mailing_list_notify).never + Jobs::NotifyMailingListSubscribers.new.execute(post_id: post.id) + end + end + +end diff --git a/spec/jobs/notify_moved_posts_spec.rb b/spec/jobs/notify_moved_posts_spec.rb index 24d8507696..eb1ed1d394 100644 --- a/spec/jobs/notify_moved_posts_spec.rb +++ b/spec/jobs/notify_moved_posts_spec.rb @@ -5,11 +5,11 @@ require_dependency 'jobs/regular/process_post' describe Jobs::NotifyMovedPosts do it "raises an error without post_ids" do - lambda { Jobs::NotifyMovedPosts.new.execute(moved_by_id: 1234) }.should raise_error(Discourse::InvalidParameters) + expect { Jobs::NotifyMovedPosts.new.execute(moved_by_id: 1234) }.to raise_error(Discourse::InvalidParameters) end it "raises an error without moved_by_id" do - lambda { Jobs::NotifyMovedPosts.new.execute(post_ids: [1,2,3]) }.should raise_error(Discourse::InvalidParameters) + expect { Jobs::NotifyMovedPosts.new.execute(post_ids: [1,2,3]) }.to raise_error(Discourse::InvalidParameters) end @@ -22,12 +22,12 @@ describe Jobs::NotifyMovedPosts do let(:moved_post_notifications) { Notification.where(notification_type: Notification.types[:moved_post]) } it "should create two notifications" do - lambda { Jobs::NotifyMovedPosts.new.execute(post_ids: [p1.id, p2.id, p3.id], moved_by_id: admin.id) }.should change(moved_post_notifications, :count).by(2) + expect { Jobs::NotifyMovedPosts.new.execute(post_ids: [p1.id, p2.id, p3.id], moved_by_id: admin.id) }.to change(moved_post_notifications, :count).by(2) end context 'when moved by one of the posters' do it "create one notifications, because the poster is the mover" do - lambda { Jobs::NotifyMovedPosts.new.execute(post_ids: [p1.id, p2.id, p3.id], moved_by_id: p1.user_id) }.should change(moved_post_notifications, :count).by(1) + expect { Jobs::NotifyMovedPosts.new.execute(post_ids: [p1.id, p2.id, p3.id], moved_by_id: p1.user_id) }.to change(moved_post_notifications, :count).by(1) end end diff --git a/spec/jobs/poll_mailbox_spec.rb b/spec/jobs/poll_mailbox_spec.rb index 3b7d883aeb..fb038c1290 100644 --- a/spec/jobs/poll_mailbox_spec.rb +++ b/spec/jobs/poll_mailbox_spec.rb @@ -116,13 +116,13 @@ describe Jobs::PollMailbox do poller.handle_mail(email) topic = Topic.where(category: category).where.not(id: category.topic_id).last - topic.should be_present - topic.title.should == "We should have a post-by-email-feature" + expect(topic).to be_present + expect(topic.title).to eq("We should have a post-by-email-feature") post = topic.posts.first - post.cooked.strip.should == expected_post.strip + expect(post.cooked.strip).to eq(expected_post.strip) - email.should be_deleted + expect(email).to be_deleted end describe "with insufficient trust" do @@ -144,8 +144,8 @@ describe Jobs::PollMailbox do expect_success poller.handle_mail(email) topic = Topic.where(category: category).where.not(id: category.topic_id).last - topic.should be_present - topic.title.should == "We should have a post-by-email-feature" + expect(topic).to be_present + expect(topic.title).to eq("We should have a post-by-email-feature") ensure category.email_in_allow_strangers = false category.save @@ -179,7 +179,7 @@ describe Jobs::PollMailbox do assert new_post.present? assert_equal expected_post.strip, new_post.cooked.strip - email.should be_deleted + expect(email).to be_deleted end it "works with multiple To addresses" do @@ -192,7 +192,7 @@ describe Jobs::PollMailbox do assert new_post.present? assert_equal expected_post.strip, new_post.cooked.strip - email.should be_deleted + expect(email).to be_deleted end describe "with the wrong reply key" do @@ -202,7 +202,7 @@ describe Jobs::PollMailbox do expect_exception Email::Receiver::EmailLogNotFound poller.handle_mail(email) - email.should be_deleted + expect(email).to be_deleted end end end @@ -227,7 +227,7 @@ describe Jobs::PollMailbox do expect_exception Email::Receiver::TopicClosedError poller.handle_mail(email) - email.should be_deleted + expect(email).to be_deleted end end end @@ -252,7 +252,7 @@ describe Jobs::PollMailbox do expect_exception Email::Receiver::TopicNotFoundError poller.handle_mail(email) - email.should be_deleted + expect(email).to be_deleted end end end @@ -264,7 +264,7 @@ describe Jobs::PollMailbox do expect_exception Email::Receiver::EmailLogNotFound poller.handle_mail(email) - email.should be_deleted + expect(email).to be_deleted end it "a no content reply raises an EmptyEmailError" do @@ -272,7 +272,7 @@ describe Jobs::PollMailbox do expect_exception Email::Receiver::EmptyEmailError poller.handle_mail(email) - email.should be_deleted + expect(email).to be_deleted end it "a fully empty email raises an EmptyEmailError" do @@ -280,7 +280,7 @@ describe Jobs::PollMailbox do expect_exception Email::Receiver::EmptyEmailError poller.handle_mail(email) - email.should be_deleted + expect(email).to be_deleted end end diff --git a/spec/jobs/process_post_spec.rb b/spec/jobs/process_post_spec.rb index 9d0223be83..723605373a 100644 --- a/spec/jobs/process_post_spec.rb +++ b/spec/jobs/process_post_spec.rb @@ -4,31 +4,46 @@ require 'jobs/regular/process_post' describe Jobs::ProcessPost do it "returns when the post cannot be found" do - lambda { Jobs::ProcessPost.new.perform(post_id: 1, sync_exec: true) }.should_not raise_error + expect { Jobs::ProcessPost.new.perform(post_id: 1, sync_exec: true) }.not_to raise_error end context 'with a post' do - before do - @post = Fabricate(:post) + let(:post) do + Fabricate(:post) end - it 'calls process on a CookedPostProcessor' do - CookedPostProcessor.any_instance.expects(:post_process).once - Jobs::ProcessPost.new.execute(post_id: @post.id) + it 'does not erase posts when CookedPostProcessor malfunctions' do + # Look kids, an actual reason why you want to use mocks + CookedPostProcessor.any_instance.expects(:html).returns(' ') + cooked = post.cooked + + post.reload + expect(post.cooked).to eq(cooked) + + Jobs::ProcessPost.new.execute(post_id: post.id, cook: true) end - it 'updates the html if the dirty flag is true' do - CookedPostProcessor.any_instance.expects(:dirty?).returns(true) - CookedPostProcessor.any_instance.expects(:html).returns('test') - Post.any_instance.expects(:update_column).with(:cooked, 'test').once - Jobs::ProcessPost.new.execute(post_id: @post.id) + it 'recooks if needed' do + cooked = post.cooked + + post.update_columns(cooked: "frogs") + Jobs::ProcessPost.new.execute(post_id: post.id, cook: true) + + post.reload + expect(post.cooked).to eq(cooked) end - it "doesn't update the cooked content if dirty is false" do - CookedPostProcessor.any_instance.expects(:dirty?).returns(false) - Post.any_instance.expects(:update_column).never - Jobs::ProcessPost.new.execute(post_id: @post.id) + it 'processes posts' do + + post = Fabricate(:post, raw: "") + expect(post.cooked).to match(/http/) + + Jobs::ProcessPost.new.execute(post_id: post.id) + post.reload + + # subtle but cooked post processor strip this stuff, this ensures all the code gets a workout + expect(post.cooked).not_to match(/http/) end end diff --git a/spec/jobs/send_system_message_spec.rb b/spec/jobs/send_system_message_spec.rb index 7ba2ac0f0f..4025c94e0d 100644 --- a/spec/jobs/send_system_message_spec.rb +++ b/spec/jobs/send_system_message_spec.rb @@ -4,11 +4,11 @@ require 'jobs/regular/send_system_message' describe Jobs::SendSystemMessage do it "raises an error without a user_id" do - lambda { Jobs::SendSystemMessage.new.execute(message_type: 'welcome_invite') }.should raise_error(Discourse::InvalidParameters) + expect { Jobs::SendSystemMessage.new.execute(message_type: 'welcome_invite') }.to raise_error(Discourse::InvalidParameters) end it "raises an error without a message_type" do - lambda { Jobs::SendSystemMessage.new.execute(user_id: 1234) }.should raise_error(Discourse::InvalidParameters) + expect { Jobs::SendSystemMessage.new.execute(user_id: 1234) }.to raise_error(Discourse::InvalidParameters) end context 'with valid parameters' do diff --git a/spec/jobs/test_email_spec.rb b/spec/jobs/test_email_spec.rb index 69daebf7e5..1c5e4ccb97 100644 --- a/spec/jobs/test_email_spec.rb +++ b/spec/jobs/test_email_spec.rb @@ -5,7 +5,7 @@ describe Jobs::TestEmail do context '.execute' do it 'raises an error when the address is missing' do - lambda { Jobs::TestEmail.new.execute({}) }.should raise_error(Discourse::InvalidParameters) + expect { Jobs::TestEmail.new.execute({}) }.to raise_error(Discourse::InvalidParameters) end context 'with an address' do diff --git a/spec/jobs/tl3_promotions_spec.rb b/spec/jobs/tl3_promotions_spec.rb index 49f3b6f1b7..b7521691a6 100644 --- a/spec/jobs/tl3_promotions_spec.rb +++ b/spec/jobs/tl3_promotions_spec.rb @@ -23,7 +23,7 @@ describe Jobs::Tl3Promotions do def create_leader_user user = Fabricate(:user, trust_level: TrustLevel[2]) TrustLevel3Requirements.any_instance.stubs(:requirements_met?).returns(true) - Promotion.new(user).review_tl2.should == true + expect(Promotion.new(user).review_tl2).to eq(true) user end @@ -40,7 +40,7 @@ describe Jobs::Tl3Promotions do TrustLevel3Requirements.any_instance.stubs(:requirements_met?).returns(false) TrustLevel3Requirements.any_instance.stubs(:requirements_lost?).returns(true) run_job - user.reload.trust_level.should == TrustLevel[2] + expect(user.reload.trust_level).to eq(TrustLevel[2]) end it "doesn't demote if user was promoted recently" do @@ -52,7 +52,7 @@ describe Jobs::Tl3Promotions do TrustLevel3Requirements.any_instance.stubs(:requirements_met?).returns(false) TrustLevel3Requirements.any_instance.stubs(:requirements_lost?).returns(true) run_job - user.reload.trust_level.should == TrustLevel[3] + expect(user.reload.trust_level).to eq(TrustLevel[3]) end it "doesn't demote if user hasn't lost requirements (low water mark)" do @@ -64,7 +64,7 @@ describe Jobs::Tl3Promotions do TrustLevel3Requirements.any_instance.stubs(:requirements_met?).returns(false) TrustLevel3Requirements.any_instance.stubs(:requirements_lost?).returns(false) run_job - user.reload.trust_level.should == TrustLevel[3] + expect(user.reload.trust_level).to eq(TrustLevel[3]) end end diff --git a/spec/jobs/update_gravatar_spec.rb b/spec/jobs/update_gravatar_spec.rb new file mode 100644 index 0000000000..c3f9b45295 --- /dev/null +++ b/spec/jobs/update_gravatar_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +describe Jobs::UpdateGravatar do + + it "picks gravatar if system avatar is picked and gravatar was just downloaded" do + user = User.create!(username: "bob", name: "bob", email: "a@a.com") + expect(user.uploaded_avatar_id).to eq(nil) + expect(user.user_avatar.gravatar_upload_id).to eq(nil) + + png = Base64.decode64("R0lGODlhAQABALMAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwICAgP8AAAD/AP//AAAA//8A/wD//wBiZCH5BAEAAA8ALAAAAAABAAEAAAQC8EUAOw==") + FakeWeb.register_uri(:get, "http://www.gravatar.com/avatar/d10ca8d11301c2f4993ac2279ce4b930.png?s=500&d=404", body: png) + + SiteSetting.automatically_download_gravatars = true + + user.refresh_avatar + user.reload + + expect(user.uploaded_avatar_id).to eq(user.user_avatar.gravatar_upload_id) + end + +end diff --git a/spec/jobs/user_email_spec.rb b/spec/jobs/user_email_spec.rb index 334749a500..a36fff4018 100644 --- a/spec/jobs/user_email_spec.rb +++ b/spec/jobs/user_email_spec.rb @@ -12,15 +12,15 @@ describe Jobs::UserEmail do let(:mailer) { Mail::Message.new(to: user.email) } it "raises an error when there is no user" do - lambda { Jobs::UserEmail.new.execute(type: :digest) }.should raise_error(Discourse::InvalidParameters) + expect { Jobs::UserEmail.new.execute(type: :digest) }.to raise_error(Discourse::InvalidParameters) end it "raises an error when there is no type" do - lambda { Jobs::UserEmail.new.execute(user_id: user.id) }.should raise_error(Discourse::InvalidParameters) + expect { Jobs::UserEmail.new.execute(user_id: user.id) }.to raise_error(Discourse::InvalidParameters) end it "raises an error when the type doesn't exist" do - lambda { Jobs::UserEmail.new.execute(type: :no_method, user_id: user.id) }.should raise_error(Discourse::InvalidParameters) + expect { Jobs::UserEmail.new.execute(type: :no_method, user_id: user.id) }.to raise_error(Discourse::InvalidParameters) end it "doesn't call the mailer when the user is missing" do @@ -34,7 +34,7 @@ describe Jobs::UserEmail do UserNotifications.expects(:authorize_email).returns(mailer) Email::Sender.any_instance.expects(:send) Jobs::UserEmail.new.execute(type: :authorize_email, user_id: user.id, to_address: 'jake@adventuretime.ooo') - mailer.to.should == ['jake@adventuretime.ooo'] + expect(mailer.to).to eq(['jake@adventuretime.ooo']) end end diff --git a/spec/mailers/test_mailer_spec.rb b/spec/mailers/test_mailer_spec.rb index 1937dadcbe..f1f6544d64 100644 --- a/spec/mailers/test_mailer_spec.rb +++ b/spec/mailers/test_mailer_spec.rb @@ -7,10 +7,10 @@ describe TestMailer do it "works" do test_mailer = TestMailer.send_test('marcheline@adventuretime.ooo') - test_mailer.from.should == [SiteSetting.notification_email] - test_mailer.to.should == ['marcheline@adventuretime.ooo'] - test_mailer.subject.should be_present - test_mailer.body.should be_present + expect(test_mailer.from).to eq([SiteSetting.notification_email]) + expect(test_mailer.to).to eq(['marcheline@adventuretime.ooo']) + expect(test_mailer.subject).to be_present + expect(test_mailer.body).to be_present end end diff --git a/spec/mailers/user_notifications_spec.rb b/spec/mailers/user_notifications_spec.rb index a8ddc4246d..18921cfb97 100644 --- a/spec/mailers/user_notifications_spec.rb +++ b/spec/mailers/user_notifications_spec.rb @@ -20,7 +20,7 @@ describe UserNotifications do reply3.hidden = true reply3.save - UserNotifications.get_context_posts(reply4, nil).count.should == 1 + expect(UserNotifications.get_context_posts(reply4, nil).count).to eq(1) end end @@ -29,10 +29,10 @@ describe UserNotifications do subject { UserNotifications.signup(user) } it "works" do - subject.to.should == [user.email] - subject.subject.should be_present - subject.from.should == [SiteSetting.notification_email] - subject.body.should be_present + expect(subject.to).to eq([user.email]) + expect(subject.subject).to be_present + expect(subject.from).to eq([SiteSetting.notification_email]) + expect(subject.body).to be_present end end @@ -42,10 +42,10 @@ describe UserNotifications do subject { UserNotifications.forgot_password(user) } it "works" do - subject.to.should == [user.email] - subject.subject.should be_present - subject.from.should == [SiteSetting.notification_email] - subject.body.should be_present + expect(subject.to).to eq([user.email]) + expect(subject.subject).to be_present + expect(subject.from).to eq([SiteSetting.notification_email]) + expect(subject.body).to be_present end end @@ -57,7 +57,7 @@ describe UserNotifications do context "without new topics" do it "doesn't send the email" do - subject.to.should be_blank + expect(subject.to).to be_blank end end @@ -70,11 +70,11 @@ describe UserNotifications do end it "works" do - subject.to.should == [user.email] - subject.subject.should be_present - subject.from.should == [SiteSetting.notification_email] - subject.html_part.body.to_s.should be_present - subject.text_part.body.to_s.should be_present + expect(subject.to).to eq([user.email]) + expect(subject.subject).to be_present + expect(subject.from).to eq([SiteSetting.notification_email]) + expect(subject.html_part.body.to_s).to be_present + expect(subject.text_part.body.to_s).to be_present end end @@ -90,7 +90,8 @@ describe UserNotifications do let(:notification) { Fabricate(:notification, user: user) } it 'generates a correct email' do - SiteSetting.stubs(:enable_email_names).returns(true) + SiteSetting.enable_names = true + SiteSetting.display_name_on_posts = true mail = UserNotifications.user_replied(response.user, post: response, notification: notification) # from should include full user name @@ -100,91 +101,94 @@ describe UserNotifications do expect(mail.subject).to match(/India/) # 2 respond to links cause we have 1 context post - mail.html_part.to_s.scan(/To respond/).count.should == 2 + expect(mail.html_part.to_s.scan(/To respond/).count).to eq(2) # 1 unsubscribe - mail.html_part.to_s.scan(/To unsubscribe/).count.should == 1 + expect(mail.html_part.to_s.scan(/To unsubscribe/).count).to eq(1) # side effect, topic user is updated with post number tu = TopicUser.get(post.topic_id, response.user) - tu.last_emailed_post_number.should == response.post_number + expect(tu.last_emailed_post_number).to eq(response.post_number) # in mailing list mode user_replies is not sent through response.user.mailing_list_mode = true mail = UserNotifications.user_replied(response.user, post: response, notification: notification) if rails_master? - mail.message.class.should == ActionMailer::Base::NullMail + expect(mail.message.class).to eq(ActionMailer::Base::NullMail) else - mail.class.should == ActionMailer::Base::NullMail + expect(mail.class).to eq(ActionMailer::Base::NullMail) end response.user.mailing_list_mode = nil mail = UserNotifications.user_replied(response.user, post: response, notification: notification) if rails_master? - mail.message.class.should_not == ActionMailer::Base::NullMail + expect(mail.message.class).not_to eq(ActionMailer::Base::NullMail) else - mail.class.should_not == ActionMailer::Base::NullMail + expect(mail.class).not_to eq(ActionMailer::Base::NullMail) end end end describe '.user_posted' do - let(:response_by_user) { Fabricate(:user, name: "John Doe") } + let(:response_by_user) { Fabricate(:user, name: "John Doe", username: "john") } let(:post) { Fabricate(:post) } let(:response) { Fabricate(:post, topic: post.topic, user: response_by_user)} let(:user) { Fabricate(:user) } - let(:notification) { Fabricate(:notification, user: user) } + let(:notification) { Fabricate(:notification, user: user, data: {original_username: response_by_user.username}.to_json) } it 'generates a correct email' do - SiteSetting.stubs(:enable_email_names).returns(false) + SiteSetting.enable_names = false mail = UserNotifications.user_posted(response.user, post: response, notification: notification) # from should not include full user name if "show user full names" is disabled expect(mail[:from].display_names).to_not eql(['John Doe']) + # from should include username if "show user full names" is disabled + expect(mail[:from].display_names).to eql(['john']) + # subject should not include category name expect(mail.subject).not_to match(/Uncategorized/) # 2 respond to links cause we have 1 context post - mail.html_part.to_s.scan(/To respond/).count.should == 2 + expect(mail.html_part.to_s.scan(/To respond/).count).to eq(2) # 1 unsubscribe link - mail.html_part.to_s.scan(/To unsubscribe/).count.should == 1 + expect(mail.html_part.to_s.scan(/To unsubscribe/).count).to eq(1) # side effect, topic user is updated with post number tu = TopicUser.get(post.topic_id, response.user) - tu.last_emailed_post_number.should == response.post_number + expect(tu.last_emailed_post_number).to eq(response.post_number) end end describe '.user_private_message' do - let(:response_by_user) { Fabricate(:user, name: "John Doe") } + let(:response_by_user) { Fabricate(:user, name: "", username: "john") } let(:topic) { Fabricate(:private_message_topic) } let(:response) { Fabricate(:post, topic: topic, user: response_by_user)} let(:user) { Fabricate(:user) } - let(:notification) { Fabricate(:notification, user: user) } + let(:notification) { Fabricate(:notification, user: user, data: {original_username: response_by_user.username}.to_json) } it 'generates a correct email' do - SiteSetting.stubs(:enable_email_names).returns(true) + SiteSetting.enable_names = true mail = UserNotifications.user_private_message(response.user, post: response, notification: notification) - # from should include full user name - expect(mail[:from].display_names).to eql(['John Doe']) + # from should include username if full user name is not provided + expect(mail[:from].display_names).to eql(['john']) # subject should include "[PM]" expect(mail.subject).to match("[PM]") # 1 respond to link - mail.html_part.to_s.scan(/To respond/).count.should == 1 + expect(mail.html_part.to_s.scan(/To respond/).count).to eq(1) # 1 unsubscribe link - mail.html_part.to_s.scan(/To unsubscribe/).count.should == 1 + expect(mail.html_part.to_s.scan(/To unsubscribe/).count).to eq(1) # side effect, topic user is updated with post number tu = TopicUser.get(topic.id, response.user) - tu.last_emailed_post_number.should == response.post_number + expect(tu.last_emailed_post_number).to eq(response.post_number) end end @@ -264,11 +268,18 @@ describe UserNotifications do expects_build_with(has_key(:topic_id)) end - it "has a from alias" do - SiteSetting.stubs(:enable_email_names).returns(true) + it "should have user name as from_alias" do + SiteSetting.enable_names = true + SiteSetting.display_name_on_posts = true expects_build_with(has_entry(:from_alias, "#{user.name}")) end + it "should not have user name as from_alias if display_name_on_posts is disabled" do + SiteSetting.enable_names = false + SiteSetting.display_name_on_posts = false + expects_build_with(has_entry(:from_alias, "walterwhite")) + end + it "should explain how to respond" do expects_build_with(Not(has_entry(:include_respond_instructions, false))) end diff --git a/spec/mailers/version_mailer_spec.rb b/spec/mailers/version_mailer_spec.rb index 0a98e9de6d..61f8e0c251 100644 --- a/spec/mailers/version_mailer_spec.rb +++ b/spec/mailers/version_mailer_spec.rb @@ -7,7 +7,7 @@ describe VersionMailer do before { SiteSetting.stubs(:contact_email).returns('') } it "doesn't send the email" do - subject.to.should be_blank + expect(subject.to).to be_blank end end @@ -15,10 +15,10 @@ describe VersionMailer do before { SiteSetting.stubs(:contact_email).returns('me@example.com') } it "works" do - subject.to.should == ['me@example.com'] - subject.subject.should be_present - subject.from.should == [SiteSetting.notification_email] - subject.body.should be_present + expect(subject.to).to eq(['me@example.com']) + expect(subject.subject).to be_present + expect(subject.from).to eq([SiteSetting.notification_email]) + expect(subject.body).to be_present end end diff --git a/spec/models/admin_dashboard_data_spec.rb b/spec/models/admin_dashboard_data_spec.rb index fbf320cd3e..d26d088300 100644 --- a/spec/models/admin_dashboard_data_spec.rb +++ b/spec/models/admin_dashboard_data_spec.rb @@ -7,17 +7,17 @@ describe AdminDashboardData do it 'returns nil when running in production mode' do Rails.stubs(env: ActiveSupport::StringInquirer.new('production')) - subject.should == nil + expect(subject).to be_nil end it 'returns a string when running in development mode' do Rails.stubs(env: ActiveSupport::StringInquirer.new('development')) - subject.should_not == nil + expect(subject).to_not be_nil end it 'returns a string when running in test mode' do Rails.stubs(env: ActiveSupport::StringInquirer.new('test')) - subject.should_not == nil + expect(subject).to_not be_nil end end @@ -26,17 +26,17 @@ describe AdminDashboardData do it 'returns nil when host_names is set' do Discourse.stubs(:current_hostname).returns('something.com') - subject.should == nil + expect(subject).to be_nil end it 'returns a string when host_name is localhost' do Discourse.stubs(:current_hostname).returns('localhost') - subject.should_not == nil + expect(subject).to_not be_nil end it 'returns a string when host_name is production.localhost' do Discourse.stubs(:current_hostname).returns('production.localhost') - subject.should_not == nil + expect(subject).to_not be_nil end end @@ -45,12 +45,12 @@ describe AdminDashboardData do it 'returns nil when gc params are set' do ENV.stubs(:[]).with('RUBY_GC_MALLOC_LIMIT').returns(90000000) - subject.should == nil + expect(subject).to be_nil end it 'returns a string when gc params are not set' do ENV.stubs(:[]).with('RUBY_GC_MALLOC_LIMIT').returns(nil) - subject.should_not == nil + expect(subject).to_not be_nil end end @@ -60,31 +60,31 @@ describe AdminDashboardData do it 'returns nil when sidekiq processed a job recently' do Jobs.stubs(:last_job_performed_at).returns(1.minute.ago) Jobs.stubs(:queued).returns(0) - subject.should == nil + expect(subject).to be_nil end it 'returns nil when last job processed was a long time ago, but no jobs are queued' do Jobs.stubs(:last_job_performed_at).returns(7.days.ago) Jobs.stubs(:queued).returns(0) - subject.should == nil + expect(subject).to be_nil end it 'returns nil when no jobs have ever been processed, but no jobs are queued' do Jobs.stubs(:last_job_performed_at).returns(nil) Jobs.stubs(:queued).returns(0) - subject.should == nil + expect(subject).to be_nil end it 'returns a string when no jobs were processed recently and some jobs are queued' do Jobs.stubs(:last_job_performed_at).returns(20.minutes.ago) Jobs.stubs(:queued).returns(1) - subject.should_not == nil + expect(subject).to_not be_nil end it 'returns a string when no jobs have ever been processed, and some jobs are queued' do Jobs.stubs(:last_job_performed_at).returns(nil) Jobs.stubs(:queued).returns(1) - subject.should_not == nil + expect(subject).to_not be_nil end end @@ -93,17 +93,17 @@ describe AdminDashboardData do it 'returns nil when total ram is 1 GB' do MemInfo.any_instance.stubs(:mem_total).returns(1025272) - subject.should == nil + expect(subject).to be_nil end it 'returns nil when total ram cannot be determined' do MemInfo.any_instance.stubs(:mem_total).returns(nil) - subject.should == nil + expect(subject).to be_nil end it 'returns a string when total ram is less than 1 GB' do MemInfo.any_instance.stubs(:mem_total).returns(512636) - subject.should_not == nil + expect(subject).to_not be_nil end end @@ -184,7 +184,7 @@ describe AdminDashboardData do context 'when disabled' do it 'returns nil' do SiteSetting.stubs(enable_setting).returns(false) - subject.should == nil + expect(subject).to be_nil end end @@ -196,25 +196,25 @@ describe AdminDashboardData do it 'returns nil key and secret are set' do SiteSetting.stubs(key).returns('12313213') SiteSetting.stubs(secret).returns('12312313123') - subject.should == nil + expect(subject).to be_nil end it 'returns a string when key is not set' do SiteSetting.stubs(key).returns('') SiteSetting.stubs(secret).returns('12312313123') - subject.should_not == nil + expect(subject).to_not be_nil end it 'returns a string when secret is not set' do SiteSetting.stubs(key).returns('123123') SiteSetting.stubs(secret).returns('') - subject.should_not == nil + expect(subject).to_not be_nil end it 'returns a string when key and secret are not set' do SiteSetting.stubs(key).returns('') SiteSetting.stubs(secret).returns('') - subject.should_not == nil + expect(subject).to_not be_nil end end end diff --git a/spec/models/api_key_spec.rb b/spec/models/api_key_spec.rb index 7df92e574c..79aeac601d 100644 --- a/spec/models/api_key_spec.rb +++ b/spec/models/api_key_spec.rb @@ -3,14 +3,14 @@ require 'spec_helper' require_dependency 'api_key' describe ApiKey do - it { should belong_to :user } - it { should belong_to :created_by } + it { is_expected.to belong_to :user } + it { is_expected.to belong_to :created_by } - it { should validate_presence_of :key } + it { is_expected.to validate_presence_of :key } - it 'validates uniqueness of user_id' do + skip 'validates uniqueness of user_id' do Fabricate(:api_key) - should validate_uniqueness_of(:user_id) + is_expected.to validate_uniqueness_of(:user_id) end end diff --git a/spec/models/application_request_spec.rb b/spec/models/application_request_spec.rb new file mode 100644 index 0000000000..bfc4ff63b3 --- /dev/null +++ b/spec/models/application_request_spec.rb @@ -0,0 +1,76 @@ +require 'spec_helper' + +describe ApplicationRequest do + + before do + ApplicationRequest.clear_cache! + end + + def inc(key,opts=nil) + ApplicationRequest.increment!(key,opts) + end + + it 'logs nothing for an unflushed increment' do + ApplicationRequest.increment!(:anon) + ApplicationRequest.count.should == 0 + end + + it 'can automatically flush' do + t1 = Time.now.utc.at_midnight + freeze_time(t1) + inc(:http_total) + inc(:http_total) + inc(:http_total, autoflush: 3) + + ApplicationRequest.http_total.first.count.should == 3 + end + + it 'can flush based on time' do + t1 = Time.now.utc.at_midnight + freeze_time(t1) + ApplicationRequest.write_cache! + inc(:http_total) + ApplicationRequest.count.should == 0 + + freeze_time(t1 + ApplicationRequest.autoflush_seconds + 1) + inc(:http_total) + + ApplicationRequest.count.should == 1 + end + + it 'flushes yesterdays results' do + t1 = Time.now.utc.at_midnight + freeze_time(t1) + inc(:http_total) + freeze_time(t1.tomorrow) + inc(:http_total) + + ApplicationRequest.write_cache! + ApplicationRequest.count.should == 2 + end + + it 'clears cache correctly' do + # otherwise we have test pollution + inc(:anon) + ApplicationRequest.clear_cache! + ApplicationRequest.write_cache! + + ApplicationRequest.count.should == 0 + end + + it 'logs a few counts once flushed' do + time = Time.now.at_midnight + freeze_time(time) + + 3.times { inc(:http_total) } + 2.times { inc(:http_2xx) } + 4.times { inc(:http_3xx) } + + ApplicationRequest.write_cache! + + ApplicationRequest.http_total.first.count.should == 3 + ApplicationRequest.http_2xx.first.count.should == 2 + ApplicationRequest.http_3xx.first.count.should == 4 + + end +end diff --git a/spec/models/badge_spec.rb b/spec/models/badge_spec.rb index 16301ffdc2..f695d1de88 100644 --- a/spec/models/badge_spec.rb +++ b/spec/models/badge_spec.rb @@ -2,9 +2,16 @@ require 'spec_helper' require_dependency 'badge' describe Badge do + it { is_expected.to belong_to(:badge_type) } + it { is_expected.to belong_to(:badge_grouping) } + it { is_expected.to have_many(:user_badges).dependent(:destroy) } + + it { is_expected.to validate_presence_of(:name) } + it { is_expected.to validate_presence_of(:badge_type) } + it { is_expected.to validate_uniqueness_of(:name) } it 'has a valid system attribute for new badges' do - Badge.create!(name: "test", badge_type_id: 1).system?.should == false + expect(Badge.create!(name: "test", badge_type_id: 1).system?).to be false end end diff --git a/spec/models/badge_type.rb b/spec/models/badge_type.rb index 552518e7c7..8fc13e83dc 100644 --- a/spec/models/badge_type.rb +++ b/spec/models/badge_type.rb @@ -3,7 +3,7 @@ require_dependency 'badge_type' describe BadgeType do - it { should validate_presence_of :name } - it { should validate_uniqueness_of :name } + it { is_expected.to validate_presence_of :name } + it { is_expected.to validate_uniqueness_of :name } end diff --git a/spec/models/category_featured_topic_spec.rb b/spec/models/category_featured_topic_spec.rb index 3e757e0046..436b7e6119 100644 --- a/spec/models/category_featured_topic_spec.rb +++ b/spec/models/category_featured_topic_spec.rb @@ -2,8 +2,8 @@ require 'spec_helper' describe CategoryFeaturedTopic do - it { should belong_to :category } - it { should belong_to :topic } + it { is_expected.to belong_to :category } + it { is_expected.to belong_to :topic } context 'feature_topics_for' do let(:user) { Fabricate(:user) } @@ -21,7 +21,7 @@ describe CategoryFeaturedTopic do _uncategorized_post = PostCreator.create(user, raw: "this is my new post 123 post", title: "hello world") CategoryFeaturedTopic.feature_topics_for(category) - CategoryFeaturedTopic.count.should == 1 + expect(CategoryFeaturedTopic.count).to be(1) end @@ -29,7 +29,7 @@ describe CategoryFeaturedTopic do invisible_post = PostCreator.create(user, raw: "Don't look at this post because it's awful.", title: "not visible to anyone", category: category.id) invisible_post.topic.update_status('visible', false, Fabricate(:admin)) CategoryFeaturedTopic.feature_topics_for(category) - CategoryFeaturedTopic.count.should == 1 + expect(CategoryFeaturedTopic.count).to be(1) end end diff --git a/spec/models/category_featured_user_spec.rb b/spec/models/category_featured_user_spec.rb index a4d4a8e742..de7e0ff0ad 100644 --- a/spec/models/category_featured_user_spec.rb +++ b/spec/models/category_featured_user_spec.rb @@ -2,9 +2,8 @@ require 'spec_helper' describe CategoryFeaturedUser do - it { should belong_to :category } - it { should belong_to :user } - + it { is_expected.to belong_to :category } + it { is_expected.to belong_to :user } context 'featuring users' do @@ -14,11 +13,11 @@ describe CategoryFeaturedUser do end it 'has a featured user' do - CategoryFeaturedUser.count.should_not == 0 + expect(CategoryFeaturedUser.count).to be(1) end it 'returns the user via the category association' do - @category.featured_users.should be_present + expect(@category.featured_users).to be_present end end diff --git a/spec/models/category_spec.rb b/spec/models/category_spec.rb index 6defee8fee..3b4d2cd784 100644 --- a/spec/models/category_spec.rb +++ b/spec/models/category_spec.rb @@ -4,26 +4,26 @@ require 'spec_helper' require_dependency 'post_creator' describe Category do - it { should validate_presence_of :user_id } - it { should validate_presence_of :name } + it { is_expected.to validate_presence_of :user_id } + it { is_expected.to validate_presence_of :name } it 'validates uniqueness of name' do Fabricate(:category) - should validate_uniqueness_of(:name).scoped_to(:parent_category_id) + is_expected.to validate_uniqueness_of(:name).scoped_to(:parent_category_id) end it 'validates uniqueness in case insensitive way' do Fabricate(:category, name: "Cats") - c = Fabricate.build(:category, name: "cats") - c.should_not be_valid - c.errors[:name].should be_present + cats = Fabricate.build(:category, name: "cats") + expect(cats).to_not be_valid + expect(cats.errors[:name]).to be_present end describe "last_updated_at" do it "returns a number value of when the category was last updated" do last = Category.last_updated_at - last.should be_present - last.to_i.should == last + expect(last).to be_present + expect(last.to_i).to eq(last) end end @@ -31,8 +31,8 @@ describe Category do it "can determine read_restricted" do read_restricted, resolved = Category.resolve_permissions(:everyone => :full) - read_restricted.should == false - resolved.should == [] + expect(read_restricted).to be false + expect(resolved).to be_blank end end @@ -64,24 +64,24 @@ describe Category do can_read_category.save guardian = Guardian.new(admin) - Category.topic_create_allowed(guardian).count.should == 5 - Category.post_create_allowed(guardian).count.should == 5 - Category.secured(guardian).count.should == 5 + expect(Category.topic_create_allowed(guardian).count).to be(5) + expect(Category.post_create_allowed(guardian).count).to be(5) + expect(Category.secured(guardian).count).to be(5) guardian = Guardian.new(user) - Category.secured(guardian).count.should == 5 - Category.post_create_allowed(guardian).count.should == 4 - Category.topic_create_allowed(guardian).count.should == 3 # explicitly allowed once, default allowed once + expect(Category.secured(guardian).count).to be(5) + expect(Category.post_create_allowed(guardian).count).to be(4) + expect(Category.topic_create_allowed(guardian).count).to be(3) # explicitly allowed once, default allowed once # everyone has special semantics, test it as well can_post_category.set_permissions(:everyone => :create_post) can_post_category.save - Category.post_create_allowed(guardian).count.should == 4 + expect(Category.post_create_allowed(guardian).count).to be(4) # anonymous has permission to create no topics guardian = Guardian.new(nil) - Category.post_create_allowed(guardian).count.should == 0 + expect(Category.post_create_allowed(guardian).count).to be(0) end @@ -94,15 +94,15 @@ describe Category do let(:group) { Fabricate(:group) } it "secures categories correctly" do - category.read_restricted?.should == false + expect(category.read_restricted?).to be false category.set_permissions({}) - category.read_restricted?.should == true + expect(category.read_restricted?).to be true category.set_permissions(:everyone => :full) - category.read_restricted?.should == false + expect(category.read_restricted?).to be false - user.secure_categories.should be_empty + expect(user.secure_categories).to be_empty group.add(user) group.save @@ -111,7 +111,7 @@ describe Category do category.save user.reload - user.secure_categories.should == [category] + expect(user.secure_categories).to eq([category]) end it "lists all secured categories correctly" do @@ -123,48 +123,48 @@ describe Category do category_2.set_permissions(group.id => :full) category_2.save - Category.secured.should =~ [uncategorized] - Category.secured(Guardian.new(user)).should =~ [uncategorized,category, category_2] + expect(Category.secured).to match_array([uncategorized]) + expect(Category.secured(Guardian.new(user))).to match_array([uncategorized,category, category_2]) end end it "strips leading blanks" do - Fabricate(:category, name: " music").name.should == "music" + expect(Fabricate(:category, name: " music").name).to eq("music") end it "strips trailing blanks" do - Fabricate(:category, name: "bugs ").name.should == "bugs" + expect(Fabricate(:category, name: "bugs ").name).to eq("bugs") end it "strips leading and trailing blanks" do - Fabricate(:category, name: " blanks ").name.should == "blanks" + expect(Fabricate(:category, name: " blanks ").name).to eq("blanks") end it "sets name_lower" do - Fabricate(:category, name: "Not MySQL").name_lower.should == "not mysql" + expect(Fabricate(:category, name: "Not MySQL").name_lower).to eq("not mysql") end it "has custom fields" do category = Fabricate(:category, name: " music") - category.custom_fields["a"].should == nil + expect(category.custom_fields["a"]).to be_nil category.custom_fields["bob"] = "marley" category.custom_fields["jack"] = "black" category.save category = Category.find(category.id) - category.custom_fields.should == {"bob" => "marley", "jack" => "black"} + expect(category.custom_fields).to eq({"bob" => "marley", "jack" => "black"}) end describe "short name" do let!(:category) { Fabricate(:category, name: 'xx') } it "creates the category" do - category.should be_present + expect(category).to be_present end it 'has one topic' do - Topic.where(category_id: category.id).count.should == 1 + expect(Topic.where(category_id: category.id).count).to eq(1) end end @@ -172,14 +172,14 @@ describe Category do let(:category) { Fabricate(:category, name: "测试") } it "creates a blank slug, this is OK." do - category.slug.should be_blank - category.slug_for_url.should == "#{category.id}-category" + expect(category.slug).to be_blank + expect(category.slug_for_url).to eq("#{category.id}-category") end it "creates a localized slug if default locale is zh_CN" do SiteSetting.default_locale = 'zh_CN' - category.slug.should_not be_blank - category.slug_for_url.should == "ce-shi" + expect(category.slug).to_not be_blank + expect(category.slug_for_url).to eq("ce-shi") end end @@ -187,17 +187,36 @@ describe Category do let(:category) { Fabricate(:category, name: "2") } it 'creates a blank slug' do - category.slug.should be_blank - category.slug_for_url.should == "#{category.id}-category" + expect(category.slug).to be_blank + expect(category.slug_for_url).to eq("#{category.id}-category") + end + end + + describe 'custom slug can be provided' do + it 'has the custom value' do + c = Fabricate(:category, name: "Cats", slug: "cats-category") + expect(c.slug).to eq("cats-category") + end + + it 'and be sanitized' do + c = Fabricate(:category, name: 'Cats', slug: ' invalid slug') + c.slug.should == 'invalid-slug' + end + + it 'fails if custom slug is duplicate with existing' do + c1 = Fabricate(:category, name: "Cats", slug: "cats") + c2 = Fabricate.build(:category, name: "More Cats", slug: "cats") + expect(c2).to_not be_valid + expect(c2.errors[:slug]).to be_present end end describe 'description_text' do it 'correctly generates text description as needed' do c = Category.new - c.description_text.should == nil + expect(c.description_text).to be_nil c.description = "<hello test." - c.description_text.should == " { @category.update_attributes(name: 'Troutfishing', topic_id: nil) }.should_not raise_error + expect { @category.update_attributes(name: 'Troutfishing', topic_id: nil) }.to_not raise_error end it "should not set its description topic to auto-close" do category = Fabricate(:category, name: 'Closing Topics', auto_close_hours: 1) - category.topic.auto_close_at.should == nil + expect(category.topic.auto_close_at).to be_nil end describe "creating a new category with the same slug" do it "should have a blank slug if at the same level" do category = Fabricate(:category, name: "Amazing Categóry") - category.slug.should be_blank - category.slug_for_url.should == "#{category.id}-category" + expect(category.slug).to be_blank + expect(category.slug_for_url).to eq("#{category.id}-category") end it "doesn't have a blank slug if not at the same level" do parent = Fabricate(:category, name: 'Other parent') category = Fabricate(:category, name: "Amazing Categóry", parent_category_id: parent.id) - category.slug.should == 'amazing-category' - category.slug_for_url.should == "amazing-category" + expect(category.slug).to eq('amazing-category') + expect(category.slug_for_url).to eq("amazing-category") end end @@ -277,13 +296,23 @@ describe Category do end it 'does not cause changes' do - @category.topic_count.should == 0 - @topic.category.should == @category - @category.topic.should == @topic + expect(@category.topic_count).to eq(0) + expect(@topic.category).to eq(@category) + expect(@category.topic).to eq(@topic) end end end + describe "update" do + it "should enforce uniqueness of slug" do + Fabricate(:category, slug: "the-slug") + c2 = Fabricate(:category, slug: "different-slug") + c2.slug = "the-slug" + expect(c2).to_not be_valid + expect(c2.errors[:slug]).to be_present + end + end + describe 'destroy' do before do @category = Fabricate(:category) @@ -293,8 +322,8 @@ describe Category do end it 'is deleted correctly' do - Category.exists?(id: @category_id).should == false - Topic.exists?(id: @topic_id).should == false + expect(Category.exists?(id: @category_id)).to be false + expect(Topic.exists?(id: @topic_id)).to be false end end @@ -304,15 +333,15 @@ describe Category do post = create_post(category: category.name) category.reload - category.latest_post_id.should == post.id - category.latest_topic_id.should == post.topic_id + expect(category.latest_post_id).to eq(post.id) + expect(category.latest_topic_id).to eq(post.topic_id) post2 = create_post(category: category.name) post3 = create_post(topic_id: post.topic_id, category: category.name) category.reload - category.latest_post_id.should == post3.id - category.latest_topic_id.should == post2.topic_id + expect(category.latest_post_id).to eq(post3.id) + expect(category.latest_topic_id).to eq(post2.topic_id) post3.reload @@ -320,7 +349,7 @@ describe Category do destroyer.destroy category.reload - category.latest_post_id.should == post2.id + expect(category.latest_post_id).to eq(post2.id) end end @@ -337,14 +366,14 @@ describe Category do end it 'updates topic stats' do - @category.topics_week.should == 1 - @category.topics_month.should == 1 - @category.topics_year.should == 1 - @category.topic_count.should == 1 - @category.post_count.should == 1 - @category.posts_year.should == 1 - @category.posts_month.should == 1 - @category.posts_week.should == 1 + expect(@category.topics_week).to eq(1) + expect(@category.topics_month).to eq(1) + expect(@category.topics_year).to eq(1) + expect(@category.topic_count).to eq(1) + expect(@category.post_count).to eq(1) + expect(@category.posts_year).to eq(1) + expect(@category.posts_month).to eq(1) + expect(@category.posts_week).to eq(1) end end @@ -358,14 +387,14 @@ describe Category do end it 'does not count deleted topics' do - @category.topics_week.should == 0 - @category.topic_count.should == 0 - @category.topics_month.should == 0 - @category.topics_year.should == 0 - @category.post_count.should == 0 - @category.posts_year.should == 0 - @category.posts_month.should == 0 - @category.posts_week.should == 0 + expect(@category.topics_week).to eq(0) + expect(@category.topic_count).to eq(0) + expect(@category.topics_month).to eq(0) + expect(@category.topics_year).to eq(0) + expect(@category.post_count).to eq(0) + expect(@category.posts_year).to eq(0) + expect(@category.posts_month).to eq(0) + expect(@category.posts_week).to eq(0) end end @@ -381,10 +410,10 @@ describe Category do end it "doesn't count each version of a post" do - @category.post_count.should == 1 - @category.posts_year.should == 1 - @category.posts_month.should == 1 - @category.posts_week.should == 1 + expect(@category.post_count).to eq(1) + expect(@category.posts_year).to eq(1) + expect(@category.posts_month).to eq(1) + expect(@category.posts_week).to eq(1) end end @@ -397,14 +426,14 @@ describe Category do end it 'updates topic stats' do - @uncategorized.topics_week.should == 1 - @uncategorized.topics_month.should == 1 - @uncategorized.topics_year.should == 1 - @uncategorized.topic_count.should == 1 - @uncategorized.post_count.should == 1 - @uncategorized.posts_year.should == 1 - @uncategorized.posts_month.should == 1 - @uncategorized.posts_week.should == 1 + expect(@uncategorized.topics_week).to eq(1) + expect(@uncategorized.topics_month).to eq(1) + expect(@uncategorized.topics_year).to eq(1) + expect(@uncategorized.topic_count).to eq(1) + expect(@uncategorized.post_count).to eq(1) + expect(@uncategorized.posts_year).to eq(1) + expect(@uncategorized.posts_month).to eq(1) + expect(@uncategorized.posts_week).to eq(1) end end end @@ -412,7 +441,7 @@ describe Category do describe "#url" do it "builds a url for normal categories" do category = Fabricate(:category, name: "cats") - expect(category.url).to eq "/category/cats" + expect(category.url).to eq "/c/cats" end describe "for subcategories" do @@ -420,7 +449,7 @@ describe Category do parent_category = Fabricate(:category, name: "parent") subcategory = Fabricate(:category, name: "child", parent_category_id: parent_category.id) - expect(subcategory.url).to eq "/category/parent/child" + expect(subcategory.url).to eq "/c/parent/child" end end end @@ -429,12 +458,12 @@ describe Category do let(:cat) { Category.where(id: SiteSetting.uncategorized_category_id).first } it "reports as `uncategorized?`" do - cat.should be_uncategorized + expect(cat).to be_uncategorized end it "cannot have a parent category" do cat.parent_category_id = Fabricate(:category).id - cat.should_not be_valid + expect(cat).to_not be_valid end end @@ -444,27 +473,26 @@ describe Category do it "can be associated with a parent category" do sub_category = Fabricate.build(:category, parent_category_id: parent_category.id, user: user) - sub_category.should be_valid - sub_category.parent_category.should == parent_category + expect(sub_category).to be_valid + expect(sub_category.parent_category).to eq(parent_category) end it "cannot associate a category with itself" do category = Fabricate(:category, user: user) category.parent_category_id = category.id - category.should_not be_valid + expect(category).to_not be_valid end it "cannot have a category two levels deep" do sub_category = Fabricate(:category, parent_category_id: parent_category.id, user: user) nested_sub_category = Fabricate.build(:category, parent_category_id: sub_category.id, user: user) - nested_sub_category.should_not be_valid - + expect(nested_sub_category).to_not be_valid end describe ".query_parent_category" do it "should return the parent category id given a parent slug" do parent_category.name = "Amazing Category" - parent_category.id.should == Category.query_parent_category(parent_category.slug) + expect(parent_category.id).to eq(Category.query_parent_category(parent_category.slug)) end end @@ -472,7 +500,7 @@ describe Category do it "should return the category" do category = Fabricate(:category, name: "Amazing Category", parent_category_id: parent_category.id, user: user) parent_category.name = "Amazing Parent Category" - category.should == Category.query_category(category.slug, parent_category.id) + expect(category).to eq(Category.query_category(category.slug, parent_category.id)) end end @@ -483,10 +511,10 @@ describe Category do c1 = Fabricate(:category, email_in: 'lower@example.com') c2 = Fabricate(:category, email_in: 'UPPER@EXAMPLE.COM') c3 = Fabricate(:category, email_in: 'Mixed.Case@Example.COM') - Category.find_by_email('LOWER@EXAMPLE.COM').should == c1 - Category.find_by_email('upper@example.com').should == c2 - Category.find_by_email('mixed.case@example.com').should == c3 - Category.find_by_email('MIXED.CASE@EXAMPLE.COM').should == c3 + expect(Category.find_by_email('LOWER@EXAMPLE.COM')).to eq(c1) + expect(Category.find_by_email('upper@example.com')).to eq(c2) + expect(Category.find_by_email('mixed.case@example.com')).to eq(c3) + expect(Category.find_by_email('MIXED.CASE@EXAMPLE.COM')).to eq(c3) end end diff --git a/spec/models/category_user_spec.rb b/spec/models/category_user_spec.rb index e1a1808d68..fdb4ed9af9 100644 --- a/spec/models/category_user_spec.rb +++ b/spec/models/category_user_spec.rb @@ -13,13 +13,13 @@ describe CategoryUser do watching = CategoryUser.where(user_id: user.id, notification_level: CategoryUser.notification_levels[:watching]) CategoryUser.batch_set(user, :watching, [category1.id, category2.id]) - watching.pluck(:category_id).sort.should == [category1.id, category2.id] + expect(watching.pluck(:category_id).sort).to eq [category1.id, category2.id] CategoryUser.batch_set(user, :watching, []) - watching.count.should == 0 + expect(watching.count).to eq 0 CategoryUser.batch_set(user, :watching, [category2.id]) - watching.count.should == 1 + expect(watching.count).to eq 1 end @@ -43,14 +43,15 @@ describe CategoryUser do muted_post = create_post(category: muted_category) tracked_post = create_post(category: tracked_category) - Notification.where(user_id: user.id, topic_id: watched_post.topic_id).count.should == 1 - Notification.where(user_id: user.id, topic_id: tracked_post.topic_id).count.should == 0 + expect(Notification.where(user_id: user.id, topic_id: watched_post.topic_id).count).to eq 1 + expect(Notification.where(user_id: user.id, topic_id: tracked_post.topic_id).count).to eq 0 tu = TopicUser.get(tracked_post.topic, user) - tu.notification_level.should == TopicUser.notification_levels[:tracking] - tu.notifications_reason_id.should == TopicUser.notification_reasons[:auto_track_category] + expect(tu.notification_level).to eq TopicUser.notification_levels[:tracking] + expect(tu.notifications_reason_id).to eq TopicUser.notification_reasons[:auto_track_category] end end end + diff --git a/spec/models/color_scheme_color_spec.rb b/spec/models/color_scheme_color_spec.rb index e183e59142..e5ec898c6c 100644 --- a/spec/models/color_scheme_color_spec.rb +++ b/spec/models/color_scheme_color_spec.rb @@ -3,13 +3,13 @@ require 'spec_helper' describe ColorSchemeColor do def test_invalid_hex(hex) c = described_class.new(hex: hex) - c.should_not be_valid - c.errors[:hex].should be_present + expect(c).not_to be_valid + expect(c.errors[:hex]).to be_present end it "validates hex value" do ['fff', 'ffffff', '333333', '333', '0BeeF0'].each do |hex| - described_class.new(hex: hex).should be_valid + expect(described_class.new(hex: hex)).to be_valid end ['fffff', 'ffff', 'ff', 'f', '00000', '00', 'cheese', '#666666', '#666', '555 666'].each do |hex| test_invalid_hex(hex) diff --git a/spec/models/color_scheme_spec.rb b/spec/models/color_scheme_spec.rb index 729719304c..5ae8fface0 100644 --- a/spec/models/color_scheme_spec.rb +++ b/spec/models/color_scheme_spec.rb @@ -11,10 +11,10 @@ describe ColorScheme do describe "new" do it "can take colors" do c = described_class.new(valid_params) - c.colors.size.should == valid_colors.size - c.colors.first.should be_a(ColorSchemeColor) + expect(c.colors.size).to eq valid_colors.size + expect(c.colors.first).to be_a(ColorSchemeColor) expect { - c.save.should == true + expect(c.save).to eq true }.to change { ColorSchemeColor.count }.by(valid_colors.size) end end @@ -32,22 +32,22 @@ describe ColorScheme do it "creates a new color scheme" do c = described_class.create_from_base(name: 'Yellow', colors: {first_one: 'FFFF00', third_one: 'F00D33'}) - c.colors.size.should == base_colors.size + expect(c.colors.size).to eq base_colors.size first = c.colors.find {|x| x.name == 'first_one'} second = c.colors.find {|x| x.name == 'second_one'} third = c.colors.find {|x| x.name == 'third_one'} - first.hex.should == 'FFFF00' - second.hex.should == base_colors[:second_one] - third.hex.should == 'F00D33' + expect(first.hex).to eq 'FFFF00' + expect(second.hex).to eq base_colors[:second_one] + expect(third.hex).to eq 'F00D33' end context "hex_for_name without anything enabled" do it "returns nil for a missing attribute" do - described_class.hex_for_name('undefined').should == nil + expect(described_class.hex_for_name('undefined')).to eq nil end it "returns the base color for an attribute" do - described_class.hex_for_name('second_one').should == base_colors[:second_one] + expect(described_class.hex_for_name('second_one')).to eq base_colors[:second_one] end end end @@ -65,14 +65,14 @@ describe ColorScheme do describe "#enabled" do it "returns nil when there is no enabled record" do - described_class.enabled.should == nil + expect(described_class.enabled).to eq nil end it "returns the enabled color scheme" do - described_class.hex_for_name('$primary_background_color').should == nil + expect(described_class.hex_for_name('$primary_background_color')).to eq nil c = described_class.create(valid_params.merge(enabled: true)) - described_class.enabled.id.should == c.id - described_class.hex_for_name('$primary_background_color').should == "FFBB00" + expect(described_class.enabled.id).to eq c.id + expect(described_class.hex_for_name('$primary_background_color')).to eq "FFBB00" end end end diff --git a/spec/models/digest_email_site_setting_spec.rb b/spec/models/digest_email_site_setting_spec.rb index e4ec957995..cc5f39fa86 100644 --- a/spec/models/digest_email_site_setting_spec.rb +++ b/spec/models/digest_email_site_setting_spec.rb @@ -3,16 +3,16 @@ require 'spec_helper' describe DigestEmailSiteSetting do describe 'valid_value?' do it 'returns true for a valid value as an int' do - DigestEmailSiteSetting.valid_value?(1).should == true + expect(DigestEmailSiteSetting.valid_value?(1)).to eq true end it 'returns true for a valid value as a string' do - DigestEmailSiteSetting.valid_value?('1').should == true + expect(DigestEmailSiteSetting.valid_value?('1')).to eq true end it 'returns false for an invalid value' do - DigestEmailSiteSetting.valid_value?(1.5).should == false - DigestEmailSiteSetting.valid_value?('7 dogs').should == false + expect(DigestEmailSiteSetting.valid_value?(1.5)).to eq false + expect(DigestEmailSiteSetting.valid_value?('7 dogs')).to eq false end end end diff --git a/spec/models/digest_unsubscribe_key_spec.rb b/spec/models/digest_unsubscribe_key_spec.rb new file mode 100644 index 0000000000..f658a40219 --- /dev/null +++ b/spec/models/digest_unsubscribe_key_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper' +require_dependency 'digest_unsubscribe_key' + +describe DigestUnsubscribeKey do + + it { is_expected.to belong_to :user } + + describe 'key' do + + let(:user) { Fabricate(:user) } + let!(:key) { DigestUnsubscribeKey.create_key_for(user) } + + it 'has a temporary key' do + expect(key).to be_present + end + + describe '#user_for_key' do + + it 'can be used to find the user' do + expect(DigestUnsubscribeKey.user_for_key(key)).to eq(user) + end + + it 'returns nil with an invalid key' do + expect(DigestUnsubscribeKey.user_for_key('asdfasdf')).to be_blank + end + + end + + end + +end diff --git a/spec/models/discourse_single_sign_on_spec.rb b/spec/models/discourse_single_sign_on_spec.rb index 5f174c8b49..f6aa259c30 100644 --- a/spec/models/discourse_single_sign_on_spec.rb +++ b/spec/models/discourse_single_sign_on_spec.rb @@ -5,9 +5,9 @@ describe DiscourseSingleSignOn do @sso_url = "http://somesite.com/discourse_sso" @sso_secret = "shjkfdhsfkjh" - SiteSetting.stubs("enable_sso").returns(true) - SiteSetting.stubs("sso_url").returns(@sso_url) - SiteSetting.stubs("sso_secret").returns(@sso_secret) + SiteSetting.enable_sso = true + SiteSetting.sso_url = @sso_url + SiteSetting.sso_secret = @sso_secret end def make_sso @@ -25,13 +25,27 @@ describe DiscourseSingleSignOn do end def test_parsed(parsed, sso) - parsed.nonce.should == sso.nonce - parsed.email.should == sso.email - parsed.username.should == sso.username - parsed.name.should == sso.name - parsed.external_id.should == sso.external_id - parsed.custom_fields["a"].should == "Aa" - parsed.custom_fields["b.b"].should == "B.b" + expect(parsed.nonce).to eq sso.nonce + expect(parsed.email).to eq sso.email + expect(parsed.username).to eq sso.username + expect(parsed.name).to eq sso.name + expect(parsed.external_id).to eq sso.external_id + expect(parsed.custom_fields["a"]).to eq "Aa" + expect(parsed.custom_fields["b.b"]).to eq "B.b" + end + + it "can do round trip parsing correctly" do + sso = SingleSignOn.new + sso.sso_secret = "test" + sso.name = "sam saffron" + sso.username = "sam" + sso.email = "sam@sam.com" + + sso = SingleSignOn.parse(sso.payload, "test") + + expect(sso.name).to eq "sam saffron" + expect(sso.username).to eq "sam" + expect(sso.email).to eq "sam@sam.com" end it "can lookup or create user when name is blank" do @@ -43,14 +57,14 @@ describe DiscourseSingleSignOn do sso.email = "test@test.com" sso.external_id = "A" user = sso.lookup_or_create_user - user.should_not == nil + expect(user).to_not be_nil end it "can fill in data on way back" do sso = make_sso url, payload = sso.to_url.split("?") - url.should == sso.sso_url + expect(url).to eq sso.sso_url parsed = SingleSignOn.parse(payload, "supersecret") test_parsed(parsed, sso) @@ -60,10 +74,10 @@ describe DiscourseSingleSignOn do sso = make_sso sso.sso_url = "http://tcdev7.wpengine.com/?action=showlogin" - sso.to_url.split('?').size.should == 2 + expect(sso.to_url.split('?').size).to eq 2 url, payload = sso.to_url.split("?") - url.should == "http://tcdev7.wpengine.com/" + expect(url).to eq "http://tcdev7.wpengine.com/" parsed = SingleSignOn.parse(payload, "supersecret") test_parsed(parsed, sso) @@ -73,20 +87,54 @@ describe DiscourseSingleSignOn do _ , payload = DiscourseSingleSignOn.generate_url.split("?") sso = DiscourseSingleSignOn.parse(payload) - sso.nonce_valid?.should == true + expect(sso.nonce_valid?).to eq true sso.expire_nonce! - sso.nonce_valid?.should == false + expect(sso.nonce_valid?).to eq false end it "generates a correct sso url" do url, payload = DiscourseSingleSignOn.generate_url.split("?") - url.should == @sso_url + expect(url).to eq @sso_url sso = DiscourseSingleSignOn.parse(payload) - sso.nonce.should_not == nil + expect(sso.nonce).to_not be_nil + end + + context 'when sso_overrides_avatar is enabled' do + let!(:sso_record) { Fabricate(:single_sign_on_record, external_avatar_url: "http://example.com/an_image.png") } + let!(:sso) { + sso = DiscourseSingleSignOn.new + sso.username = "test" + sso.name = "test" + sso.email = sso_record.user.email + sso.external_id = sso_record.external_id + sso + } + let(:logo) { file_from_fixtures("logo.png") } + + before do + SiteSetting.sso_overrides_avatar = true + end + + it "deal with no avatar url passed for an existing user with an avatar" do + # Deliberately not setting avatar_url. + + user = sso.lookup_or_create_user + expect(user).to_not be_nil + end + + it "deal with no avatar_force_update passed as a boolean" do + FileHelper.stubs(:download).returns(logo) + + sso.avatar_url = "http://example.com/a_different_image.png" + sso.avatar_force_update = true + + user = sso.lookup_or_create_user + expect(user).to_not be_nil + end end end diff --git a/spec/models/draft_sequence_spec.rb b/spec/models/draft_sequence_spec.rb index 935fe8c7bd..8ed65ed450 100644 --- a/spec/models/draft_sequence_spec.rb +++ b/spec/models/draft_sequence_spec.rb @@ -3,12 +3,12 @@ require 'spec_helper' describe DraftSequence do it 'should produce next sequence for a key' do u = Fabricate(:user) - DraftSequence.next!(u, 'test').should == 1 - DraftSequence.next!(u, 'test').should == 2 + expect(DraftSequence.next!(u, 'test')).to eq 1 + expect(DraftSequence.next!(u, 'test')).to eq 2 end it 'should return 0 by default' do u = Fabricate(:user) - DraftSequence.current(u, 'test').should == 0 + expect(DraftSequence.current(u, 'test')).to eq 0 end end diff --git a/spec/models/draft_spec.rb b/spec/models/draft_spec.rb index 4ef4f3baeb..8b7f6b6ee3 100644 --- a/spec/models/draft_spec.rb +++ b/spec/models/draft_spec.rb @@ -6,32 +6,32 @@ describe Draft do end it "can get a draft by user" do Draft.set(@user, "test", 0, "data") - Draft.get(@user, "test", 0).should == "data" + expect(Draft.get(@user, "test", 0)).to eq "data" end it "uses the user id and key correctly" do Draft.set(@user, "test", 0,"data") - Draft.get(Fabricate.build(:coding_horror), "test", 0).should == nil + expect(Draft.get(Fabricate.build(:coding_horror), "test", 0)).to eq nil end it "should overwrite draft data correctly" do Draft.set(@user, "test", 0, "data") Draft.set(@user, "test", 0, "new data") - Draft.get(@user, "test", 0).should == "new data" + expect(Draft.get(@user, "test", 0)).to eq "new data" end it "should clear drafts on request" do Draft.set(@user, "test", 0, "data") Draft.clear(@user, "test", 0) - Draft.get(@user, "test", 0).should == nil + expect(Draft.get(@user, "test", 0)).to eq nil end it "should disregard old draft if sequence decreases" do Draft.set(@user, "test", 0, "data") Draft.set(@user, "test", 1, "hello") Draft.set(@user, "test", 0, "foo") - Draft.get(@user, "test", 0).should == nil - Draft.get(@user, "test", 1).should == "hello" + expect(Draft.get(@user, "test", 0)).to eq nil + expect(Draft.get(@user, "test", 1)).to eq "hello" end @@ -41,7 +41,7 @@ describe Draft do Draft.set(u, Draft::NEW_TOPIC, 0, 'my draft') _t = Fabricate(:topic, user: u) s = DraftSequence.current(u, Draft::NEW_TOPIC) - Draft.get(u, Draft::NEW_TOPIC, s).should == nil + expect(Draft.get(u, Draft::NEW_TOPIC, s)).to eq nil end it 'nukes new pm draft after a pm is created' do @@ -49,7 +49,7 @@ describe Draft do Draft.set(u, Draft::NEW_PRIVATE_MESSAGE, 0, 'my draft') t = Fabricate(:topic, user: u, archetype: Archetype.private_message, category_id: nil) s = DraftSequence.current(t.user, Draft::NEW_PRIVATE_MESSAGE) - Draft.get(u, Draft::NEW_PRIVATE_MESSAGE, s).should == nil + expect(Draft.get(u, Draft::NEW_PRIVATE_MESSAGE, s)).to eq nil end it 'does not nuke new topic draft after a pm is created' do @@ -57,7 +57,7 @@ describe Draft do Draft.set(u, Draft::NEW_TOPIC, 0, 'my draft') t = Fabricate(:topic, user: u, archetype: Archetype.private_message, category_id: nil) s = DraftSequence.current(t.user, Draft::NEW_TOPIC) - Draft.get(u, Draft::NEW_TOPIC, s).should == 'my draft' + expect(Draft.get(u, Draft::NEW_TOPIC, s)).to eq 'my draft' end it 'nukes the post draft when a post is created' do @@ -67,7 +67,7 @@ describe Draft do Draft.set(p.user, p.topic.draft_key, 0,'hello') PostCreator.new(user, raw: Fabricate.build(:post).raw).create - Draft.get(p.user, p.topic.draft_key, DraftSequence.current(p.user, p.topic.draft_key)).should == nil + expect(Draft.get(p.user, p.topic.draft_key, DraftSequence.current(p.user, p.topic.draft_key))).to eq nil end it 'nukes the post draft when a post is revised' do @@ -75,10 +75,9 @@ describe Draft do Draft.set(p.user, p.topic.draft_key, 0,'hello') p.revise(p.user, { raw: 'another test' }) s = DraftSequence.current(p.user, p.topic.draft_key) - Draft.get(p.user, p.topic.draft_key, s).should == nil + expect(Draft.get(p.user, p.topic.draft_key, s)).to eq nil end - it 'increases the sequence number when a post is revised' do - end + it 'increases the sequence number when a post is revised' end end diff --git a/spec/models/email_log_spec.rb b/spec/models/email_log_spec.rb index 4cabf766d5..70a0a79946 100644 --- a/spec/models/email_log_spec.rb +++ b/spec/models/email_log_spec.rb @@ -2,19 +2,19 @@ require 'spec_helper' describe EmailLog do - it { should belong_to :user } - it { should validate_presence_of :to_address } - it { should validate_presence_of :email_type } + it { is_expected.to belong_to :user } + it { is_expected.to validate_presence_of :to_address } + it { is_expected.to validate_presence_of :email_type } let(:user) { Fabricate(:user) } context 'after_create' do context 'with user' do it 'updates the last_emailed_at value for the user' do - lambda { + expect { user.email_logs.create(email_type: 'blah', to_address: user.email) user.reload - }.should change(user, :last_emailed_at) + }.to change(user, :last_emailed_at) end it "doesn't update last_emailed_at if skipped is true" do @@ -30,7 +30,7 @@ describe EmailLog do it "counts sent emails" do user.email_logs.create(email_type: 'blah', to_address: user.email) user.email_logs.create(email_type: 'blah', to_address: user.email, skipped: true) - described_class.count_per_day(1.day.ago, Time.now).first[1].should == 1 + expect(described_class.count_per_day(1.day.ago, Time.now).first[1]).to eq 1 end end diff --git a/spec/models/email_token_spec.rb b/spec/models/email_token_spec.rb index b42e1bdab2..994a020bdd 100644 --- a/spec/models/email_token_spec.rb +++ b/spec/models/email_token_spec.rb @@ -2,9 +2,9 @@ require 'spec_helper' describe EmailToken do - it { should validate_presence_of :user_id } - it { should validate_presence_of :email } - it { should belong_to :user } + it { is_expected.to validate_presence_of :user_id } + it { is_expected.to validate_presence_of :email } + it { is_expected.to belong_to :user } context '#create' do @@ -13,64 +13,62 @@ describe EmailToken do let!(:email_token) { user.email_tokens.create(email: 'bubblegum@adevnturetime.ooo') } it 'should create the email token' do - email_token.should be_present + expect(email_token).to be_present end it 'should downcase the email' do token = user.email_tokens.create(email: "UpperCaseSoWoW@GMail.com") - token.email.should == "uppercasesowow@gmail.com" + expect(token.email).to eq "uppercasesowow@gmail.com" end it 'is valid' do - email_token.should be_valid + expect(email_token).to be_valid end it 'has a token' do - email_token.token.should be_present + expect(email_token.token).to be_present end it 'is not confirmed' do - email_token.should_not be_confirmed + expect(email_token).to_not be_confirmed end it 'is not expired' do - email_token.should_not be_expired + expect(email_token).to_not be_expired end it 'marks the older token as expired' do original_token.reload - original_token.should be_expired + expect(original_token).to be_expired end end - - context '#confirm' do let(:user) { Fabricate(:user, active: false) } let(:email_token) { user.email_tokens.first } it 'returns nil with a nil token' do - EmailToken.confirm(nil).should be_blank + expect(EmailToken.confirm(nil)).to be_blank end it 'returns nil with a made up token' do - EmailToken.confirm(EmailToken.generate_token).should be_blank + expect(EmailToken.confirm(EmailToken.generate_token)).to be_blank end it 'returns nil unless the token is the right length' do - EmailToken.confirm('a').should be_blank + expect(EmailToken.confirm('a')).to be_blank end it 'returns nil when a token is expired' do email_token.update_column(:expired, true) - EmailToken.confirm(email_token.token).should be_blank + expect(EmailToken.confirm(email_token.token)).to be_blank end it 'returns nil when a token is older than a specific time' do SiteSetting.email_token_valid_hours = 10 email_token.update_column(:created_at, 11.hours.ago) - EmailToken.confirm(email_token.token).should be_blank + expect(EmailToken.confirm(email_token.token)).to be_blank end context 'taken email address' do @@ -81,7 +79,7 @@ describe EmailToken do end it 'returns nil when the email has been taken since the token has been generated' do - EmailToken.confirm(email_token.token).should be_blank + expect(EmailToken.confirm(email_token.token)).to be_blank end end @@ -89,7 +87,7 @@ describe EmailToken do context 'welcome message' do it 'sends a welcome message when the user is activated' do user = EmailToken.confirm(email_token.token) - user.send_welcome_message.should == true + expect(user.send_welcome_message).to eq true end context "when using the code a second time" do @@ -98,7 +96,7 @@ describe EmailToken do SiteSetting.email_token_grace_period_hours = 1 EmailToken.confirm(email_token.token) user = EmailToken.confirm(email_token.token) - user.send_welcome_message.should == false + expect(user.send_welcome_message).to eq false end end @@ -109,34 +107,30 @@ describe EmailToken do let!(:confirmed_user) { EmailToken.confirm(email_token.token) } it "returns the correct user" do - confirmed_user.should == user + expect(confirmed_user).to eq user end it 'marks the user as active' do confirmed_user.reload - confirmed_user.should be_active + expect(confirmed_user).to be_active end it 'marks the token as confirmed' do email_token.reload - email_token.should be_confirmed + expect(email_token).to be_confirmed end it "can be confirmed again" do EmailToken.stubs(:confirm_valid_after).returns(1.hour.ago) - EmailToken.confirm(email_token.token).should == user + expect(EmailToken.confirm(email_token.token)).to eq user # Unless `confirm_valid_after` has passed EmailToken.stubs(:confirm_valid_after).returns(1.hour.from_now) - EmailToken.confirm(email_token.token).should be_blank + expect(EmailToken.confirm(email_token.token)).to be_blank end - end - - end - - end + diff --git a/spec/models/error_log_spec.rb b/spec/models/error_log_spec.rb index 1afd68b694..e0576ac8d1 100644 --- a/spec/models/error_log_spec.rb +++ b/spec/models/error_log_spec.rb @@ -25,7 +25,7 @@ describe ErrorLog do it "creates a non empty file on first call" do ErrorLog.clear_all! ErrorLog.add_row!(hello: "world") - File.exists?(ErrorLog.filename).should == true + expect(File.exists?(ErrorLog.filename)).to eq true end end @@ -38,7 +38,7 @@ describe ErrorLog do ErrorLog.each do |h| i += 1 end - i.should == 2 + expect(i).to eq 2 end it "is able to skip rows" do @@ -51,7 +51,7 @@ describe ErrorLog do ErrorLog.skip(3) do |h| i += 1 end - i.should == 1 + expect(i).to eq 1 end end diff --git a/spec/models/global_setting_spec.rb b/spec/models/global_setting_spec.rb index eb02e192ff..06a604dd76 100644 --- a/spec/models/global_setting_spec.rb +++ b/spec/models/global_setting_spec.rb @@ -4,7 +4,7 @@ require 'tempfile' describe GlobalSetting::EnvProvider do it "can detect keys from env" do ENV['DISCOURSE_BLA'] = '1' - GlobalSetting::EnvProvider.new.keys.should include(:bla) + expect(GlobalSetting::EnvProvider.new.keys).to include(:bla) end end describe GlobalSetting::FileProvider do @@ -20,13 +20,13 @@ describe GlobalSetting::FileProvider do provider = GlobalSetting::FileProvider.from(f.path) - provider.lookup(:a,"").should == 1000 - provider.lookup(:b,"").should == "10 # = 00" - provider.lookup(:c,"").should == "10 # = 00" - provider.lookup(:d,"bob").should == nil - provider.lookup(:e,"bob").should == "bob" + expect(provider.lookup(:a,"")).to eq 1000 + expect(provider.lookup(:b,"")).to eq "10 # = 00" + expect(provider.lookup(:c,"")).to eq "10 # = 00" + expect(provider.lookup(:d,"bob")).to eq nil + expect(provider.lookup(:e,"bob")).to eq "bob" - provider.keys.sort.should == [:a, :b, :c, :d] + expect(provider.keys.sort).to eq [:a, :b, :c, :d] f.unlink end @@ -38,7 +38,7 @@ describe GlobalSetting::FileProvider do provider = GlobalSetting::FileProvider.from(f.path) - provider.lookup(:a,"").should == 500 + expect(provider.lookup(:a,"")).to eq 500 f.unlink end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index d71a20c4d3..f5fc34e942 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -15,17 +15,23 @@ describe Group do it "is invalid for blank" do group.name = "" - group.valid?.should == false + expect(group.valid?).to eq false end it "is valid for a longer name" do group.name = "this_is_a_name" - group.valid?.should == true + expect(group.valid?).to eq true end it "is invalid for non names" do group.name = "this is_a_name" - group.valid?.should == false + expect(group.valid?).to eq false + end + + it "is invalid for case-insensitive existing names" do + build(:group, name: 'this_is_a_name').save + group.name = 'This_Is_A_Name' + expect(group.valid?).to eq false end end @@ -41,6 +47,24 @@ describe Group do Group[:staff].user_ids - [-1] end + it "Correctly handles primary group" do + group = Fabricate(:group) + user = Fabricate(:user) + group.add(user) + group.save + + user.primary_group = group + user.save + + group.reload + + group.remove(user) + group.save + + user.reload + expect(user.primary_group).to eq nil + end + it "Can update moderator/staff/admin groups correctly" do admin = Fabricate(:admin) @@ -48,52 +72,52 @@ describe Group do Group.refresh_automatic_groups!(:admins, :staff, :moderators) - real_admins.should == [admin.id] - real_moderators.should == [moderator.id] - real_staff.sort.should == [moderator.id,admin.id].sort + expect(real_admins).to eq [admin.id] + expect(real_moderators).to eq [moderator.id] + expect(real_staff.sort).to eq [moderator.id,admin.id].sort admin.admin = false admin.save Group.refresh_automatic_group!(:admins) - real_admins.should == [] + expect(real_admins).to be_empty moderator.revoke_moderation! admin.grant_admin! - real_admins.should == [admin.id] - real_staff.should == [admin.id] + expect(real_admins).to eq [admin.id] + expect(real_staff).to eq [admin.id] admin.revoke_admin! - real_admins.should == [] - real_staff.should == [] + expect(real_admins).to be_empty + expect(real_staff).to be_empty admin.grant_moderation! - real_moderators.should == [admin.id] - real_staff.should == [admin.id] + expect(real_moderators).to eq [admin.id] + expect(real_staff).to eq [admin.id] admin.revoke_moderation! - real_admins.should == [] - real_staff.should == [] + expect(real_admins).to be_empty + expect(real_staff).to eq [] end it "Correctly updates automatic trust level groups" do user = Fabricate(:user) - Group[:trust_level_0].user_ids.should include user.id + expect(Group[:trust_level_0].user_ids).to include user.id user.change_trust_level!(TrustLevel[1]) - Group[:trust_level_1].user_ids.should include user.id + expect(Group[:trust_level_1].user_ids).to include user.id user.change_trust_level!(TrustLevel[2]) - Group[:trust_level_1].user_ids.should include user.id - Group[:trust_level_2].user_ids.should include user.id + expect(Group[:trust_level_1].user_ids).to include user.id + expect(Group[:trust_level_2].user_ids).to include user.id user2 = Fabricate(:coding_horror) user2.change_trust_level!(TrustLevel[3]) - Group[:trust_level_2].user_ids.sort.should == [-1, user.id, user2.id].sort + expect(Group[:trust_level_2].user_ids.sort).to eq [-1, user.id, user2.id].sort end it "Correctly updates all automatic groups upon request" do @@ -106,26 +130,25 @@ describe Group do Group.refresh_automatic_groups! groups = Group.includes(:users).to_a - groups.count.should == Group::AUTO_GROUPS.count + expect(groups.count).to eq Group::AUTO_GROUPS.count g = groups.find{|g| g.id == Group::AUTO_GROUPS[:admins]} - g.users.count.should == 2 - g.user_count.should == 2 + expect(g.users.count).to eq 2 + expect(g.user_count).to eq 2 g = groups.find{|g| g.id == Group::AUTO_GROUPS[:staff]} - g.users.count.should == 2 - g.user_count.should == 2 - + expect(g.users.count).to eq 2 + expect(g.user_count).to eq 2 g = groups.find{|g| g.id == Group::AUTO_GROUPS[:trust_level_1]} # admin, system and user - g.users.count.should == 3 - g.user_count.should == 3 + expect(g.users.count).to eq 3 + expect(g.user_count).to eq 3 g = groups.find{|g| g.id == Group::AUTO_GROUPS[:trust_level_2]} # system and user - g.users.count.should == 2 - g.user_count.should == 2 + expect(g.users.count).to eq 2 + expect(g.user_count).to eq 2 end @@ -143,12 +166,12 @@ describe Group do # no side effects please g.usernames = usernames g.reload - g.users.count.should == 1 + expect(g.users.count).to eq 1 g.usernames = usernames g.save! - g.usernames.split(",").sort.should == usernames.split(",").sort + expect(g.usernames.split(",").sort).to eq usernames.split(",").sort end it "correctly destroys groups" do @@ -160,31 +183,31 @@ describe Group do g.destroy - User.where(id: u1.id).count.should == 1 - GroupUser.where(group_id: g.id).count.should == 0 + expect(User.where(id: u1.id).count).to eq 1 + expect(GroupUser.where(group_id: g.id).count).to eq 0 end it "has custom fields" do group = Fabricate(:group) - group.custom_fields["a"].should == nil + expect(group.custom_fields["a"]).to be_nil group.custom_fields["hugh"] = "jackman" group.custom_fields["jack"] = "black" group.save group = Group.find(group.id) - group.custom_fields.should == {"hugh" => "jackman", "jack" => "black"} + expect(group.custom_fields).to eq({"hugh" => "jackman", "jack" => "black"}) end it "allows you to lookup a new group by name" do group = Fabricate(:group) - group.id.should == Group[group.name].id - group.id.should == Group[group.name.to_sym].id + expect(group.id).to eq Group[group.name].id + expect(group.id).to eq Group[group.name.to_sym].id end it "can find desired groups correctly" do - Group.desired_trust_level_groups(2).sort.should == [10,11,12] + expect(Group.desired_trust_level_groups(2).sort).to eq [10,11,12] end @@ -192,11 +215,34 @@ describe Group do user = Fabricate(:user, trust_level: 2) Group.user_trust_level_change!(user.id, 2) - user.groups.map(&:name).sort.should == ["trust_level_0","trust_level_1", "trust_level_2"] + expect(user.groups.map(&:name).sort).to eq ["trust_level_0","trust_level_1", "trust_level_2"] Group.user_trust_level_change!(user.id, 0) user.reload - user.groups.map(&:name).sort.should == ["trust_level_0"] + expect(user.groups.map(&:name).sort).to eq ["trust_level_0"] + end + + context "group management" do + let(:group) {Fabricate(:group)} + + it "by default has no managers" do + group.managers.should be_empty + end + + it "multiple managers can be appointed" do + 2.times do |i| + u = Fabricate(:user) + group.appoint_manager(u) + end + expect(group.managers.count).to eq(2) + end + + it "manager has authority to edit membership" do + u = Fabricate(:user) + expect(Guardian.new(u).can_edit?(group)).to be_falsy + group.appoint_manager(u) + expect(Guardian.new(u).can_edit?(group)).to be_truthy + end end end diff --git a/spec/models/incoming_link_spec.rb b/spec/models/incoming_link_spec.rb index ea239c0e4c..a03871a66b 100644 --- a/spec/models/incoming_link_spec.rb +++ b/spec/models/incoming_link_spec.rb @@ -15,14 +15,14 @@ describe IncomingLink do it "increases the incoming link counts" do link = incoming_link - link.domain.should == "twitter.com" - link.post_id.should == post.id + expect(link.domain).to eq "twitter.com" + expect(link.post_id).to eq post.id post.reload - post.incoming_link_count.should == 1 + expect(post.incoming_link_count).to eq 1 topic.reload - topic.incoming_link_count.should == 1 + expect(topic.incoming_link_count).to eq 1 end end @@ -63,22 +63,22 @@ describe IncomingLink do it "does nothing if referer is empty" do add(post_id: 1) - IncomingLink.count.should == 0 + expect(IncomingLink.count).to eq 0 end it "does nothing if referer is same as host" do add(post_id: 1, host: 'somesite.com', referer: 'http://somesite.com') - IncomingLink.count.should == 0 + expect(IncomingLink.count).to eq 0 end it "tracks not visits for invalid referers" do add(post_id: 1, referer: 'bang bang bang') - IncomingLink.count.should == 0 + expect(IncomingLink.count).to eq 0 end it "expects to be called with referer and user id" do add(host: "test.com", referer: 'http://some.other.site.com', post_id: 1) - IncomingLink.count.should == 1 + expect(IncomingLink.count).to eq 1 end it "is able to look up user_id and log it from the GET params" do @@ -86,7 +86,7 @@ describe IncomingLink do add(host: 'test.com', username: "bob", post_id: 1) first = IncomingLink.first - first.user_id.should == user.id + expect(first.user_id).to eq user.id end end diff --git a/spec/models/incoming_links_report_spec.rb b/spec/models/incoming_links_report_spec.rb index 780d0fa3d9..2320298ca3 100644 --- a/spec/models/incoming_links_report_spec.rb +++ b/spec/models/incoming_links_report_spec.rb @@ -5,63 +5,108 @@ describe IncomingLinksReport do describe 'integration' do it 'runs correctly' do p1 = create_post + p2 = create_post - IncomingLink.add( - referer: 'http://test.com', - host: 'http://boo.com', - topic_id: p1.topic.id, - ip_address: '10.0.0.2', - username: p1.user.username - ) + p1.topic.save + p2.topic.save + 7.times do |n| + IncomingLink.add( + referer: 'http://test.com', + host: 'http://discourse.example.com', + topic_id: p1.topic.id, + ip_address: "10.0.0.#{n}", + username: p1.user.username + ) + end + 3.times do |n| + IncomingLink.add( + referer: 'http://foo.com', + host: 'http://discourse.example.com', + topic_id: p2.topic.id, + ip_address: "10.0.0.#{n + 7}", + username: p2.user.username + ) + end + 2.times do |n| + IncomingLink.add( + referer: 'http://foo.com', + host: 'http://discourse.example.com', + topic_id: p2.topic.id, + ip_address: "10.0.0.#{n + 7 + 3}", + username: p1.user.username # ! user1 is the referer ! + ) + end - c = IncomingLinksReport.link_count_per_topic - c[p1.topic_id].should == 1 + r = IncomingLinksReport.find('top_referrers').as_json + expect(r[:data]).to eq [ + {username: p1.user.username, num_clicks: 7 + 2, num_topics: 2}, + {username: p2.user.username, num_clicks: 3, num_topics: 1} + ] - c = IncomingLinksReport.link_count_per_domain - c["test.com"].should == 1 + r = IncomingLinksReport.find('top_traffic_sources').as_json + expect(r[:data]).to eq [ + {domain: 'test.com', num_clicks: 7, num_topics: 1}, + {domain: 'foo.com', num_clicks: 3 + 2, num_topics: 1} + ] - c = IncomingLinksReport.topic_count_per_domain(['test.com', 'foo.com']) - c["test.com"].should == 1 - - c = IncomingLinksReport.topic_count_per_user() - c[p1.username].should == 1 + r = IncomingLinksReport.find('top_referred_topics').as_json + expect(r[:data]).to eq [ + {topic_id: p1.topic.id, topic_title: p1.topic.title, topic_slug: p1.topic.slug, num_clicks: 7}, + {topic_id: p2.topic.id, topic_title: p2.topic.title, topic_slug: p2.topic.slug, num_clicks: 2 + 3}, + ] end end describe 'top_referrers' do subject(:top_referrers) { IncomingLinksReport.find('top_referrers').as_json } - def stub_empty_referrers_data - IncomingLinksReport.stubs(:link_count_per_user).returns({}) - IncomingLinksReport.stubs(:topic_count_per_user).returns({}) + let(:amy) { Fabricate(:user, username: 'amy') } + let(:bob) { Fabricate(:user, username: 'bob') } + let(:post1) { Fabricate(:post) } + let(:post2) { Fabricate(:post) } + let(:topic1) { post1.topic } + let(:topic2) { post2.topic } + + def save_base_objects + amy.save; bob.save + post1.save; post2.save + topic1.save; topic2.save end it 'returns localized titles' do - stub_empty_referrers_data - top_referrers[:title].should be_present - top_referrers[:xaxis].should be_present - top_referrers[:ytitles].should be_present - top_referrers[:ytitles][:num_clicks].should be_present - top_referrers[:ytitles][:num_topics].should be_present + expect(top_referrers[:title]).to be_present + expect(top_referrers[:xaxis]).to be_present + expect(top_referrers[:ytitles]).to be_present + expect(top_referrers[:ytitles][:num_clicks]).to be_present + expect(top_referrers[:ytitles][:num_topics]).to be_present end it 'with no IncomingLink records, it returns correct data' do - stub_empty_referrers_data - top_referrers[:data].size.should == 0 + IncomingLink.delete_all + expect(top_referrers[:data].size).to eq 0 end it 'with some IncomingLink records, it returns correct data' do - IncomingLinksReport.stubs(:link_count_per_user).returns({'luke' => 4, 'chewie' => 2}) - IncomingLinksReport.stubs(:topic_count_per_user).returns({'luke' => 2, 'chewie' => 1}) - top_referrers[:data][0].should == {username: 'luke', num_clicks: 4, num_topics: 2} - top_referrers[:data][1].should == {username: 'chewie', num_clicks: 2, num_topics: 1} + save_base_objects + + 2.times do + Fabricate(:incoming_link, user: amy, post: post1).save + end + Fabricate(:incoming_link, user: amy, post: post2).save + 2.times do + Fabricate(:incoming_link, user: bob, post: post1).save + end + + expect(top_referrers[:data][0]).to eq({username: 'amy', num_clicks: 3, num_topics: 2}) + expect(top_referrers[:data][1]).to eq({username: 'bob', num_clicks: 2, num_topics: 1}) end end describe 'top_traffic_sources' do subject(:top_traffic_sources) { IncomingLinksReport.find('top_traffic_sources').as_json } + # TODO: STOP THE STUBBING def stub_empty_traffic_source_data IncomingLinksReport.stubs(:link_count_per_domain).returns({}) IncomingLinksReport.stubs(:topic_count_per_domain).returns({}) @@ -70,54 +115,56 @@ describe IncomingLinksReport do it 'returns localized titles' do stub_empty_traffic_source_data - top_traffic_sources[:title].should be_present - top_traffic_sources[:xaxis].should be_present - top_traffic_sources[:ytitles].should be_present - top_traffic_sources[:ytitles][:num_clicks].should be_present - top_traffic_sources[:ytitles][:num_topics].should be_present - top_traffic_sources[:ytitles][:num_users].should be_present + expect(top_traffic_sources[:title]).to be_present + expect(top_traffic_sources[:xaxis]).to be_present + expect(top_traffic_sources[:ytitles]).to be_present + expect(top_traffic_sources[:ytitles][:num_clicks]).to be_present + expect(top_traffic_sources[:ytitles][:num_topics]).to be_present + expect(top_traffic_sources[:ytitles][:num_users]).to be_present end it 'with no IncomingLink records, it returns correct data' do stub_empty_traffic_source_data - top_traffic_sources[:data].size.should == 0 + expect(top_traffic_sources[:data].size).to eq 0 end it 'with some IncomingLink records, it returns correct data' do IncomingLinksReport.stubs(:link_count_per_domain).returns({'twitter.com' => 8, 'facebook.com' => 3}) IncomingLinksReport.stubs(:topic_count_per_domain).returns({'twitter.com' => 2, 'facebook.com' => 3}) - top_traffic_sources[:data][0].should == {domain: 'twitter.com', num_clicks: 8, num_topics: 2} - top_traffic_sources[:data][1].should == {domain: 'facebook.com', num_clicks: 3, num_topics: 3} + expect(top_traffic_sources[:data][0]).to eq({domain: 'twitter.com', num_clicks: 8, num_topics: 2}) + expect(top_traffic_sources[:data][1]).to eq({domain: 'facebook.com', num_clicks: 3, num_topics: 3}) end end describe 'top_referred_topics' do subject(:top_referred_topics) { IncomingLinksReport.find('top_referred_topics').as_json } + # TODO: STOP THE STUBBING def stub_empty_referred_topics_data IncomingLinksReport.stubs(:link_count_per_topic).returns({}) end it 'returns localized titles' do stub_empty_referred_topics_data - top_referred_topics[:title].should be_present - top_referred_topics[:xaxis].should be_present - top_referred_topics[:ytitles].should be_present - top_referred_topics[:ytitles][:num_clicks].should be_present + expect(top_referred_topics[:title]).to be_present + expect(top_referred_topics[:xaxis]).to be_present + expect(top_referred_topics[:ytitles]).to be_present + expect(top_referred_topics[:ytitles][:num_clicks]).to be_present end it 'with no IncomingLink records, it returns correct data' do stub_empty_referred_topics_data - top_referred_topics[:data].size.should == 0 + expect(top_referred_topics[:data].size).to eq 0 end it 'with some IncomingLink records, it returns correct data' do topic1 = Fabricate.build(:topic, id: 123); topic2 = Fabricate.build(:topic, id: 234) + # TODO: OMG OMG THE STUBBING IncomingLinksReport.stubs(:link_count_per_topic).returns({topic1.id => 8, topic2.id => 3}) Topic.stubs(:select).returns(Topic); Topic.stubs(:where).returns(Topic) # bypass some activerecord methods Topic.stubs(:all).returns([topic1, topic2]) - top_referred_topics[:data][0].should == {topic_id: topic1.id, topic_title: topic1.title, topic_slug: topic1.slug, num_clicks: 8 } - top_referred_topics[:data][1].should == {topic_id: topic2.id, topic_title: topic2.title, topic_slug: topic2.slug, num_clicks: 3 } + expect(top_referred_topics[:data][0]).to eq({topic_id: topic1.id, topic_title: topic1.title, topic_slug: topic1.slug, num_clicks: 8 }) + expect(top_referred_topics[:data][1]).to eq({topic_id: topic2.id, topic_title: topic2.title, topic_slug: topic2.slug, num_clicks: 3 }) end end diff --git a/spec/models/invite_redeemer_spec.rb b/spec/models/invite_redeemer_spec.rb index b8992ac76f..bc30a173f5 100644 --- a/spec/models/invite_redeemer_spec.rb +++ b/spec/models/invite_redeemer_spec.rb @@ -5,10 +5,10 @@ describe InviteRedeemer do describe '#create_for_email' do let(:user) { InviteRedeemer.create_user_from_invite(Fabricate(:invite, email: 'walter.white@email.com'), 'walter', 'Walter White') } it "should be created correctly" do - user.username.should == 'walter' - user.name.should == 'Walter White' - user.should be_active - user.email.should == 'walter.white@email.com' + expect(user.username).to eq('walter') + expect(user.name).to eq('Walter White') + expect(user).to be_active + expect(user.email).to eq('walter.white@email.com') end end end diff --git a/spec/models/invite_spec.rb b/spec/models/invite_spec.rb index 63e902b330..a7a1b02a2b 100644 --- a/spec/models/invite_spec.rb +++ b/spec/models/invite_spec.rb @@ -2,7 +2,9 @@ require 'spec_helper' describe Invite do - it { should validate_presence_of :invited_by_id } + it { is_expected.to validate_presence_of :invited_by_id } + + it { is_expected.to rate_limit } let(:iceking) { 'iceking@adventuretime.ooo' } @@ -12,11 +14,11 @@ describe Invite do let(:invite) { Invite.create(email: user.email, invited_by: coding_horror) } it "should not allow an invite with the same email as an existing user" do - invite.should_not be_valid + expect(invite).not_to be_valid end it "should not allow a user to invite themselves" do - invite.email_already_exists.should == true + expect(invite.email_already_exists).to eq(true) end end @@ -26,12 +28,12 @@ describe Invite do let(:invite) { Invite.create(email: "test@mailinator.com", invited_by: coding_horror) } it "should not allow an invite with blacklisted email" do - invite.should_not be_valid + expect(invite).not_to be_valid end it "should allow an invite with non-blacklisted email" do invite = Fabricate(:invite, email: "test@mail.com", invited_by: coding_horror) - invite.should be_valid + expect(invite).to be_valid end end @@ -42,12 +44,12 @@ describe Invite do subject { Fabricate(:invite) } it "works" do - subject.invite_key.should be_present - subject.email_already_exists.should == false + expect(subject.invite_key).to be_present + expect(subject.email_already_exists).to eq(false) end it 'should store a lower case version of the email' do - subject.email.should == iceking + expect(subject.email).to eq(iceking) end end @@ -67,7 +69,7 @@ describe Invite do invite = topic.invite_by_email(inviter, iceking) invite.destroy invite = topic.invite_by_email(inviter, iceking) - invite.should be_present + expect(invite).to be_present end end @@ -77,8 +79,8 @@ describe Invite do end it 'belongs to the topic' do - topic.invites.should == [@invite] - @invite.topics.should == [topic] + expect(topic.invites).to eq([@invite]) + expect(@invite.topics).to eq([topic]) end context 'when added by another user' do @@ -86,18 +88,18 @@ describe Invite do let(:new_invite) { topic.invite_by_email(coding_horror, iceking) } it 'returns a different invite' do - new_invite.should_not == @invite - new_invite.invite_key.should_not == @invite.invite_key - new_invite.topics.should == [topic] + expect(new_invite).not_to eq(@invite) + expect(new_invite.invite_key).not_to eq(@invite.invite_key) + expect(new_invite.topics).to eq([topic]) end end context 'when adding a duplicate' do it 'returns the original invite' do - topic.invite_by_email(inviter, 'iceking@adventuretime.ooo').should == @invite - topic.invite_by_email(inviter, 'iceking@ADVENTURETIME.ooo').should == @invite - topic.invite_by_email(inviter, 'ICEKING@adventuretime.ooo').should == @invite + expect(topic.invite_by_email(inviter, 'iceking@adventuretime.ooo')).to eq(@invite) + expect(topic.invite_by_email(inviter, 'iceking@ADVENTURETIME.ooo')).to eq(@invite) + expect(topic.invite_by_email(inviter, 'ICEKING@adventuretime.ooo')).to eq(@invite) end it 'returns a new invite if the other has expired' do @@ -105,8 +107,8 @@ describe Invite do @invite.created_at = 2.days.ago @invite.save new_invite = topic.invite_by_email(inviter, 'iceking@adventuretime.ooo') - new_invite.should_not == @invite - new_invite.should_not be_expired + expect(new_invite).not_to eq(@invite) + expect(new_invite).not_to be_expired end end @@ -115,9 +117,9 @@ describe Invite do it 'should be the same invite' do @new_invite = another_topic.invite_by_email(inviter, iceking) - @new_invite.should == @invite - another_topic.invites.should == [@invite] - @invite.topics.should =~ [topic, another_topic] + expect(@new_invite).to eq(@invite) + expect(another_topic.invites).to eq([@invite]) + expect(@invite.topics).to match_array([topic, another_topic]) end end @@ -132,10 +134,10 @@ describe Invite do it "works" do # doesn't create an invite - invite.should be_blank + expect(invite).to be_blank # gives the user permission to access the topic - topic.allowed_users.include?(coding_horror).should == true + expect(topic.allowed_users.include?(coding_horror)).to eq(true) end end @@ -145,23 +147,23 @@ describe Invite do let(:invite) { Fabricate(:invite) } it 'creates a notification for the invitee' do - lambda { invite.redeem }.should change(Notification, :count) + expect { invite.redeem }.to change(Notification, :count) end it 'wont redeem an expired invite' do SiteSetting.expects(:invite_expiry_days).returns(10) invite.update_column(:created_at, 20.days.ago) - invite.redeem.should be_blank + expect(invite.redeem).to be_blank end it 'wont redeem a deleted invite' do invite.destroy - invite.redeem.should be_blank + expect(invite.redeem).to be_blank end it "won't redeem an invalidated invite" do invite.invalidated_at = 1.day.ago - invite.redeem.should be_blank + expect(invite.redeem).to be_blank end context 'enqueues a job to email "set password" instructions' do @@ -198,14 +200,14 @@ describe Invite do invite.save user = invite.redeem - user.groups.count.should == 1 + expect(user.groups.count).to eq(1) end end context "invite trust levels" do it "returns the trust level in default_invitee_trust_level" do SiteSetting.stubs(:default_invitee_trust_level).returns(TrustLevel[3]) - invite.redeem.trust_level.should == TrustLevel[3] + expect(invite.redeem.trust_level).to eq(TrustLevel[3]) end end @@ -213,7 +215,7 @@ describe Invite do it 'correctly activates accounts' do SiteSetting.stubs(:must_approve_users).returns(true) user = invite.redeem - user.approved?.should == true + expect(user.approved?).to eq(true) end end @@ -222,9 +224,9 @@ describe Invite do let!(:user) { invite.redeem } it 'works correctly' do - user.is_a?(User).should == true - user.send_welcome_message.should == true - user.trust_level.should == SiteSetting.default_invitee_trust_level + expect(user.is_a?(User)).to eq(true) + expect(user.send_welcome_message).to eq(true) + expect(user.trust_level).to eq(SiteSetting.default_invitee_trust_level) end context 'after redeeming' do @@ -234,10 +236,10 @@ describe Invite do it 'works correctly' do # has set the user_id attribute - invite.user.should == user + expect(invite.user).to eq(user) # returns true for redeemed - invite.should be_redeemed + expect(invite).to be_redeemed end @@ -248,7 +250,7 @@ describe Invite do end it 'will not redeem twice' do - invite.redeem.should be_blank + expect(invite.redeem).to be_blank end end @@ -258,8 +260,8 @@ describe Invite do end it 'will not redeem twice' do - invite.redeem.should be_present - invite.redeem.send_welcome_message.should == false + expect(invite.redeem).to be_present + expect(invite.redeem.send_welcome_message).to eq(false) end end end @@ -278,8 +280,8 @@ describe Invite do it 'adds the user to the topic_users' do user = invite.redeem topic.reload - topic.allowed_users.include?(user).should == true - Guardian.new(user).can_see?(topic).should == true + expect(topic.allowed_users.include?(user)).to eq(true) + expect(Guardian.new(user).can_see?(topic)).to eq(true) end end @@ -291,7 +293,7 @@ describe Invite do it 'adds the user to the topic_users' do topic.reload - topic.allowed_users.include?(user).should == true + expect(topic.allowed_users.include?(user)).to eq(true) end end @@ -303,19 +305,19 @@ describe Invite do let(:another_topic) { Fabricate(:topic, category_id: nil, archetype: "private_message", user: coding_horror) } it 'adds the user to the topic_users of the first topic' do - topic.allowed_users.include?(user).should == true - another_topic.allowed_users.include?(user).should == true + expect(topic.allowed_users.include?(user)).to eq(true) + expect(another_topic.allowed_users.include?(user)).to eq(true) another_invite.reload - another_invite.should_not be_redeemed + expect(another_invite).not_to be_redeemed end context 'if they redeem the other invite afterwards' do it 'returns the same user' do result = another_invite.redeem - result.should == user + expect(result).to eq(user) another_invite.reload - another_invite.should be_redeemed + expect(another_invite).to be_redeemed end end @@ -366,7 +368,7 @@ describe Invite do invites = Invite.find_redeemed_invites_from(inviter) - invites.size.should == 1 + expect(invites.size).to eq(1) expect(invites.first).to eq redeemed_invite end end @@ -376,22 +378,22 @@ describe Invite do subject { described_class.invalidate_for_email(email) } it 'returns nil if there is no invite for the given email' do - subject.should == nil + expect(subject).to eq(nil) end it 'sets the matching invite to be invalid' do invite = Fabricate(:invite, invited_by: Fabricate(:user), user_id: nil, email: email) - subject.should == invite - subject.link_valid?.should == false - subject.should be_valid + expect(subject).to eq(invite) + expect(subject.link_valid?).to eq(false) + expect(subject).to be_valid end it 'sets the matching invite to be invalid without being case-sensitive' do invite = Fabricate(:invite, invited_by: Fabricate(:user), user_id: nil, email: 'invite.me2@Example.COM') result = described_class.invalidate_for_email('invite.me2@EXAMPLE.com') - result.should == invite - result.link_valid?.should == false - result.should be_valid + expect(result).to eq(invite) + expect(result.link_valid?).to eq(false) + expect(result).to be_valid end end @@ -403,13 +405,13 @@ describe Invite do it 'redeems the invite from email' do result = Invite.redeem_from_email(user.email) invite.reload - invite.should be_redeemed + expect(invite).to be_redeemed end it 'does not redeem the invite if email does not match' do result = Invite.redeem_from_email('test24@example.com') invite.reload - invite.should_not be_redeemed + expect(invite).not_to be_redeemed end end @@ -422,13 +424,13 @@ describe Invite do it 'redeems the invite from token' do result = Invite.redeem_from_token(invite.invite_key, user.email) invite.reload - invite.should be_redeemed + expect(invite).to be_redeemed end it 'does not redeem the invite if token does not match' do result = Invite.redeem_from_token("bae0071f995bb4b6f756e80b383778b5", user.email) invite.reload - invite.should_not be_redeemed + expect(invite).not_to be_redeemed end end diff --git a/spec/models/notification_spec.rb b/spec/models/notification_spec.rb index d6b4da4cc7..f23085650f 100644 --- a/spec/models/notification_spec.rb +++ b/spec/models/notification_spec.rb @@ -5,11 +5,11 @@ describe Notification do ActiveRecord::Base.observers.enable :all end - it { should validate_presence_of :notification_type } - it { should validate_presence_of :data } + it { is_expected.to validate_presence_of :notification_type } + it { is_expected.to validate_presence_of :data } - it { should belong_to :user } - it { should belong_to :topic } + it { is_expected.to belong_to :user } + it { is_expected.to belong_to :topic } describe 'post' do let(:topic) { Fabricate(:topic) } @@ -30,17 +30,17 @@ describe Notification do it 'notifies the poster on reply' do - lambda { + expect { reply = Fabricate(:basic_reply, user: coding_horror, topic: post.topic) process_alerts(reply) - }.should change(post.user.notifications, :count).by(1) + }.to change(post.user.notifications, :count).by(1) end it "doesn't notify the poster when they reply to their own post" do - lambda { + expect { reply = Fabricate(:basic_reply, user: post.user, topic: post.topic) process_alerts(reply) - }.should_not change(post.user.notifications, :count) + }.not_to change(post.user.notifications, :count) end end @@ -49,9 +49,9 @@ describe Notification do post = PostAlerter.post_created(Fabricate(:post, post_args)) user2 = Fabricate(:coding_horror) post_args[:topic].notify_watch!(user2) - lambda { + expect { PostAlerter.post_created(Fabricate(:post, user: post.user, topic: post.topic)) - }.should change(user2.notifications, :count).by(1) + }.to change(user2.notifications, :count).by(1) end end @@ -62,9 +62,9 @@ describe Notification do user2 = Fabricate(:coding_horror) post_args[:topic].notify_muted!(user) - lambda { + expect { Fabricate(:post, user: user2, topic: post.topic, raw: 'hello @' + user.username) - }.should change(user.notifications, :count).by(0) + }.to change(user.notifications, :count).by(0) end end @@ -75,29 +75,29 @@ describe Notification do context 'a regular notification' do it 'increases unread_notifications' do - lambda { Fabricate(:notification, user: user); user.reload }.should change(user, :unread_notifications) + expect { Fabricate(:notification, user: user); user.reload }.to change(user, :unread_notifications) end it 'increases total_unread_notifications' do - lambda { Fabricate(:notification, user: user); user.reload }.should change(user, :total_unread_notifications) + expect { Fabricate(:notification, user: user); user.reload }.to change(user, :total_unread_notifications) end it "doesn't increase unread_private_messages" do - lambda { Fabricate(:notification, user: user); user.reload }.should_not change(user, :unread_private_messages) + expect { Fabricate(:notification, user: user); user.reload }.not_to change(user, :unread_private_messages) end end context 'a private message' do it "doesn't increase unread_notifications" do - lambda { Fabricate(:private_message_notification, user: user); user.reload }.should_not change(user, :unread_notifications) + expect { Fabricate(:private_message_notification, user: user); user.reload }.not_to change(user, :unread_notifications) end it 'increases total_unread_notifications' do - lambda { Fabricate(:notification, user: user); user.reload }.should change(user, :total_unread_notifications) + expect { Fabricate(:notification, user: user); user.reload }.to change(user, :total_unread_notifications) end it "increases unread_private_messages" do - lambda { Fabricate(:private_message_notification, user: user); user.reload }.should change(user, :unread_private_messages) + expect { Fabricate(:private_message_notification, user: user); user.reload }.to change(user, :unread_private_messages) end end @@ -147,14 +147,14 @@ describe Notification do end it 'should create and rollup private message notifications' do - @target.notifications.first.notification_type.should == Notification.types[:private_message] - @post.user.unread_notifications.should == 0 - @post.user.total_unread_notifications.should == 0 - @target.unread_private_messages.should == 1 + expect(@target.notifications.first.notification_type).to eq(Notification.types[:private_message]) + expect(@post.user.unread_notifications).to eq(0) + expect(@post.user.total_unread_notifications).to eq(0) + expect(@target.unread_private_messages).to eq(1) Fabricate(:post, topic: @topic, user: @topic.user) @target.reload - @target.unread_private_messages.should == 1 + expect(@target.unread_private_messages).to eq(1) end @@ -166,7 +166,7 @@ describe Notification do let!(:notification) { Fabricate(:notification, user: post.user, topic: post.topic, post_number: post.post_number) } it 'returns the post' do - notification.post.should == post + expect(notification.post).to eq(post) end end @@ -175,11 +175,11 @@ describe Notification do let(:notification) { Fabricate.build(:notification) } it 'should have a data hash' do - notification.data_hash.should be_present + expect(notification.data_hash).to be_present end it 'should have the data within the json' do - notification.data_hash[:poison].should == 'ivy' + expect(notification.data_hash[:poison]).to eq('ivy') end end @@ -187,16 +187,18 @@ describe Notification do it 'correctly updates the read state' do user = Fabricate(:user) + t = Fabricate(:topic) + Notification.create!(read: false, user_id: user.id, - topic_id: 2, + topic_id: t.id, post_number: 1, data: '{}', notification_type: Notification.types[:private_message]) other = Notification.create!(read: false, user_id: user.id, - topic_id: 2, + topic_id: t.id, post_number: 1, data: '{}', notification_type: Notification.types[:mentioned]) @@ -205,9 +207,9 @@ describe Notification do user.saw_notification_id(other.id) user.reload - user.unread_notifications.should == 0 - user.total_unread_notifications.should == 2 - user.unread_private_messages.should == 1 + expect(user.unread_notifications).to eq(0) + expect(user.total_unread_notifications).to eq(2) + expect(user.unread_private_messages).to eq(1) end end @@ -220,7 +222,7 @@ describe Notification do end Notification.create!(read: true, user_id: user.id, topic_id: 2, post_number: 4, data: '{}', notification_type: 1) - Notification.mark_posts_read(user,2,[1,2,3,4]).should == 3 + expect(Notification.mark_posts_read(user,2,[1,2,3,4])).to eq(3) end end @@ -244,7 +246,7 @@ describe Notification do # we may want to make notification "trashable" but for now we nuke pm notifications from deleted topics/posts Notification.ensure_consistency! - Notification.count.should == 2 + expect(Notification.count).to eq(2) end end @@ -284,7 +286,7 @@ describe Notification do # bumps unread pms to front of list notifications = Notification.recent_report(user, 3) - notifications.map{|n| n.id}.should == [a.id, d.id, c.id] + expect(notifications.map{|n| n.id}).to eq([a.id, d.id, c.id]) end end diff --git a/spec/models/optimized_image_spec.rb b/spec/models/optimized_image_spec.rb index 3dfdb0cc9f..9df31c7e3b 100644 --- a/spec/models/optimized_image_spec.rb +++ b/spec/models/optimized_image_spec.rb @@ -16,7 +16,7 @@ describe OptimizedImage do it "returns nil" do OptimizedImage.expects(:resize).returns(false) - OptimizedImage.create_for(upload, 100, 200).should == nil + expect(OptimizedImage.create_for(upload, 100, 200)).to eq(nil) end end @@ -39,11 +39,11 @@ describe OptimizedImage do it "works" do oi = OptimizedImage.create_for(upload, 100, 200) - oi.sha1.should == "da39a3ee5e6b4b0d3255bfef95601890afd80709" - oi.extension.should == ".png" - oi.width.should == 100 - oi.height.should == 200 - oi.url.should == "/internally/stored/optimized/image.png" + expect(oi.sha1).to eq("da39a3ee5e6b4b0d3255bfef95601890afd80709") + expect(oi.extension).to eq(".png") + expect(oi.width).to eq(100) + expect(oi.height).to eq(200) + expect(oi.url).to eq("/internally/stored/optimized/image.png") end end @@ -59,7 +59,7 @@ describe OptimizedImage do it "returns nil" do OptimizedImage.expects(:resize).returns(false) - OptimizedImage.create_for(upload, 100, 200).should == nil + expect(OptimizedImage.create_for(upload, 100, 200)).to eq(nil) end end @@ -71,18 +71,18 @@ describe OptimizedImage do end it "downloads a copy of the original image" do - Tempfile.any_instance.expects(:close!).twice + Tempfile.any_instance.expects(:close!) store.expects(:download).with(upload).returns(Tempfile.new(["discourse-external", ".png"])) OptimizedImage.create_for(upload, 100, 200) end it "works" do oi = OptimizedImage.create_for(upload, 100, 200) - oi.sha1.should == "da39a3ee5e6b4b0d3255bfef95601890afd80709" - oi.extension.should == ".png" - oi.width.should == 100 - oi.height.should == 200 - oi.url.should == "/externally/stored/optimized/image.png" + expect(oi.sha1).to eq("da39a3ee5e6b4b0d3255bfef95601890afd80709") + expect(oi.extension).to eq(".png") + expect(oi.width).to eq(100) + expect(oi.height).to eq(200) + expect(oi.url).to eq("/externally/stored/optimized/image.png") end end diff --git a/spec/models/permalink_spec.rb b/spec/models/permalink_spec.rb index b324caf695..68844219f2 100644 --- a/spec/models/permalink_spec.rb +++ b/spec/models/permalink_spec.rb @@ -5,12 +5,12 @@ describe Permalink do describe "new record" do it "strips blanks" do permalink = described_class.create(url: " my/old/url ") - permalink.url.should == "my/old/url" + expect(permalink.url).to eq("my/old/url") end it "removes leading slash" do permalink = described_class.create(url: "/my/old/url") - permalink.url.should == "my/old/url" + expect(permalink.url).to eq("my/old/url") end end @@ -24,49 +24,49 @@ describe Permalink do it "returns a topic url when topic_id is set" do permalink.topic_id = topic.id - target_url.should == topic.relative_url + expect(target_url).to eq(topic.relative_url) end it "returns nil when topic_id is set but topic is not found" do permalink.topic_id = 99999 - target_url.should == nil + expect(target_url).to eq(nil) end it "returns a post url when post_id is set" do permalink.post_id = post.id - target_url.should == post.url + expect(target_url).to eq(post.url) end it "returns nil when post_id is set but post is not found" do permalink.post_id = 99999 - target_url.should == nil + expect(target_url).to eq(nil) end it "returns a post url when post_id and topic_id are both set" do permalink.post_id = post.id permalink.topic_id = topic.id - target_url.should == post.url + expect(target_url).to eq(post.url) end it "returns a category url when category_id is set" do permalink.category_id = category.id - target_url.should == category.url + expect(target_url).to eq(category.url) end it "returns nil when category_id is set but category is not found" do permalink.category_id = 99999 - target_url.should == nil + expect(target_url).to eq(nil) end it "returns a post url when topic_id, post_id, and category_id are all set for some reason" do permalink.post_id = post.id permalink.topic_id = topic.id permalink.category_id = category.id - target_url.should == post.url + expect(target_url).to eq(post.url) end it "returns nil when nothing is set" do - target_url.should == nil + expect(target_url).to eq(nil) end end end diff --git a/spec/models/post_action_spec.rb b/spec/models/post_action_spec.rb index f150153832..b6fb5e8361 100644 --- a/spec/models/post_action_spec.rb +++ b/spec/models/post_action_spec.rb @@ -2,10 +2,10 @@ require 'spec_helper' require_dependency 'post_destroyer' describe PostAction do - it { should belong_to :user } - it { should belong_to :post } - it { should belong_to :post_action_type } - it { should rate_limit } + it { is_expected.to belong_to :user } + it { is_expected.to belong_to :post } + it { is_expected.to belong_to :post_action_type } + it { is_expected.to rate_limit } let(:moderator) { Fabricate(:moderator) } let(:codinghorror) { Fabricate(:coding_horror) } @@ -17,6 +17,12 @@ describe PostAction do describe "messaging" do + it "doesn't generate title longer than 255 characters" do + topic = create_topic(title: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc sit amet rutrum neque. Pellentesque suscipit vehicula facilisis. Phasellus lacus sapien, aliquam nec convallis sit amet, vestibulum laoreet ante. Curabitur et pellentesque tortor. Donec non.") + post = create_post(topic: topic) + expect { PostAction.act(admin, post, PostActionType.types[:notify_user], message: "WAT") }.not_to raise_error + end + it "notify moderators integration test" do post = create_post mod = moderator @@ -29,37 +35,37 @@ describe PostAction do .where('topics.archetype' => Archetype.private_message) .to_a - posts.count.should == 1 - action.related_post_id.should == posts[0].id.to_i - posts[0].subtype.should == TopicSubtype.notify_moderators + expect(posts.count).to eq(1) + expect(action.related_post_id).to eq(posts[0].id.to_i) + expect(posts[0].subtype).to eq(TopicSubtype.notify_moderators) topic = posts[0].topic # Moderators should be invited to the private topic, otherwise they're not permitted to see it topic_user_ids = topic.topic_users(true).map {|x| x.user_id} - topic_user_ids.should include(codinghorror.id) - topic_user_ids.should include(mod.id) + expect(topic_user_ids).to include(codinghorror.id) + expect(topic_user_ids).to include(mod.id) # Notification level should be "Watching" for everyone - topic.topic_users(true).map(&:notification_level).uniq.should == [TopicUser.notification_levels[:watching]] + expect(topic.topic_users(true).map(&:notification_level).uniq).to eq([TopicUser.notification_levels[:watching]]) # reply to PM should not clear flag PostCreator.new(mod, topic_id: posts[0].topic_id, raw: "This is my test reply to the user, it should clear flags").create action.reload - action.deleted_at.should == nil + expect(action.deleted_at).to eq(nil) # Acting on the flag should post an automated status message - topic.posts.count.should == 2 + expect(topic.posts.count).to eq(2) PostAction.agree_flags!(post, admin) topic.reload - topic.posts.count.should == 3 - topic.posts.last.post_type.should == Post.types[:moderator_action] + expect(topic.posts.count).to eq(3) + expect(topic.posts.last.post_type).to eq(Post.types[:moderator_action]) # Clearing the flags should not post another automated status message PostAction.act(mod, post, PostActionType.types[:notify_moderators], message: "another special message") PostAction.clear_flags!(post, admin) topic.reload - topic.posts.count.should == 3 + expect(topic.posts.count).to eq(3) end describe 'notify_moderators' do @@ -94,39 +100,39 @@ describe PostAction do end it "increments the numbers correctly" do - PostAction.flagged_posts_count.should == 0 + expect(PostAction.flagged_posts_count).to eq(0) PostAction.act(codinghorror, post, PostActionType.types[:off_topic]) - PostAction.flagged_posts_count.should == 1 + expect(PostAction.flagged_posts_count).to eq(1) PostAction.clear_flags!(post, Discourse.system_user) - PostAction.flagged_posts_count.should == 0 + expect(PostAction.flagged_posts_count).to eq(0) end it "should reset counts when a topic is deleted" do PostAction.act(codinghorror, post, PostActionType.types[:off_topic]) post.topic.trash! - PostAction.flagged_posts_count.should == 0 + expect(PostAction.flagged_posts_count).to eq(0) end it "should ignore validated flags" do post = create_post PostAction.act(codinghorror, post, PostActionType.types[:off_topic]) - post.hidden.should == false - post.hidden_at.should be_blank + expect(post.hidden).to eq(false) + expect(post.hidden_at).to be_blank PostAction.defer_flags!(post, admin) - PostAction.flagged_posts_count.should == 0 + expect(PostAction.flagged_posts_count).to eq(0) post.reload - post.hidden.should == false - post.hidden_at.should be_blank + expect(post.hidden).to eq(false) + expect(post.hidden_at).to be_blank PostAction.hide_post!(post, PostActionType.types[:off_topic]) post.reload - post.hidden.should == true - post.hidden_at.should be_present + expect(post.hidden).to eq(true) + expect(post.hidden_at).to be_present end end @@ -134,22 +140,30 @@ describe PostAction do describe "update_counters" do it "properly updates topic counters" do + # we need this to test it + TopicUser.change(codinghorror, post.topic, posted: true) + PostAction.act(moderator, post, PostActionType.types[:like]) PostAction.act(codinghorror, second_post, PostActionType.types[:like]) post.topic.reload - post.topic.like_count.should == 2 + expect(post.topic.like_count).to eq(2) + + tu = TopicUser.get(post.topic, codinghorror) + expect(tu.liked).to be true + expect(tu.bookmarked).to be false + end end describe "when a user bookmarks something" do it "increases the post's bookmark count when saved" do - lambda { bookmark.save; post.reload }.should change(post, :bookmark_count).by(1) + expect { bookmark.save; post.reload }.to change(post, :bookmark_count).by(1) end it "increases the forum topic's bookmark count when saved" do - lambda { bookmark.save; post.topic.reload }.should change(post.topic, :bookmark_count).by(1) + expect { bookmark.save; post.topic.reload }.to change(post.topic, :bookmark_count).by(1) end describe 'when deleted' do @@ -164,11 +178,11 @@ describe PostAction do end it 'reduces the bookmark count of the post' do - lambda { post.reload }.should change(post, :bookmark_count).by(-1) + expect { post.reload }.to change(post, :bookmark_count).by(-1) end it 'reduces the bookmark count of the forum topic' do - lambda { @topic.reload }.should change(post.topic, :bookmark_count).by(-1) + expect { @topic.reload }.to change(post.topic, :bookmark_count).by(-1) end end end @@ -177,27 +191,27 @@ describe PostAction do it 'should increase the `like_count` and `like_score` when a user likes something' do PostAction.act(codinghorror, post, PostActionType.types[:like]) post.reload - post.like_count.should == 1 - post.like_score.should == 1 + expect(post.like_count).to eq(1) + expect(post.like_score).to eq(1) post.topic.reload - post.topic.like_count.should == 1 + expect(post.topic.like_count).to eq(1) # When a staff member likes it PostAction.act(moderator, post, PostActionType.types[:like]) post.reload - post.like_count.should == 2 - post.like_score.should == 4 + expect(post.like_count).to eq(2) + expect(post.like_score).to eq(4) # Removing likes PostAction.remove_act(codinghorror, post, PostActionType.types[:like]) post.reload - post.like_count.should == 1 - post.like_score.should == 3 + expect(post.like_count).to eq(1) + expect(post.like_score).to eq(3) PostAction.remove_act(moderator, post, PostActionType.types[:like]) post.reload - post.like_count.should == 0 - post.like_score.should == 0 + expect(post.like_count).to eq(0) + expect(post.like_score).to eq(0) end end @@ -206,27 +220,27 @@ describe PostAction do PostAction.act(codinghorror, post, PostActionType.types[:like]) PostAction.remove_act(codinghorror, post, PostActionType.types[:like]) PostAction.act(codinghorror, post, PostActionType.types[:like]) - PostAction.where(post: post).with_deleted.count.should == 1 + expect(PostAction.where(post: post).with_deleted.count).to eq(1) PostAction.remove_act(codinghorror, post, PostActionType.types[:like]) # Check that we don't lose consistency into negatives - post.reload.like_count.should == 0 + expect(post.reload.like_count).to eq(0) end end describe 'when a user votes for something' do it 'should increase the vote counts when a user votes' do - lambda { + expect { PostAction.act(codinghorror, post, PostActionType.types[:vote]) post.reload - }.should change(post, :vote_count).by(1) + }.to change(post, :vote_count).by(1) end it 'should increase the forum topic vote count when a user votes' do - lambda { + expect { PostAction.act(codinghorror, post, PostActionType.types[:vote]) post.topic.reload - }.should change(post.topic, :vote_count).by(1) + }.to change(post.topic, :vote_count).by(1) end end @@ -239,32 +253,32 @@ describe PostAction do SiteSetting.stubs(:flags_required_to_hide_post).returns(7) # A post with no flags has 0 for flag counts - PostAction.flag_counts_for(post.id).should == [0, 0] + expect(PostAction.flag_counts_for(post.id)).to eq([0, 0]) flag = PostAction.act(eviltrout, post, PostActionType.types[:spam]) - PostAction.flag_counts_for(post.id).should == [0, 1] + expect(PostAction.flag_counts_for(post.id)).to eq([0, 1]) # If staff takes action, it is ranked higher PostAction.act(admin, post, PostActionType.types[:spam], take_action: true) - PostAction.flag_counts_for(post.id).should == [0, 8] + expect(PostAction.flag_counts_for(post.id)).to eq([0, 8]) # If a flag is dismissed PostAction.clear_flags!(post, admin) - PostAction.flag_counts_for(post.id).should == [8, 0] + expect(PostAction.flag_counts_for(post.id)).to eq([8, 0]) end end it 'does not allow you to flag stuff with the same reason more than once' do post = Fabricate(:post) PostAction.act(eviltrout, post, PostActionType.types[:spam]) - lambda { PostAction.act(eviltrout, post, PostActionType.types[:off_topic]) }.should raise_error(PostAction::AlreadyActed) + expect { PostAction.act(eviltrout, post, PostActionType.types[:off_topic]) }.to raise_error(PostAction::AlreadyActed) end it 'allows you to flag stuff with another reason' do post = Fabricate(:post) PostAction.act(eviltrout, post, PostActionType.types[:spam]) PostAction.remove_act(eviltrout, post, PostActionType.types[:spam]) - lambda { PostAction.act(eviltrout, post, PostActionType.types[:off_topic]) }.should_not raise_error() + expect { PostAction.act(eviltrout, post, PostActionType.types[:off_topic]) }.not_to raise_error() end it 'should update counts when you clear flags' do @@ -272,12 +286,12 @@ describe PostAction do PostAction.act(eviltrout, post, PostActionType.types[:spam]) post.reload - post.spam_count.should == 1 + expect(post.spam_count).to eq(1) PostAction.clear_flags!(post, Discourse.system_user) post.reload - post.spam_count.should == 0 + expect(post.spam_count).to eq(0) end it 'should follow the rules for automatic hiding workflow' do @@ -292,38 +306,38 @@ describe PostAction do post.reload - post.hidden.should == true - post.hidden_at.should be_present - post.hidden_reason_id.should == Post.hidden_reasons[:flag_threshold_reached] - post.topic.visible.should == false + expect(post.hidden).to eq(true) + expect(post.hidden_at).to be_present + expect(post.hidden_reason_id).to eq(Post.hidden_reasons[:flag_threshold_reached]) + expect(post.topic.visible).to eq(false) post.revise(post.user, { raw: post.raw + " ha I edited it " }) post.reload - post.hidden.should == false - post.hidden_reason_id.should == nil - post.hidden_at.should be_blank - post.topic.visible.should == true + expect(post.hidden).to eq(false) + expect(post.hidden_reason_id).to eq(nil) + expect(post.hidden_at).to be_blank + expect(post.topic.visible).to eq(true) PostAction.act(eviltrout, post, PostActionType.types[:spam]) PostAction.act(walterwhite, post, PostActionType.types[:off_topic]) post.reload - post.hidden.should == true - post.hidden_at.should be_present - post.hidden_reason_id.should == Post.hidden_reasons[:flag_threshold_reached_again] - post.topic.visible.should == false + expect(post.hidden).to eq(true) + expect(post.hidden_at).to be_present + expect(post.hidden_reason_id).to eq(Post.hidden_reasons[:flag_threshold_reached_again]) + expect(post.topic.visible).to eq(false) post.revise(post.user, { raw: post.raw + " ha I edited it again " }) post.reload - post.hidden.should == true - post.hidden_at.should be_present - post.hidden_reason_id.should == Post.hidden_reasons[:flag_threshold_reached_again] - post.topic.visible.should == false + expect(post.hidden).to eq(true) + expect(post.hidden_at).to be_present + expect(post.hidden_reason_id).to eq(Post.hidden_reasons[:flag_threshold_reached_again]) + expect(post.topic.visible).to eq(false) end it "hide tl0 posts that are flagged as spam by a tl3 user" do @@ -336,23 +350,23 @@ describe PostAction do post.reload - post.hidden.should == true - post.hidden_at.should be_present - post.hidden_reason_id.should == Post.hidden_reasons[:flagged_by_tl3_user] + expect(post.hidden).to eq(true) + expect(post.hidden_at).to be_present + expect(post.hidden_reason_id).to eq(Post.hidden_reasons[:flagged_by_tl3_user]) end it "can flag the topic instead of a post" do post1 = create_post post2 = create_post(topic: post1.topic) post_action = PostAction.act(Fabricate(:user), post1, PostActionType.types[:spam], { flag_topic: true }) - post_action.targets_topic.should == true + expect(post_action.targets_topic).to eq(true) end it "will flag the first post if you flag a topic but there is only one post in the topic" do post = create_post post_action = PostAction.act(Fabricate(:user), post, PostActionType.types[:spam], { flag_topic: true }) - post_action.targets_topic.should == false - post_action.post_id.should == post.id + expect(post_action.targets_topic).to eq(false) + expect(post_action.post_id).to eq(post.id) end it "will unhide the post when a moderator undos the flag on which s/he took action" do @@ -362,12 +376,56 @@ describe PostAction do PostAction.act(moderator, post, PostActionType.types[:spam], { take_action: true }) post.reload - post.hidden.should == true + expect(post.hidden).to eq(true) PostAction.remove_act(moderator, post, PostActionType.types[:spam]) post.reload - post.hidden.should == false + expect(post.hidden).to eq(false) + end + + it "will automatically close a topic due to large community flagging" do + SiteSetting.stubs(:flags_required_to_hide_post).returns(0) + + SiteSetting.stubs(:num_flags_to_close_topic).returns(3) + SiteSetting.stubs(:num_flaggers_to_close_topic).returns(2) + + topic = Fabricate(:topic) + post1 = create_post(topic: topic) + post2 = create_post(topic: topic) + post3 = create_post(topic: topic) + + flagger1 = Fabricate(:user) + flagger2 = Fabricate(:user) + + # reaching `num_flaggers_to_close_topic` isn't enough + [flagger1, flagger2].each do |flagger| + PostAction.act(flagger, post1, PostActionType.types[:inappropriate]) + end + + expect(topic.reload.closed).to eq(false) + + # clean up + PostAction.where(post: post1).delete_all + + # reaching `num_flags_to_close_topic` isn't enough + [post1, post2, post3].each do |post| + PostAction.act(flagger1, post, PostActionType.types[:inappropriate]) + end + + expect(topic.reload.closed).to eq(false) + + # clean up + PostAction.where(post: [post1, post2, post3]).delete_all + + # reaching both should close the topic + [flagger1, flagger2].each do |flagger| + [post1, post2, post3].each do |post| + PostAction.act(flagger, post, PostActionType.types[:inappropriate]) + end + end + + expect(topic.reload.closed).to eq(true) end end @@ -378,11 +436,26 @@ describe PostAction do # flags are already being tested all_types_except_flags = PostActionType.types.except(PostActionType.flag_types) all_types_except_flags.values.each do |action| - lambda do + expect do PostAction.act(eviltrout, post, action) PostAction.act(eviltrout, post, action) - end.should raise_error(PostAction::AlreadyActed) + end.to raise_error(PostAction::AlreadyActed) end end + describe "#create_message_for_post_action" do + it "does not create a message when there is no message" do + message_id = PostAction.create_message_for_post_action(Discourse.system_user, post, PostActionType.types[:spam], {}) + expect(message_id).to be_nil + end + + [:notify_moderators, :notify_user, :spam].each do |post_action_type| + it "creates a message for #{post_action_type}" do + message_id = PostAction.create_message_for_post_action(Discourse.system_user, post, PostActionType.types[post_action_type], message: "WAT") + expect(message_id).to be_present + end + end + + end + end diff --git a/spec/models/post_alert_observer_spec.rb b/spec/models/post_alert_observer_spec.rb index e61d84d04d..0b72771d60 100644 --- a/spec/models/post_alert_observer_spec.rb +++ b/spec/models/post_alert_observer_spec.rb @@ -13,10 +13,10 @@ describe PostAlertObserver do context 'liking' do context 'when liking a post' do it 'creates a notification' do - lambda { + expect { PostAction.act(evil_trout, post, PostActionType.types[:like]) # one like (welcome badge deferred) - }.should change(Notification, :count).by(1) + }.to change(Notification, :count).by(1) end end @@ -26,18 +26,18 @@ describe PostAlertObserver do end it 'removes a notification' do - lambda { + expect { PostAction.remove_act(evil_trout, post, PostActionType.types[:like]) - }.should change(Notification, :count).by(-1) + }.to change(Notification, :count).by(-1) end end end context 'when editing a post' do it 'notifies a user of the revision' do - lambda { + expect { post.revise(evil_trout, { raw: "world is the new body of the message" }) - }.should change(post.user.notifications, :count).by(1) + }.to change(post.user.notifications, :count).by(1) end context "edit notifications are disabled" do @@ -46,15 +46,15 @@ describe PostAlertObserver do it 'notifies a user of the revision made by another user' do - lambda { + expect { post.revise(evil_trout, { raw: "world is the new body of the message" }) - }.should change(post.user.notifications, :count).by(1) + }.to change(post.user.notifications, :count).by(1) end it 'does not notifiy a user of the revision made by the system user' do - lambda { + expect { post.revise(Discourse.system_user, { raw: "world is the new body of the message" }) - }.should_not change(post.user.notifications, :count) + }.not_to change(post.user.notifications, :count) end end @@ -71,20 +71,20 @@ describe PostAlertObserver do end it "won't notify someone who can't see the post" do - lambda { + expect { Guardian.any_instance.expects(:can_see?).with(instance_of(Post)).returns(false) mention_post PostAlerter.new.after_create_post(mention_post) PostAlerter.new.after_save_post(mention_post) - }.should_not change(evil_trout.notifications, :count) + }.not_to change(evil_trout.notifications, :count) end it 'creates like notifications' do other_user = Fabricate(:user) topic.allowed_users << user << other_user - lambda { + expect { PostAction.act(other_user, mention_post, PostActionType.types[:like]) - }.should change(user.notifications, :count) + }.to change(user.notifications, :count) end end diff --git a/spec/models/post_analyzer_spec.rb b/spec/models/post_analyzer_spec.rb index 92fb796fd4..a947490bc0 100644 --- a/spec/models/post_analyzer_spec.rb +++ b/spec/models/post_analyzer_spec.rb @@ -46,44 +46,44 @@ describe PostAnalyzer do describe "raw_links" do it "returns a blank collection for a post with no links" do post_analyzer = PostAnalyzer.new(raw_no_links, default_topic_id) - post_analyzer.raw_links.should be_blank + expect(post_analyzer.raw_links).to be_blank end it "finds a link within markdown" do post_analyzer = PostAnalyzer.new(raw_one_link_md, default_topic_id) - post_analyzer.raw_links.should == ["http://www.imdb.com/name/nm2225369"] + expect(post_analyzer.raw_links).to eq(["http://www.imdb.com/name/nm2225369"]) end it "can find two links from html" do post_analyzer = PostAnalyzer.new(raw_two_links_html, default_topic_id) - post_analyzer.raw_links.should == ["http://disneyland.disney.go.com/", "http://reddit.com"] + expect(post_analyzer.raw_links).to eq(["http://disneyland.disney.go.com/", "http://reddit.com"]) end it "can find three links without markup" do post_analyzer = PostAnalyzer.new(raw_three_links, default_topic_id) - post_analyzer.raw_links.should == ["http://discourse.org", "http://discourse.org/another_url", "http://www.imdb.com/name/nm2225369"] + expect(post_analyzer.raw_links).to eq(["http://discourse.org", "http://discourse.org/another_url", "http://www.imdb.com/name/nm2225369"]) end end describe "linked_hosts" do it "returns blank with no links" do post_analyzer = PostAnalyzer.new(raw_no_links, default_topic_id) - post_analyzer.linked_hosts.should be_blank + expect(post_analyzer.linked_hosts).to be_blank end it "returns the host and a count for links" do post_analyzer = PostAnalyzer.new(raw_two_links_html, default_topic_id) - post_analyzer.linked_hosts.should == {"disneyland.disney.go.com" => 1, "reddit.com" => 1} + expect(post_analyzer.linked_hosts).to eq({"disneyland.disney.go.com" => 1, "reddit.com" => 1}) end it "it counts properly with more than one link on the same host" do post_analyzer = PostAnalyzer.new(raw_three_links, default_topic_id) - post_analyzer.linked_hosts.should == {"discourse.org" => 1, "www.imdb.com" => 1} + expect(post_analyzer.linked_hosts).to eq({"discourse.org" => 1, "www.imdb.com" => 1}) end it 'returns blank for ipv6 output' do post_analyzer = PostAnalyzer.new('PING www.google.com(lb-in-x93.1e100.net) 56 data bytes', default_topic_id) - post_analyzer.linked_hosts.should be_blank + expect(post_analyzer.linked_hosts).to be_blank end end end @@ -98,38 +98,38 @@ describe PostAnalyzer do it "returns 0 images for an empty post" do post_analyzer = PostAnalyzer.new("Hello world", nil) - post_analyzer.image_count.should == 0 + expect(post_analyzer.image_count).to eq(0) end it "finds images from markdown" do post_analyzer = PostAnalyzer.new(raw_post_one_image_md, default_topic_id) - post_analyzer.image_count.should == 1 + expect(post_analyzer.image_count).to eq(1) end it "finds images from HTML" do post_analyzer = PostAnalyzer.new(raw_post_two_images_html, default_topic_id) - post_analyzer.image_count.should == 2 + expect(post_analyzer.image_count).to eq(2) end it "doesn't count avatars as images" do post_analyzer = PostAnalyzer.new(raw_post_with_avatars, default_topic_id) - post_analyzer.image_count.should == 0 + expect(post_analyzer.image_count).to eq(0) end it "doesn't count favicons as images" do post_analyzer = PostAnalyzer.new(raw_post_with_favicon, default_topic_id) - post_analyzer.image_count.should == 0 + expect(post_analyzer.image_count).to eq(0) end it "doesn't count thumbnails as images" do post_analyzer = PostAnalyzer.new(raw_post_with_thumbnail, default_topic_id) - post_analyzer.image_count.should == 0 + expect(post_analyzer.image_count).to eq(0) end it "doesn't count whitelisted images" do Post.stubs(:white_listed_image_classes).returns(["classy"]) post_analyzer = PostAnalyzer.new(raw_post_with_two_classy_images, default_topic_id) - post_analyzer.image_count.should == 0 + expect(post_analyzer.image_count).to eq(0) end end @@ -140,23 +140,23 @@ describe PostAnalyzer do it "returns 0 links for an empty post" do post_analyzer = PostAnalyzer.new("Hello world", nil) - post_analyzer.link_count.should == 0 + expect(post_analyzer.link_count).to eq(0) end it "returns 0 links for a post with mentions" do post_analyzer = PostAnalyzer.new(raw_post_with_mentions, default_topic_id) - post_analyzer.link_count.should == 0 + expect(post_analyzer.link_count).to eq(0) end it "finds links from markdown" do Oneboxer.stubs :onebox post_analyzer = PostAnalyzer.new(raw_post_one_link_md, default_topic_id) - post_analyzer.link_count.should == 1 + expect(post_analyzer.link_count).to eq(1) end it "finds links from HTML" do post_analyzer = PostAnalyzer.new(raw_post_two_links_html, default_topic_id) - post_analyzer.link_count.should == 2 + expect(post_analyzer.link_count).to eq(2) end end @@ -165,42 +165,42 @@ describe PostAnalyzer do it "returns an empty array with no matches" do post_analyzer = PostAnalyzer.new("Hello Jake and Finn!", default_topic_id) - post_analyzer.raw_mentions.should == [] + expect(post_analyzer.raw_mentions).to eq([]) end it "returns lowercase unique versions of the mentions" do post_analyzer = PostAnalyzer.new("@Jake @Finn @Jake", default_topic_id) - post_analyzer.raw_mentions.should == ['jake', 'finn'] + expect(post_analyzer.raw_mentions).to eq(['jake', 'finn']) end it "ignores pre" do post_analyzer = PostAnalyzer.new("
@Jake
@Finn", default_topic_id) - post_analyzer.raw_mentions.should == ['finn'] + expect(post_analyzer.raw_mentions).to eq(['finn']) end it "catches content between pre tags" do post_analyzer = PostAnalyzer.new("
hello
@Finn
", default_topic_id)
-      post_analyzer.raw_mentions.should == ['finn']
+      expect(post_analyzer.raw_mentions).to eq(['finn'])
     end
 
     it "ignores code" do
       post_analyzer = PostAnalyzer.new("@Jake `@Finn`", default_topic_id)
-      post_analyzer.raw_mentions.should == ['jake']
+      expect(post_analyzer.raw_mentions).to eq(['jake'])
     end
 
     it "ignores code in markdown-formatted code blocks" do
       post_analyzer = PostAnalyzer.new("    @Jake @Finn\n@Ryan", default_topic_id)
-      post_analyzer.raw_mentions.should == ['ryan']
+      expect(post_analyzer.raw_mentions).to eq(['ryan'])
     end
 
     it "ignores quotes" do
       post_analyzer = PostAnalyzer.new("[quote=\"Evil Trout\"]@Jake[/quote] @Finn", default_topic_id)
-      post_analyzer.raw_mentions.should == ['finn']
+      expect(post_analyzer.raw_mentions).to eq(['finn'])
     end
 
     it "handles underscore in username" do
       post_analyzer = PostAnalyzer.new("@Jake @Finn @Jake_Old", default_topic_id)
-      post_analyzer.raw_mentions.should == ['jake', 'finn', 'jake_old']
+      expect(post_analyzer.raw_mentions).to eq(['jake', 'finn', 'jake_old'])
     end
   end
 end
diff --git a/spec/models/post_detail_spec.rb b/spec/models/post_detail_spec.rb
index 9319bfcf6d..940b8a0a41 100644
--- a/spec/models/post_detail_spec.rb
+++ b/spec/models/post_detail_spec.rb
@@ -1,9 +1,9 @@
 require 'spec_helper'
 
 describe PostDetail do
-  it { should belong_to :post }
+  it { is_expected.to belong_to :post }
 
-  it { should validate_presence_of :key }
-  it { should validate_presence_of :value }
-  it { should validate_uniqueness_of(:key).scoped_to(:post_id) }
+  it { is_expected.to validate_presence_of :key }
+  it { is_expected.to validate_presence_of :value }
+  it { is_expected.to validate_uniqueness_of(:key).scoped_to(:post_id) }
 end
diff --git a/spec/models/post_mover_spec.rb b/spec/models/post_mover_spec.rb
index d94eff7266..d67ef6e6e0 100644
--- a/spec/models/post_mover_spec.rb
+++ b/spec/models/post_mover_spec.rb
@@ -36,14 +36,14 @@ describe PostMover do
     context "errors" do
 
       it "raises an error when one of the posts doesn't exist" do
-        lambda { topic.move_posts(user, [1003], title: "new testing topic name") }.should raise_error(Discourse::InvalidParameters)
+        expect { topic.move_posts(user, [1003], title: "new testing topic name") }.to raise_error(Discourse::InvalidParameters)
       end
 
       it "raises an error and does not create a topic if no posts were moved" do
         Topic.count.tap do |original_topic_count|
-          lambda {
+          expect {
             topic.move_posts(user, [], title: "new testing topic name")
-          }.should raise_error(Discourse::InvalidParameters)
+          }.to raise_error(Discourse::InvalidParameters)
 
           expect(Topic.count).to eq original_topic_count
         end
@@ -61,41 +61,41 @@ describe PostMover do
         let!(:new_topic) { topic.move_posts(user, [p2.id, p4.id], title: "new testing topic name", category_id: category.id) }
 
         it "works correctly" do
-          TopicUser.find_by(user_id: user.id, topic_id: topic.id).last_read_post_number.should == p3.post_number
+          expect(TopicUser.find_by(user_id: user.id, topic_id: topic.id).last_read_post_number).to eq(p3.post_number)
 
-          new_topic.should be_present
-          new_topic.featured_user1_id.should == another_user.id
-          new_topic.like_count.should == 1
+          expect(new_topic).to be_present
+          expect(new_topic.featured_user1_id).to eq(another_user.id)
+          expect(new_topic.like_count).to eq(1)
 
-          new_topic.category.should == category
-          topic.featured_user1_id.should be_blank
-          new_topic.posts.by_post_number.should =~ [p2, p4]
+          expect(new_topic.category).to eq(category)
+          expect(topic.featured_user1_id).to be_blank
+          expect(new_topic.posts.by_post_number).to match_array([p2, p4])
 
           new_topic.reload
-          new_topic.posts_count.should == 2
-          new_topic.highest_post_number.should == 2
-          new_topic.last_post_user_id.should == new_topic.posts.last.user_id
+          expect(new_topic.posts_count).to eq(2)
+          expect(new_topic.highest_post_number).to eq(2)
+          expect(new_topic.last_post_user_id).to eq(new_topic.posts.last.user_id)
           expect(new_topic.last_posted_at).to be_present
 
           p2.reload
-          p2.sort_order.should == 1
-          p2.post_number.should == 1
-          p2.topic_links.first.topic_id.should == new_topic.id
+          expect(p2.sort_order).to eq(1)
+          expect(p2.post_number).to eq(1)
+          expect(p2.topic_links.first.topic_id).to eq(new_topic.id)
 
           p4.reload
-          p4.post_number.should == 2
-          p4.sort_order.should == 2
+          expect(p4.post_number).to eq(2)
+          expect(p4.sort_order).to eq(2)
 
           topic.reload
-          topic.featured_user1_id.should be_blank
-          topic.like_count.should == 0
-          topic.posts_count.should == 2
-          topic.posts.by_post_number.should =~ [p1, p3]
-          topic.highest_post_number.should == p3.post_number
+          expect(topic.featured_user1_id).to be_blank
+          expect(topic.like_count).to eq(0)
+          expect(topic.posts_count).to eq(2)
+          expect(topic.posts.by_post_number).to match_array([p1, p3])
+          expect(topic.highest_post_number).to eq(p3.post_number)
 
           # both the like and was_liked user actions should be correct
           action = UserAction.find_by(user_id: another_user.id)
-          action.target_topic_id.should == new_topic.id
+          expect(action.target_topic_id).to eq(new_topic.id)
         end
       end
 
@@ -106,43 +106,43 @@ describe PostMover do
         let!(:moved_to) { topic.move_posts(user, [p2.id, p4.id], destination_topic_id: destination_topic.id)}
 
         it "works correctly" do
-          moved_to.should == destination_topic
+          expect(moved_to).to eq(destination_topic)
 
           # Check out new topic
           moved_to.reload
-          moved_to.posts_count.should == 3
-          moved_to.highest_post_number.should == 3
-          moved_to.featured_user1_id.should == another_user.id
-          moved_to.like_count.should == 1
-          moved_to.category_id.should == SiteSetting.uncategorized_category_id
+          expect(moved_to.posts_count).to eq(3)
+          expect(moved_to.highest_post_number).to eq(3)
+          expect(moved_to.featured_user1_id).to eq(another_user.id)
+          expect(moved_to.like_count).to eq(1)
+          expect(moved_to.category_id).to eq(SiteSetting.uncategorized_category_id)
 
           # Posts should be re-ordered
           p2.reload
-          p2.sort_order.should == 2
-          p2.post_number.should == 2
-          p2.topic_id.should == moved_to.id
-          p2.reply_count.should == 1
-          p2.reply_to_post_number.should == nil
+          expect(p2.sort_order).to eq(2)
+          expect(p2.post_number).to eq(2)
+          expect(p2.topic_id).to eq(moved_to.id)
+          expect(p2.reply_count).to eq(1)
+          expect(p2.reply_to_post_number).to eq(nil)
 
           p4.reload
-          p4.post_number.should == 3
-          p4.sort_order.should == 3
-          p4.topic_id.should == moved_to.id
-          p4.reply_count.should == 0
-          p4.reply_to_post_number.should == 2
+          expect(p4.post_number).to eq(3)
+          expect(p4.sort_order).to eq(3)
+          expect(p4.topic_id).to eq(moved_to.id)
+          expect(p4.reply_count).to eq(0)
+          expect(p4.reply_to_post_number).to eq(2)
 
           # Check out the original topic
           topic.reload
-          topic.posts_count.should == 2
-          topic.highest_post_number.should == 3
-          topic.featured_user1_id.should be_blank
-          topic.like_count.should == 0
-          topic.posts_count.should == 2
-          topic.posts.by_post_number.should =~ [p1, p3]
-          topic.highest_post_number.should == p3.post_number
+          expect(topic.posts_count).to eq(2)
+          expect(topic.highest_post_number).to eq(3)
+          expect(topic.featured_user1_id).to be_blank
+          expect(topic.like_count).to eq(0)
+          expect(topic.posts_count).to eq(2)
+          expect(topic.posts.by_post_number).to match_array([p1, p3])
+          expect(topic.highest_post_number).to eq(p3.post_number)
 
           # Should update last reads
-          TopicUser.find_by(user_id: user.id, topic_id: topic.id).last_read_post_number.should == p3.post_number
+          expect(TopicUser.find_by(user_id: user.id, topic_id: topic.id).last_read_post_number).to eq(p3.post_number)
         end
       end
 
@@ -151,36 +151,36 @@ describe PostMover do
         let!(:new_topic) { topic.move_posts(user, [p1.id, p2.id], title: "new testing topic name") }
 
         it "copies the OP, doesn't delete it" do
-          new_topic.should be_present
+          expect(new_topic).to be_present
           new_topic.posts.reload
-          new_topic.posts.by_post_number.first.raw.should == p1.raw
+          expect(new_topic.posts.by_post_number.first.raw).to eq(p1.raw)
 
           new_topic.reload
-          new_topic.posts_count.should == 2
-          new_topic.highest_post_number.should == 2
+          expect(new_topic.posts_count).to eq(2)
+          expect(new_topic.highest_post_number).to eq(2)
 
           # First post didn't move
           p1.reload
-          p1.sort_order.should == 1
-          p1.post_number.should == 1
+          expect(p1.sort_order).to eq(1)
+          expect(p1.post_number).to eq(1)
           p1.topic_id == topic.id
-          p1.reply_count.should == 0
+          expect(p1.reply_count).to eq(0)
 
           # New first post
           new_first = new_topic.posts.where(post_number: 1).first
-          new_first.reply_count.should == 1
+          expect(new_first.reply_count).to eq(1)
 
           # Second post is in a new topic
           p2.reload
-          p2.post_number.should == 2
-          p2.sort_order.should == 2
+          expect(p2.post_number).to eq(2)
+          expect(p2.sort_order).to eq(2)
           p2.topic_id == new_topic.id
-          p2.reply_to_post_number.should == 1
-          p2.reply_count.should == 0
+          expect(p2.reply_to_post_number).to eq(1)
+          expect(p2.reply_count).to eq(0)
 
           topic.reload
-          topic.posts.by_post_number.should =~ [p1, p3, p4]
-          topic.highest_post_number.should == p4.post_number
+          expect(topic.posts.by_post_number).to match_array([p1, p3, p4])
+          expect(topic.highest_post_number).to eq(p4.post_number)
         end
 
       end
@@ -195,26 +195,26 @@ describe PostMover do
         it "works correctly" do
           destination_deleted_reply.trash!
 
-          moved_to.should == destination_topic
+          expect(moved_to).to eq(destination_topic)
 
           # Check out new topic
           moved_to.reload
-          moved_to.posts_count.should == 3
-          moved_to.highest_post_number.should == 4
+          expect(moved_to.posts_count).to eq(3)
+          expect(moved_to.highest_post_number).to eq(4)
 
           # Posts should be re-ordered
           p2.reload
-          p2.sort_order.should == 3
-          p2.post_number.should == 3
-          p2.topic_id.should == moved_to.id
-          p2.reply_count.should == 1
-          p2.reply_to_post_number.should == nil
+          expect(p2.sort_order).to eq(3)
+          expect(p2.post_number).to eq(3)
+          expect(p2.topic_id).to eq(moved_to.id)
+          expect(p2.reply_count).to eq(1)
+          expect(p2.reply_to_post_number).to eq(nil)
 
           p4.reload
-          p4.post_number.should == 4
-          p4.sort_order.should == 4
-          p4.topic_id.should == moved_to.id
-          p4.reply_to_post_number.should == p2.post_number
+          expect(p4.post_number).to eq(4)
+          expect(p4.sort_order).to eq(4)
+          expect(p4.topic_id).to eq(moved_to.id)
+          expect(p4.reply_to_post_number).to eq(p2.post_number)
         end
       end
 
diff --git a/spec/models/post_reply_spec.rb b/spec/models/post_reply_spec.rb
index d49e466288..1e34c0ae43 100644
--- a/spec/models/post_reply_spec.rb
+++ b/spec/models/post_reply_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
 
 describe PostReply do
 
-  it { should belong_to :post }
-  it { should belong_to :reply }
+  it { is_expected.to belong_to :post }
+  it { is_expected.to belong_to :reply }
 
 end
diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb
index d7d714a1a1..a85b327f27 100644
--- a/spec/models/post_spec.rb
+++ b/spec/models/post_spec.rb
@@ -11,14 +11,14 @@ describe Post do
     Fabricate.build(:post, args)
   end
 
-  it { should validate_presence_of :raw }
+  it { is_expected.to validate_presence_of :raw }
 
   # Min/max body lengths, respecting padding
-  it { should_not allow_value("x").for(:raw) }
-  it { should_not allow_value("x" * (SiteSetting.max_post_length + 1)).for(:raw) }
-  it { should_not allow_value((" " * SiteSetting.min_post_length) + "x").for(:raw) }
+  it { is_expected.not_to allow_value("x").for(:raw) }
+  it { is_expected.not_to allow_value("x" * (SiteSetting.max_post_length + 1)).for(:raw) }
+  it { is_expected.not_to allow_value((" " * SiteSetting.min_post_length) + "x").for(:raw) }
 
-  it { should rate_limit }
+  it { is_expected.to rate_limit }
 
   let(:topic) { Fabricate(:topic) }
   let(:post_args) do
@@ -32,14 +32,14 @@ describe Post do
         2.times do |t|
           Fabricate(:post, created_at: t.seconds.from_now)
         end
-        Post.by_newest.first.created_at.should > Post.by_newest.last.created_at
+        expect(Post.by_newest.first.created_at).to be > Post.by_newest.last.created_at
       end
     end
 
     describe '#with_user' do
       it 'gives you a user' do
         Fabricate(:post, user: Fabricate.build(:user))
-        Post.with_user.first.user.should be_a User
+        expect(Post.with_user.first.user).to be_a User
       end
     end
 
@@ -56,7 +56,7 @@ describe Post do
       end
 
       it "doesn't create a new revision when deleted" do
-        post.revisions.count.should == 0
+        expect(post.revisions.count).to eq(0)
       end
 
       describe "recovery" do
@@ -66,7 +66,7 @@ describe Post do
         end
 
         it "doesn't create a new revision when recovered" do
-          post.revisions.count.should == 0
+          expect(post.revisions.count).to eq(0)
         end
       end
     end
@@ -95,11 +95,11 @@ describe Post do
       PostAction.act(user, post, PostActionType.types[:off_topic])
 
       post.reload
-      post.is_flagged?.should == true
+      expect(post.is_flagged?).to eq(true)
 
       PostAction.remove_act(user, post, PostActionType.types[:off_topic])
       post.reload
-      post.is_flagged?.should == false
+      expect(post.is_flagged?).to eq(false)
     end
   end
 
@@ -114,32 +114,32 @@ describe Post do
     let(:post_with_two_classy_images) { post_with_body(" ", newuser) }
 
     it "returns 0 images for an empty post" do
-      Fabricate.build(:post).image_count.should == 0
+      expect(Fabricate.build(:post).image_count).to eq(0)
     end
 
     it "finds images from markdown" do
-      post_one_image.image_count.should == 1
+      expect(post_one_image.image_count).to eq(1)
     end
 
     it "finds images from HTML" do
-      post_two_images.image_count.should == 2
+      expect(post_two_images.image_count).to eq(2)
     end
 
     it "doesn't count avatars as images" do
-      post_with_avatars.image_count.should == 0
+      expect(post_with_avatars.image_count).to eq(0)
     end
 
     it "doesn't count favicons as images" do
-      post_with_favicon.image_count.should == 0
+      expect(post_with_favicon.image_count).to eq(0)
     end
 
     it "doesn't count thumbnails as images" do
-      post_with_thumbnail.image_count.should == 0
+      expect(post_with_thumbnail.image_count).to eq(0)
     end
 
     it "doesn't count whitelisted images" do
       Post.stubs(:white_listed_image_classes).returns(["classy"])
-      post_with_two_classy_images.image_count.should == 0
+      expect(post_with_two_classy_images.image_count).to eq(0)
     end
 
     context "validation" do
@@ -150,26 +150,26 @@ describe Post do
 
       context 'newuser' do
         it "allows a new user to post below the limit" do
-          post_one_image.should be_valid
+          expect(post_one_image).to be_valid
         end
 
         it "doesn't allow more than the maximum" do
-          post_two_images.should_not be_valid
+          expect(post_two_images).not_to be_valid
         end
 
         it "doesn't allow a new user to edit their post to insert an image" do
           post_no_images.user.trust_level = TrustLevel[0]
           post_no_images.save
-          -> {
+          expect {
             post_no_images.revise(post_no_images.user, { raw: post_two_images.raw })
             post_no_images.reload
-          }.should_not change(post_no_images, :raw)
+          }.not_to change(post_no_images, :raw)
         end
       end
 
       it "allows more images from a not-new account" do
         post_two_images.user.trust_level = TrustLevel[1]
-        post_two_images.should be_valid
+        expect(post_two_images).to be_valid
       end
 
     end
@@ -183,11 +183,11 @@ describe Post do
     let(:post_two_attachments) { post_with_body('errors.log model.3ds', newuser) }
 
     it "returns 0 attachments for an empty post" do
-      Fabricate.build(:post).attachment_count.should == 0
+      expect(Fabricate.build(:post).attachment_count).to eq(0)
     end
 
     it "finds attachments from HTML" do
-      post_two_attachments.attachment_count.should == 2
+      expect(post_two_attachments.attachment_count).to eq(2)
     end
 
     context "validation" do
@@ -198,26 +198,26 @@ describe Post do
 
       context 'newuser' do
         it "allows a new user to post below the limit" do
-          post_one_attachment.should be_valid
+          expect(post_one_attachment).to be_valid
         end
 
         it "doesn't allow more than the maximum" do
-          post_two_attachments.should_not be_valid
+          expect(post_two_attachments).not_to be_valid
         end
 
         it "doesn't allow a new user to edit their post to insert an attachment" do
           post_no_attachments.user.trust_level = TrustLevel[0]
           post_no_attachments.save
-          -> {
+          expect {
             post_no_attachments.revise(post_no_attachments.user, { raw: post_two_attachments.raw })
             post_no_attachments.reload
-          }.should_not change(post_no_attachments, :raw)
+          }.not_to change(post_no_attachments, :raw)
         end
       end
 
       it "allows more attachments from a not-new account" do
         post_two_attachments.user.trust_level = TrustLevel[1]
-        post_two_attachments.should be_valid
+        expect(post_two_attachments).to be_valid
       end
 
     end
@@ -233,40 +233,40 @@ describe Post do
 
     describe "raw_links" do
       it "returns a blank collection for a post with no links" do
-        no_links.raw_links.should be_blank
+        expect(no_links.raw_links).to be_blank
       end
 
       it "finds a link within markdown" do
-        one_link.raw_links.should == ["http://www.imdb.com/name/nm2225369"]
+        expect(one_link.raw_links).to eq(["http://www.imdb.com/name/nm2225369"])
       end
 
       it "can find two links from html" do
-        two_links.raw_links.should == ["http://disneyland.disney.go.com/", "http://reddit.com"]
+        expect(two_links.raw_links).to eq(["http://disneyland.disney.go.com/", "http://reddit.com"])
       end
 
       it "can find three links without markup" do
-        three_links.raw_links.should == ["http://discourse.org", "http://discourse.org/another_url", "http://www.imdb.com/name/nm2225369"]
+        expect(three_links.raw_links).to eq(["http://discourse.org", "http://discourse.org/another_url", "http://www.imdb.com/name/nm2225369"])
       end
     end
 
     describe "linked_hosts" do
       it "returns blank with no links" do
-        no_links.linked_hosts.should be_blank
+        expect(no_links.linked_hosts).to be_blank
       end
 
       it "returns the host and a count for links" do
-        two_links.linked_hosts.should == {"disneyland.disney.go.com" => 1, "reddit.com" => 1}
+        expect(two_links.linked_hosts).to eq({"disneyland.disney.go.com" => 1, "reddit.com" => 1})
       end
 
       it "it counts properly with more than one link on the same host" do
-        three_links.linked_hosts.should == {"discourse.org" => 1, "www.imdb.com" => 1}
+        expect(three_links.linked_hosts).to eq({"discourse.org" => 1, "www.imdb.com" => 1})
       end
     end
 
     describe "total host usage" do
 
       it "has none for a regular post" do
-        no_links.total_hosts_usage.should be_blank
+        expect(no_links.total_hosts_usage).to be_blank
       end
 
       context "with a previous host" do
@@ -280,7 +280,7 @@ describe Post do
         end
 
         it "contains the new post's links, PLUS the previous one" do
-          two_links.total_hosts_usage.should == {'disneyland.disney.go.com' => 2, 'reddit.com' => 1}
+          expect(two_links.total_hosts_usage).to eq({'disneyland.disney.go.com' => 2, 'reddit.com' => 1})
         end
 
       end
@@ -298,19 +298,19 @@ describe Post do
     let(:post_with_mentions) { post_with_body("hello @#{newuser.username} how are you doing?", newuser) }
 
     it "returns 0 links for an empty post" do
-      Fabricate.build(:post).link_count.should == 0
+      expect(Fabricate.build(:post).link_count).to eq(0)
     end
 
     it "returns 0 links for a post with mentions" do
-      post_with_mentions.link_count.should == 0
+      expect(post_with_mentions.link_count).to eq(0)
     end
 
     it "finds links from markdown" do
-      post_one_link.link_count.should == 1
+      expect(post_one_link.link_count).to eq(1)
     end
 
     it "finds links from HTML" do
-      post_two_links.link_count.should == 2
+      expect(post_two_links.link_count).to eq(2)
     end
 
     context "validation" do
@@ -321,17 +321,17 @@ describe Post do
 
       context 'newuser' do
         it "returns true when within the amount of links allowed" do
-          post_one_link.should be_valid
+          expect(post_one_link).to be_valid
         end
 
         it "doesn't allow more links than allowed" do
-          post_two_links.should_not be_valid
+          expect(post_two_links).not_to be_valid
         end
       end
 
       it "allows multiple images for basic accounts" do
         post_two_links.user.trust_level = TrustLevel[1]
-        post_two_links.should be_valid
+        expect(post_two_links).to be_valid
       end
 
     end
@@ -345,37 +345,37 @@ describe Post do
 
       it "returns an empty array with no matches" do
         post = Fabricate.build(:post, post_args.merge(raw: "Hello Jake and Finn!"))
-        post.raw_mentions.should == []
+        expect(post.raw_mentions).to eq([])
       end
 
       it "returns lowercase unique versions of the mentions" do
         post = Fabricate.build(:post, post_args.merge(raw: "@Jake @Finn @Jake"))
-        post.raw_mentions.should == ['jake', 'finn']
+        expect(post.raw_mentions).to eq(['jake', 'finn'])
       end
 
       it "ignores pre" do
         post = Fabricate.build(:post, post_args.merge(raw: "
@Jake
@Finn")) - post.raw_mentions.should == ['finn'] + expect(post.raw_mentions).to eq(['finn']) end it "catches content between pre tags" do post = Fabricate.build(:post, post_args.merge(raw: "
hello
@Finn
"))
-        post.raw_mentions.should == ['finn']
+        expect(post.raw_mentions).to eq(['finn'])
       end
 
       it "ignores code" do
         post = Fabricate.build(:post, post_args.merge(raw: "@Jake `@Finn`"))
-        post.raw_mentions.should == ['jake']
+        expect(post.raw_mentions).to eq(['jake'])
       end
 
       it "ignores quotes" do
         post = Fabricate.build(:post, post_args.merge(raw: "[quote=\"Evil Trout\"]@Jake[/quote] @Finn"))
-        post.raw_mentions.should == ['finn']
+        expect(post.raw_mentions).to eq(['finn'])
       end
 
       it "handles underscore in username" do
         post = Fabricate.build(:post, post_args.merge(raw: "@Jake @Finn @Jake_Old"))
-        post.raw_mentions.should == ['jake', 'finn', 'jake_old']
+        expect(post.raw_mentions).to eq(['jake', 'finn', 'jake_old'])
       end
 
     end
@@ -393,11 +393,11 @@ describe Post do
         end
 
         it "allows a new user to have newuser_max_mentions_per_post mentions" do
-          post_with_one_mention.should be_valid
+          expect(post_with_one_mention).to be_valid
         end
 
         it "doesn't allow a new user to have more than newuser_max_mentions_per_post mentions" do
-          post_with_two_mentions.should_not be_valid
+          expect(post_with_two_mentions).not_to be_valid
         end
       end
 
@@ -409,12 +409,12 @@ describe Post do
 
         it "allows vmax_mentions_per_post mentions" do
           post_with_one_mention.user.trust_level = TrustLevel[1]
-          post_with_one_mention.should be_valid
+          expect(post_with_one_mention).to be_valid
         end
 
         it "doesn't allow to have more than max_mentions_per_post mentions" do
           post_with_two_mentions.user.trust_level = TrustLevel[1]
-          post_with_two_mentions.should_not be_valid
+          expect(post_with_two_mentions).not_to be_valid
         end
       end
 
@@ -425,11 +425,11 @@ describe Post do
 
   context 'validation' do
     it 'validates our default post' do
-      Fabricate.build(:post, post_args).should be_valid
+      expect(Fabricate.build(:post, post_args)).to be_valid
     end
 
     it 'treate blank posts as invalid' do
-      Fabricate.build(:post, raw: "").should_not be_valid
+      expect(Fabricate.build(:post, raw: "")).not_to be_valid
     end
   end
 
@@ -439,24 +439,24 @@ describe Post do
     let(:post) { post_with_body(raw) }
 
     it "returns a value" do
-      post.raw_hash.should be_present
+      expect(post.raw_hash).to be_present
     end
 
     it "returns blank for a nil body" do
       post.raw = nil
-      post.raw_hash.should be_blank
+      expect(post.raw_hash).to be_blank
     end
 
     it "returns the same value for the same raw" do
-      post.raw_hash.should == post_with_body(raw).raw_hash
+      expect(post.raw_hash).to eq(post_with_body(raw).raw_hash)
     end
 
     it "returns a different value for a different raw" do
-      post.raw_hash.should_not == post_with_body("something else").raw_hash
+      expect(post.raw_hash).not_to eq(post_with_body("something else").raw_hash)
     end
 
     it "returns a different value with different text case" do
-      post.raw_hash.should_not == post_with_body("THIS is OUR TEST post BODy").raw_hash
+      expect(post.raw_hash).not_to eq(post_with_body("THIS is OUR TEST post BODy").raw_hash)
     end
   end
 
@@ -466,15 +466,15 @@ describe Post do
     let(:first_version_at) { post.last_version_at }
 
     it 'has no revision' do
-      post.revisions.size.should == 0
-      first_version_at.should be_present
-      post.revise(post.user, { raw: post.raw }).should == false
+      expect(post.revisions.size).to eq(0)
+      expect(first_version_at).to be_present
+      expect(post.revise(post.user, { raw: post.raw })).to eq(false)
     end
 
     describe 'with the same body' do
 
       it "doesn't change version" do
-        lambda { post.revise(post.user, { raw: post.raw }); post.reload }.should_not change(post, :version)
+        expect { post.revise(post.user, { raw: post.raw }); post.reload }.not_to change(post, :version)
       end
 
     end
@@ -490,34 +490,34 @@ describe Post do
         # ninja edit
         post.revise(post.user, { raw: 'updated body' }, revised_at: post.updated_at + 10.seconds)
         post.reload
-        post.version.should == 1
-        post.public_version.should == 1
-        post.revisions.size.should == 0
-        post.last_version_at.to_i.should == first_version_at.to_i
+        expect(post.version).to eq(1)
+        expect(post.public_version).to eq(1)
+        expect(post.revisions.size).to eq(0)
+        expect(post.last_version_at.to_i).to eq(first_version_at.to_i)
 
         # revision much later
         post.revise(post.user, { raw: 'another updated body' }, revised_at: revised_at)
         post.reload
-        post.version.should == 2
-        post.public_version.should == 2
-        post.revisions.size.should == 1
-        post.last_version_at.to_i.should == revised_at.to_i
+        expect(post.version).to eq(2)
+        expect(post.public_version).to eq(2)
+        expect(post.revisions.size).to eq(1)
+        expect(post.last_version_at.to_i).to eq(revised_at.to_i)
 
         # new edit window
         post.revise(post.user, { raw: 'yet another updated body' }, revised_at: revised_at + 10.seconds)
         post.reload
-        post.version.should == 2
-        post.public_version.should == 2
-        post.revisions.size.should == 1
-        post.last_version_at.to_i.should == revised_at.to_i
+        expect(post.version).to eq(2)
+        expect(post.public_version).to eq(2)
+        expect(post.revisions.size).to eq(1)
+        expect(post.last_version_at.to_i).to eq(revised_at.to_i)
 
         # after second window
         post.revise(post.user, { raw: 'yet another, another updated body' }, revised_at: new_revised_at)
         post.reload
-        post.version.should == 3
-        post.public_version.should == 3
-        post.revisions.size.should == 2
-        post.last_version_at.to_i.should == new_revised_at.to_i
+        expect(post.version).to eq(3)
+        expect(post.public_version).to eq(3)
+        expect(post.revisions.size).to eq(2)
+        expect(post.last_version_at.to_i).to eq(new_revised_at.to_i)
       end
 
     end
@@ -536,13 +536,13 @@ describe Post do
       let!(:result) { post.revise(changed_by, { raw: 'updated body' }) }
 
       it 'acts correctly' do
-        result.should == true
-        post.raw.should == 'updated body'
-        post.invalidate_oneboxes.should == true
-        post.version.should == 2
-        post.public_version.should == 2
-        post.revisions.size.should == 1
-        post.revisions.first.user.should be_present
+        expect(result).to eq(true)
+        expect(post.raw).to eq('updated body')
+        expect(post.invalidate_oneboxes).to eq(true)
+        expect(post.version).to eq(2)
+        expect(post.public_version).to eq(2)
+        expect(post.revisions.size).to eq(1)
+        expect(post.revisions.first.user).to be_present
       end
 
       context 'second poster posts again quickly' do
@@ -552,9 +552,9 @@ describe Post do
           post.revise(changed_by, { raw: 'yet another updated body' }, revised_at: post.updated_at + 10.seconds)
           post.reload
 
-          post.version.should == 2
-          post.public_version.should == 2
-          post.revisions.size.should == 1
+          expect(post.version).to eq(2)
+          expect(post.public_version).to eq(2)
+          expect(post.revisions.size).to eq(1)
         end
 
       end
@@ -569,15 +569,15 @@ describe Post do
     let(:post) { Fabricate(:post, post_args) }
 
     it "has correct info set" do
-      post.user_deleted?.should == false
-      post.post_number.should be_present
-      post.excerpt.should be_present
-      post.post_type.should == Post.types[:regular]
-      post.revisions.should be_blank
-      post.cooked.should be_present
-      post.external_id.should be_present
-      post.quote_count.should == 0
-      post.replies.should be_blank
+      expect(post.user_deleted?).to eq(false)
+      expect(post.post_number).to be_present
+      expect(post.excerpt).to be_present
+      expect(post.post_type).to eq(Post.types[:regular])
+      expect(post.revisions).to be_blank
+      expect(post.cooked).to be_present
+      expect(post.external_id).to be_present
+      expect(post.quote_count).to eq(0)
+      expect(post.replies).to be_blank
     end
 
     describe 'extract_quoted_post_numbers' do
@@ -588,13 +588,13 @@ describe Post do
       it "finds the quote when in the same topic" do
         reply.raw = "[quote=\"EvilTrout, post:#{post.post_number}, topic:#{post.topic_id}\"]hello[/quote]"
         reply.extract_quoted_post_numbers
-        reply.quoted_post_numbers.should == [post.post_number]
+        expect(reply.quoted_post_numbers).to eq([post.post_number])
       end
 
       it "doesn't find the quote in a different topic" do
         reply.raw = "[quote=\"EvilTrout, post:#{post.post_number}, topic:#{post.topic_id+1}\"]hello[/quote]"
         reply.extract_quoted_post_numbers
-        reply.quoted_post_numbers.should be_blank
+        expect(reply.quoted_post_numbers).to be_blank
       end
 
     end
@@ -608,25 +608,25 @@ describe Post do
       let!(:reply) { PostCreator.new(other_user, raw: reply_text, topic_id: topic.id, reply_to_post_number: post.post_number ).create }
 
       it 'has a quote' do
-        reply.quote_count.should == 1
+        expect(reply.quote_count).to eq(1)
       end
 
       it 'has a reply to the user of the original user' do
-        reply.reply_to_user.should == post.user
+        expect(reply.reply_to_user).to eq(post.user)
       end
 
       it 'increases the reply count of the parent' do
         post.reload
-        post.reply_count.should == 1
+        expect(post.reply_count).to eq(1)
       end
 
       it 'increases the reply count of the topic' do
         topic.reload
-        topic.reply_count.should == 1
+        expect(topic.reply_count).to eq(1)
       end
 
       it 'is the child of the parent post' do
-        post.replies.should == [reply]
+        expect(post.replies).to eq([reply])
       end
 
 
@@ -634,7 +634,7 @@ describe Post do
         reply.raw = 'updated raw'
         reply.save
         post.reload
-        post.reply_count.should == 1
+        expect(post.reply_count).to eq(1)
       end
 
       context 'a multi-quote reply' do
@@ -645,9 +645,9 @@ describe Post do
         end
 
         it 'has the correct info set' do
-          multi_reply.quote_count.should == 2
-          post.replies.include?(multi_reply).should == true
-          reply.replies.include?(multi_reply).should == true
+          expect(multi_reply.quote_count).to eq(2)
+          expect(post.replies.include?(multi_reply)).to eq(true)
+          expect(reply.replies.include?(multi_reply)).to eq(true)
         end
       end
 
@@ -662,7 +662,7 @@ describe Post do
 
     it "returns the OP and posts above the threshold in summary mode" do
       SiteSetting.stubs(:summary_percent_filter).returns(66)
-      Post.summary.order(:post_number).should == [p1, p2]
+      expect(Post.summary.order(:post_number)).to eq([p1, p2])
     end
 
   end
@@ -676,7 +676,7 @@ describe Post do
       let!(:p3) { Fabricate(:post, post_args) }
 
       it 'defaults to created order' do
-        Post.regular_order.should == [p1, p2, p3]
+        expect(Post.regular_order).to eq([p1, p2, p3])
       end
     end
   end
@@ -689,24 +689,24 @@ describe Post do
     let!(:p4) { Fabricate(:post, post_args.merge(reply_to_post_number: p2.post_number)) }
 
     it "returns the posts in reply to this post" do
-      p4.reply_history.should == [p1, p2]
-      p4.reply_history(1).should == [p2]
-      p3.reply_history.should be_blank
-      p2.reply_history.should == [p1]
+      expect(p4.reply_history).to eq([p1, p2])
+      expect(p4.reply_history(1)).to eq([p2])
+      expect(p3.reply_history).to be_blank
+      expect(p2.reply_history).to eq([p1])
     end
 
   end
 
   describe 'urls' do
     it 'no-ops for empty list' do
-      Post.urls([]).should == {}
+      expect(Post.urls([])).to eq({})
     end
 
     # integration test -> should move to centralized integration test
     it 'finds urls for posts presented' do
       p1 = Fabricate(:post)
       p2 = Fabricate(:post)
-      Post.urls([p1.id, p2.id]).should == {p1.id => p1.url, p2.id => p2.url}
+      expect(Post.urls([p1.id, p2.id])).to eq({p1.id => p1.url, p2.id => p2.url})
     end
   end
 
@@ -714,15 +714,15 @@ describe Post do
     it "adds details" do
       post = Fabricate.build(:post)
       post.add_detail("key", "value")
-      post.post_details.size.should == 1
-      post.post_details.first.key.should == "key"
-      post.post_details.first.value.should == "value"
+      expect(post.post_details.size).to eq(1)
+      expect(post.post_details.first.key).to eq("key")
+      expect(post.post_details.first.value).to eq("value")
     end
 
     it "can find a post by a detail" do
       detail = Fabricate(:post_detail)
       post   = detail.post
-      Post.find_by_detail(detail.key, detail.value).id.should == post.id
+      expect(Post.find_by_detail(detail.key, detail.value).id).to eq(post.id)
     end
   end
 
@@ -732,21 +732,21 @@ describe Post do
     it "should add nofollow to links in the post for trust levels below 3" do
       post.user.trust_level = 2
       post.save
-      post.cooked.should =~ /nofollow/
+      expect(post.cooked).to match(/nofollow/)
     end
 
     it "when tl3_links_no_follow is false, should not add nofollow for trust level 3 and higher" do
       SiteSetting.stubs(:tl3_links_no_follow).returns(false)
       post.user.trust_level = 3
       post.save
-      post.cooked.should_not =~ /nofollow/
+      expect(post.cooked).not_to match(/nofollow/)
     end
 
     it "when tl3_links_no_follow is true, should add nofollow for trust level 3 and higher" do
       SiteSetting.stubs(:tl3_links_no_follow).returns(true)
       post.user.trust_level = 3
       post.save
-      post.cooked.should =~ /nofollow/
+      expect(post.cooked).to match(/nofollow/)
     end
   end
 
@@ -763,36 +763,36 @@ describe Post do
     it "correctly detects host spam" do
       post = Fabricate(:post, raw: "hello from my site http://www.somesite.com http://#{GlobalSetting.hostname} http://#{RailsMultisite::ConnectionManagement.current_hostname}")
 
-      post.total_hosts_usage.should == {"www.somesite.com" => 1}
+      expect(post.total_hosts_usage).to eq({"www.somesite.com" => 1})
       post.acting_user.trust_level = 0
 
-      post.has_host_spam?.should == false
+      expect(post.has_host_spam?).to eq(false)
 
       SiteSetting.newuser_spam_host_threshold = 1
 
-      post.has_host_spam?.should == true
+      expect(post.has_host_spam?).to eq(true)
 
       SiteSetting.white_listed_spam_host_domains = "bla.com|boo.com | somesite.com "
-      post.has_host_spam?.should == false
+      expect(post.has_host_spam?).to eq(false)
     end
   end
 
   it "has custom fields" do
     post = Fabricate(:post)
-    post.custom_fields["a"].should == nil
+    expect(post.custom_fields["a"]).to eq(nil)
 
     post.custom_fields["Tommy"] = "Hanks"
     post.custom_fields["Vincent"] = "Vega"
     post.save
 
     post = Post.find(post.id)
-    post.custom_fields.should == {"Tommy" => "Hanks", "Vincent" => "Vega"}
+    expect(post.custom_fields).to eq({"Tommy" => "Hanks", "Vincent" => "Vega"})
   end
 
   describe "#rebake!" do
     it "will rebake a post correctly" do
       post = create_post
-      post.baked_at.should_not == nil
+      expect(post.baked_at).not_to eq(nil)
       first_baked = post.baked_at
       first_cooked = post.cooked
 
@@ -803,9 +803,9 @@ describe Post do
 
       result = post.rebake!
 
-      post.baked_at.should_not == first_baked
-      post.cooked.should == first_cooked
-      result.should == true
+      expect(post.baked_at).not_to eq(first_baked)
+      expect(post.cooked).to eq(first_cooked)
+      expect(result).to eq(true)
     end
   end
 
@@ -816,12 +816,12 @@ describe Post do
       Post.rebake_old(100)
 
       post.reload
-      post.baked_at.should be > 1.day.ago
+      expect(post.baked_at).to be > 1.day.ago
 
       baked = post.baked_at
       Post.rebake_old(100)
       post.reload
-      post.baked_at.should == baked
+      expect(post.baked_at).to eq(baked)
     end
   end
 
@@ -835,7 +835,7 @@ describe Post do
       post.update_columns(hidden: true, hidden_at: Time.now, hidden_reason_id: 1)
       post.reload
 
-      post.hidden.should == true
+      expect(post.hidden).to eq(true)
 
       post.expects(:publish_change_to_clients!).with(:acted)
 
@@ -844,8 +844,8 @@ describe Post do
       post.reload
       hidden_topic.reload
 
-      post.hidden.should == false
-      hidden_topic.visible.should == true
+      expect(post.hidden).to eq(false)
+      expect(hidden_topic.visible).to eq(true)
     end
   end
 
@@ -862,8 +862,8 @@ describe Post do
     second_post.reload
     hidden_topic.reload
 
-    second_post.hidden.should == false
-    hidden_topic.visible.should == false
+    expect(second_post.hidden).to eq(false)
+    expect(hidden_topic.visible).to eq(false)
   end
 
 end
diff --git a/spec/models/post_timing_spec.rb b/spec/models/post_timing_spec.rb
index c3694386d2..452d6ad3e8 100644
--- a/spec/models/post_timing_spec.rb
+++ b/spec/models/post_timing_spec.rb
@@ -2,8 +2,8 @@ require 'spec_helper'
 
 describe PostTiming do
 
-  it { should validate_presence_of :post_number }
-  it { should validate_presence_of :msecs }
+  it { is_expected.to validate_presence_of :post_number }
+  it { is_expected.to validate_presence_of :msecs }
 
   describe 'pretend_read' do
     let!(:p1) { Fabricate(:post) }
@@ -41,21 +41,21 @@ describe PostTiming do
 
       PostTiming.pretend_read(topic_id, 2, 3)
 
-      PostTiming.where(topic_id: topic_id, user_id: 1, post_number: 3).count.should == 0
-      PostTiming.where(topic_id: topic_id, user_id: 2, post_number: 3).count.should == 1
-      PostTiming.where(topic_id: topic_id, user_id: 3, post_number: 3).count.should == 1
+      expect(PostTiming.where(topic_id: topic_id, user_id: 1, post_number: 3).count).to eq(0)
+      expect(PostTiming.where(topic_id: topic_id, user_id: 2, post_number: 3).count).to eq(1)
+      expect(PostTiming.where(topic_id: topic_id, user_id: 3, post_number: 3).count).to eq(1)
 
       tu = TopicUser.find_by(topic_id: topic_id, user_id: 1)
-      tu.last_read_post_number.should == 1
-      tu.highest_seen_post_number.should == 1
+      expect(tu.last_read_post_number).to eq(1)
+      expect(tu.highest_seen_post_number).to eq(1)
 
       tu = TopicUser.find_by(topic_id: topic_id, user_id: 2)
-      tu.last_read_post_number.should == 3
-      tu.highest_seen_post_number.should == 3
+      expect(tu.last_read_post_number).to eq(3)
+      expect(tu.highest_seen_post_number).to eq(3)
 
       tu = TopicUser.find_by(topic_id: topic_id, user_id: 3)
-      tu.last_read_post_number.should == 3
-      tu.highest_seen_post_number.should == 3
+      expect(tu.last_read_post_number).to eq(3)
+      expect(tu.highest_seen_post_number).to eq(3)
 
     end
   end
@@ -73,14 +73,14 @@ describe PostTiming do
 
       PostAction.act(user2, post, PostActionType.types[:like])
 
-      post.user.unread_notifications.should == 1
-      post.user.unread_notifications_by_type.should == {Notification.types[:liked] => 1 }
+      expect(post.user.unread_notifications).to eq(1)
+      expect(post.user.unread_notifications_by_type).to eq({Notification.types[:liked] => 1 })
 
       PostTiming.process_timings(post.user, post.topic_id, 1, [[post.post_number, 100]])
 
       post.user.reload
-      post.user.unread_notifications_by_type.should == {}
-      post.user.unread_notifications.should == 0
+      expect(post.user.unread_notifications_by_type).to eq({})
+      expect(post.user.unread_notifications).to eq(0)
 
     end
   end
@@ -94,10 +94,10 @@ describe PostTiming do
     end
 
     it 'adds a view to the post' do
-      lambda {
+      expect {
         PostTiming.record_timing(@timing_attrs)
         @post.reload
-      }.should change(@post, :reads).by(1)
+      }.to change(@post, :reads).by(1)
     end
 
     describe 'multiple calls' do
@@ -106,10 +106,10 @@ describe PostTiming do
         PostTiming.record_timing(@timing_attrs)
         timing = PostTiming.find_by(topic_id: @post.topic_id, user_id: @coding_horror.id, post_number: @post.post_number)
 
-        timing.should be_present
-        timing.msecs.should == 2468
+        expect(timing).to be_present
+        expect(timing.msecs).to eq(2468)
 
-        @coding_horror.user_stat.posts_read_count.should == 1
+        expect(@coding_horror.user_stat.posts_read_count).to eq(1)
       end
 
     end
@@ -118,25 +118,25 @@ describe PostTiming do
 
       describe 'posts' do
         it 'has no avg_time by default' do
-          @post.avg_time.should be_blank
+          expect(@post.avg_time).to be_blank
         end
 
         it "doesn't change when we calculate the avg time for the post because there's no timings" do
           Post.calculate_avg_time
           @post.reload
-          @post.avg_time.should be_blank
+          expect(@post.avg_time).to be_blank
         end
       end
 
       describe 'topics' do
         it 'has no avg_time by default' do
-          @topic.avg_time.should be_blank
+          expect(@topic.avg_time).to be_blank
         end
 
         it "doesn't change when we calculate the avg time for the post because there's no timings" do
           Topic.calculate_avg_time
           @topic.reload
-          @topic.avg_time.should be_blank
+          expect(@topic.avg_time).to be_blank
         end
       end
 
@@ -145,7 +145,7 @@ describe PostTiming do
           PostTiming.record_timing(@timing_attrs.merge(user_id: @post.user_id))
           Post.calculate_avg_time
           @post.reload
-          @post.avg_time.should be_blank
+          expect(@post.avg_time).to be_blank
         end
 
       end
@@ -158,7 +158,7 @@ describe PostTiming do
         end
 
         it 'has a post avg_time from the timing' do
-          @post.avg_time.should be_present
+          expect(@post.avg_time).to be_present
         end
 
         describe 'forum topics' do
@@ -168,7 +168,7 @@ describe PostTiming do
           end
 
           it 'has an avg_time from the timing' do
-            @topic.avg_time.should be_present
+            expect(@topic.avg_time).to be_present
           end
 
         end
diff --git a/spec/models/post_upload_spec.rb b/spec/models/post_upload_spec.rb
index a4e8e5782b..4e370c1a8d 100644
--- a/spec/models/post_upload_spec.rb
+++ b/spec/models/post_upload_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
 
 describe PostUpload do
 
-  it { should belong_to :post }
-  it { should belong_to :upload }
+  it { is_expected.to belong_to :post }
+  it { is_expected.to belong_to :upload }
 
 end
diff --git a/spec/models/quoted_post_spec.rb b/spec/models/quoted_post_spec.rb
index 38c50cb8e9..dc34fa4a47 100644
--- a/spec/models/quoted_post_spec.rb
+++ b/spec/models/quoted_post_spec.rb
@@ -7,8 +7,8 @@ describe QuotedPost do
                         raw: "[quote=\"#{post1.user.username}, post: 1, topic:#{post1.topic_id}\"]\ntest\n[/quote]\nthis is a test post",
                         reply_to_post_number: 1)
 
-    QuotedPost.find_by(post_id: post2.id, quoted_post_id: post1.id).should_not == nil
-    post2.reply_quoted.should == true
+    expect(QuotedPost.find_by(post_id: post2.id, quoted_post_id: post1.id)).not_to eq(nil)
+    expect(post2.reply_quoted).to eq(true)
   end
 
   it 'correctly handles deltas' do
@@ -22,9 +22,9 @@ HTML
     QuotedPost.create!(post_id: post2.id, quoted_post_id: 999)
 
     QuotedPost.extract_from(post2)
-    QuotedPost.where(post_id: post2.id).count.should == 1
-    QuotedPost.find_by(post_id: post2.id, quoted_post_id: post1.id).should_not == nil
+    expect(QuotedPost.where(post_id: post2.id).count).to eq(1)
+    expect(QuotedPost.find_by(post_id: post2.id, quoted_post_id: post1.id)).not_to eq(nil)
 
-    post2.reply_quoted.should == false
+    expect(post2.reply_quoted).to eq(false)
   end
 end
diff --git a/spec/models/report_spec.rb b/spec/models/report_spec.rb
index 02b806dee2..4f8914755d 100644
--- a/spec/models/report_spec.rb
+++ b/spec/models/report_spec.rb
@@ -7,21 +7,22 @@ describe Report do
 
     context "no visits" do
       it "returns an empty report" do
-        report.data.should be_blank
+        expect(report.data).to be_blank
       end
     end
 
     context "with visits" do
       let(:user) { Fabricate(:user) }
 
-      before do
+      it "returns a report with data" do
+        freeze_time DateTime.parse('2000-01-01')
+        user.user_visits.create(visited_at: 1.hour.from_now)
         user.user_visits.create(visited_at: 1.day.ago)
         user.user_visits.create(visited_at: 2.days.ago)
+        expect(report.data).to be_present
+        expect(report.data.select { |v| v[:x].today? }).to be_present
       end
 
-      it "returns a report with data" do
-        report.data.should be_present
-      end
     end
   end
 
@@ -33,12 +34,13 @@ describe Report do
 
       context "no #{pluralized}" do
         it 'returns an empty report' do
-          report.data.should be_blank
+          expect(report.data).to be_blank
         end
       end
 
       context "with #{pluralized}" do
-        before do
+        before(:each) do
+          Timecop.freeze
           fabricator = case arg
           when :signup
             :user
@@ -47,48 +49,73 @@ describe Report do
           else
             arg
           end
-          Fabricate(fabricator, created_at: 25.hours.ago)
+          Fabricate(fabricator)
           Fabricate(fabricator, created_at: 1.hours.ago)
           Fabricate(fabricator, created_at: 1.hours.ago)
+          Fabricate(fabricator, created_at: 1.day.ago)
+          Fabricate(fabricator, created_at: 2.days.ago)
+          Fabricate(fabricator, created_at: 30.days.ago)
+          Fabricate(fabricator, created_at: 35.days.ago)
         end
+        after(:each) { Timecop.return }
 
-        it 'returns correct data' do
-          report.data[0][:y].should == 1
-          report.data[1][:y].should == 2
+        context 'returns a report with data'
+          it 'with 30 days data' do
+            expect(report.data.count).to eq(4)
+          end
+
+          it 'has correct data sorted as asc' do
+            skip("Something is off with this spec @neil, it fails at some times of the day")
+            expect(report.data[0][:y]).to eq(1) # 30.days.ago
+            expect(report.data[1][:y]).to eq(1) # 2.days.ago
+            expect(report.data[2][:y]).to eq(1) # 1.day.ago
+            expect(report.data[3][:y]).to eq(3) # today
+          end
+
+          it "returns today's data" do
+            expect(report.data.select { |v| v[:x].today? }).to be_present
+          end
+
+          it 'returns total data' do
+            expect(report.total).to eq 7
+          end
+
+          it "returns previous 30 day's data" do
+            expect(report.prev30Days).to eq 1
+          end
         end
       end
     end
-  end
 
   describe 'private messages' do
     let(:report) { Report.find('user_to_user_private_messages') }
 
-    it 'topic report should not include private messages' do
+    it 'topic report).to not include private messages' do
       Fabricate(:private_message_topic, created_at: 1.hour.ago)
       Fabricate(:topic, created_at: 1.hour.ago)
       report = Report.find('topics')
-      report.data[0][:y].should == 1
-      report.total.should == 1
+      expect(report.data[0][:y]).to eq(1)
+      expect(report.total).to eq(1)
     end
 
-    it 'post report should not include private messages' do
+    it 'post report).to not include private messages' do
       Fabricate(:private_message_post, created_at: 1.hour.ago)
       Fabricate(:post)
       report = Report.find('posts')
-      report.data[0][:y].should == 1
-      report.total.should == 1
+      expect(report.data[0][:y]).to eq 1
+      expect(report.total).to eq 1
     end
 
     context 'no private messages' do
       it 'returns an empty report' do
-        report.data.should be_blank
+        expect(report.data).to be_blank
       end
 
       context 'some public posts' do
         it 'returns an empty report' do
           Fabricate(:post); Fabricate(:post)
-          report.data.should be_blank
-          report.total.should == 0
+          expect(report.data).to be_blank
+          expect(report.total).to eq 0
         end
       end
     end
@@ -101,9 +128,9 @@ describe Report do
       end
 
       it 'returns correct data' do
-        report.data[0][:y].should == 1
-        report.data[1][:y].should == 2
-        report.total.should == 3
+        expect(report.data[0][:y]).to eq 1
+        expect(report.data[1][:y]).to eq 2
+        expect(report.total).to eq 3
       end
 
       context 'and some public posts' do
@@ -112,9 +139,9 @@ describe Report do
         end
 
         it 'returns correct data' do
-          report.data[0][:y].should == 1
-          report.data[1][:y].should == 2
-          report.total.should == 3
+          expect(report.data[0][:y]).to eq 1
+          expect(report.data[1][:y]).to eq 2
+          expect(report.total).to eq 3
         end
       end
     end
@@ -125,7 +152,7 @@ describe Report do
 
     context "no users" do
       it "returns an empty report" do
-        report.data.should be_blank
+        expect(report.data).to be_blank
       end
     end
 
@@ -137,12 +164,12 @@ describe Report do
       end
 
       it "returns a report with data" do
-        report.data.should be_present
-        report.data.find {|d| d[:x] == TrustLevel[0]}[:y].should == 3
-        report.data.find {|d| d[:x] == TrustLevel[2]}[:y].should == 2
-        report.data.find {|d| d[:x] == TrustLevel[4]}[:y].should == 1
+        expect(report.data).to be_present
+        expect(report.data.find {|d| d[:x] == TrustLevel[0]}[:y]).to eq 3
+        expect(report.data.find {|d| d[:x] == TrustLevel[2]}[:y]).to eq 2
+        expect(report.data.find {|d| d[:x] == TrustLevel[4]}[:y]).to eq 1
       end
     end
   end
-
 end
+
diff --git a/spec/models/rtl_spec.rb b/spec/models/rtl_spec.rb
index 0d69a85884..780743a0bb 100644
--- a/spec/models/rtl_spec.rb
+++ b/spec/models/rtl_spec.rb
@@ -13,13 +13,13 @@ describe RTL do
         before { user.stubs(:locale).returns('he') }
 
         it 'returns rtl class' do
-          RTL.new(user).css_class.should == 'rtl'
+          expect(RTL.new(user).css_class).to eq('rtl')
         end
       end
 
       context 'user locale is not RTL' do
         it 'returns empty class' do
-          RTL.new(user).css_class.should == ''
+          expect(RTL.new(user).css_class).to eq('')
         end
       end
 
@@ -32,7 +32,7 @@ describe RTL do
         before { SiteSetting.stubs(:default_locale).returns('he') }
 
         it 'returns rtl class' do
-          RTL.new(user).css_class.should == 'rtl'
+          expect(RTL.new(user).css_class).to eq('rtl')
         end
       end
 
@@ -43,7 +43,7 @@ describe RTL do
           before { user.stubs(:locale).returns('he') }
 
           it 'returns empty class' do
-            RTL.new(user).css_class.should == ''
+            expect(RTL.new(user).css_class).to eq('')
           end
         end
       end
diff --git a/spec/models/s3_region_site_setting_spec.rb b/spec/models/s3_region_site_setting_spec.rb
index f2743e1669..7bb346445c 100644
--- a/spec/models/s3_region_site_setting_spec.rb
+++ b/spec/models/s3_region_site_setting_spec.rb
@@ -14,7 +14,7 @@ describe S3RegionSiteSetting do
 
   describe 'values' do
     it 'returns all the S3 regions and blank' do
-      expect(S3RegionSiteSetting.values.map {|x| x[:value]}.sort).to eq(['', 'us-east-1', 'us-west-1', 'us-west-2', 'us-gov-west-1', 'eu-west-1', 'ap-southeast-1', 'ap-southeast-2', 'ap-northeast-1', 'sa-east-1'].sort)
+      expect(S3RegionSiteSetting.values.map {|x| x[:value]}.sort).to eq(['', 'us-east-1', 'us-west-1', 'us-west-2', 'us-gov-west-1', 'eu-west-1', 'eu-central-1', 'ap-southeast-1', 'ap-southeast-2', 'ap-northeast-1', 'sa-east-1'].sort)
     end
   end
 
diff --git a/spec/models/screened_email_spec.rb b/spec/models/screened_email_spec.rb
index 67c9cb446d..0621b69cdf 100644
--- a/spec/models/screened_email_spec.rb
+++ b/spec/models/screened_email_spec.rb
@@ -7,18 +7,18 @@ describe ScreenedEmail do
 
   describe "new record" do
     it "sets a default action_type" do
-      ScreenedEmail.create(email: email).action_type.should == ScreenedEmail.actions[:block]
+      expect(ScreenedEmail.create(email: email).action_type).to eq(ScreenedEmail.actions[:block])
     end
 
     it "last_match_at is null" do
       # If we manually load the table with some emails, we can see whether those emails
       # have ever been blocked by looking at last_match_at.
-      ScreenedEmail.create(email: email).last_match_at.should == nil
+      expect(ScreenedEmail.create(email: email).last_match_at).to eq(nil)
     end
 
     it "downcases the email" do
       s = ScreenedEmail.create(email: 'SPAMZ@EXAMPLE.COM')
-      s.email.should == 'spamz@example.com'
+      expect(s.email).to eq('spamz@example.com')
     end
   end
 
@@ -26,16 +26,16 @@ describe ScreenedEmail do
     context 'email is not being blocked' do
       it 'creates a new record with default action of :block' do
         record = ScreenedEmail.block(email)
-        record.should_not be_new_record
-        record.email.should == email
-        record.action_type.should == ScreenedEmail.actions[:block]
+        expect(record).not_to be_new_record
+        expect(record.email).to eq(email)
+        expect(record.action_type).to eq(ScreenedEmail.actions[:block])
       end
 
       it 'lets action_type be overriden' do
         record = ScreenedEmail.block(email, action_type: ScreenedEmail.actions[:do_nothing])
-        record.should_not be_new_record
-        record.email.should == email
-        record.action_type.should == ScreenedEmail.actions[:do_nothing]
+        expect(record).not_to be_new_record
+        expect(record.email).to eq(email)
+        expect(record.action_type).to eq(ScreenedEmail.actions[:do_nothing])
       end
     end
 
@@ -47,7 +47,7 @@ describe ScreenedEmail do
       end
 
       it "returns the existing record" do
-        ScreenedEmail.block(email).should == existing
+        expect(ScreenedEmail.block(email)).to eq(existing)
       end
     end
   end
@@ -56,44 +56,44 @@ describe ScreenedEmail do
     subject { ScreenedEmail.should_block?(email) }
 
     it "returns false if a record with the email doesn't exist" do
-      subject.should == false
+      expect(subject).to eq(false)
     end
 
     it "returns true when there is a record with the email" do
-      ScreenedEmail.should_block?(email).should == false
+      expect(ScreenedEmail.should_block?(email)).to eq(false)
       ScreenedEmail.create(email: email).save
-      ScreenedEmail.should_block?(email).should == true
+      expect(ScreenedEmail.should_block?(email)).to eq(true)
     end
 
     it "returns true when there is a record with a similar email" do
-      ScreenedEmail.should_block?(email).should == false
+      expect(ScreenedEmail.should_block?(email)).to eq(false)
       ScreenedEmail.create(email: similar_email).save
-      ScreenedEmail.should_block?(email).should == true
+      expect(ScreenedEmail.should_block?(email)).to eq(true)
     end
 
     it "returns true when it's same email, but all caps" do
       ScreenedEmail.create(email: email).save
-      ScreenedEmail.should_block?(email.upcase).should == true
+      expect(ScreenedEmail.should_block?(email.upcase)).to eq(true)
     end
 
     shared_examples "when a ScreenedEmail record matches" do
       it "updates statistics" do
         Timecop.freeze(Time.zone.now) do
           expect { subject }.to change { screened_email.reload.match_count }.by(1)
-          screened_email.last_match_at.should be_within_one_second_of(Time.zone.now)
+          expect(screened_email.last_match_at).to be_within_one_second_of(Time.zone.now)
         end
       end
     end
 
     context "action_type is :block" do
       let!(:screened_email) { Fabricate(:screened_email, email: email, action_type: ScreenedEmail.actions[:block]) }
-      it { should == true }
+      it { is_expected.to eq(true) }
       include_examples "when a ScreenedEmail record matches"
     end
 
     context "action_type is :do_nothing" do
       let!(:screened_email) { Fabricate(:screened_email, email: email, action_type: ScreenedEmail.actions[:do_nothing]) }
-      it { should == false }
+      it { is_expected.to eq(false) }
       include_examples "when a ScreenedEmail record matches"
     end
   end
diff --git a/spec/models/screened_ip_address_spec.rb b/spec/models/screened_ip_address_spec.rb
index 27b67ddb5c..c62021ad59 100644
--- a/spec/models/screened_ip_address_spec.rb
+++ b/spec/models/screened_ip_address_spec.rb
@@ -6,18 +6,18 @@ describe ScreenedIpAddress do
 
   describe 'new record' do
     it 'sets a default action_type' do
-      described_class.create(valid_params).action_type.should == described_class.actions[:block]
+      expect(described_class.create(valid_params).action_type).to eq(described_class.actions[:block])
     end
 
     it 'sets an error when ip_address is invalid' do
-      described_class.create(valid_params.merge(ip_address: '99.99.99')).errors[:ip_address].should be_present
+      expect(described_class.create(valid_params.merge(ip_address: '99.99.99')).errors[:ip_address]).to be_present
     end
 
     it 'can set action_type using the action_name virtual attribute' do
-      described_class.new(valid_params.merge(action_name: :do_nothing)).action_type.should == described_class.actions[:do_nothing]
-      described_class.new(valid_params.merge(action_name: :block)).action_type.should == described_class.actions[:block]
-      described_class.new(valid_params.merge(action_name: 'do_nothing')).action_type.should == described_class.actions[:do_nothing]
-      described_class.new(valid_params.merge(action_name: 'block')).action_type.should == described_class.actions[:block]
+      expect(described_class.new(valid_params.merge(action_name: :do_nothing)).action_type).to eq(described_class.actions[:do_nothing])
+      expect(described_class.new(valid_params.merge(action_name: :block)).action_type).to eq(described_class.actions[:block])
+      expect(described_class.new(valid_params.merge(action_name: 'do_nothing')).action_type).to eq(described_class.actions[:do_nothing])
+      expect(described_class.new(valid_params.merge(action_name: 'block')).action_type).to eq(described_class.actions[:block])
     end
 
     it 'raises a useful exception when action is invalid' do
@@ -35,15 +35,15 @@ describe ScreenedIpAddress do
 
   describe "ip_address_with_mask" do
     it "returns nil when ip_address is nil" do
-      described_class.new.ip_address_with_mask.should == nil
+      expect(described_class.new.ip_address_with_mask).to eq(nil)
     end
 
     it "returns ip_address without mask if there is no mask" do
-      described_class.new(ip_address: "123.123.23.22").ip_address_with_mask.should == "123.123.23.22"
+      expect(described_class.new(ip_address: "123.123.23.22").ip_address_with_mask).to eq("123.123.23.22")
     end
 
     it "returns ip_address with mask" do
-      described_class.new(ip_address: "123.12.0.0/16").ip_address_with_mask.should == "123.12.0.0/16"
+      expect(described_class.new(ip_address: "123.12.0.0/16").ip_address_with_mask).to eq("123.12.0.0/16")
     end
   end
 
@@ -52,14 +52,14 @@ describe ScreenedIpAddress do
 
     def test_good_value(arg, expected)
       record.ip_address = arg
-      record.ip_address_with_mask.should == expected
+      expect(record.ip_address_with_mask).to eq(expected)
     end
 
     def test_bad_value(arg)
       r = described_class.new
       r.ip_address = arg
-      r.should_not be_valid
-      r.errors[:ip_address].should be_present
+      expect(r).not_to be_valid
+      expect(r.errors[:ip_address]).to be_present
     end
 
     it "handles valid ip addresses" do
@@ -95,14 +95,14 @@ describe ScreenedIpAddress do
     context 'ip_address is not being watched' do
       it 'should create a new record' do
         record = described_class.watch(ip_address)
-        record.should_not be_new_record
-        record.action_type.should == described_class.actions[:block]
+        expect(record).not_to be_new_record
+        expect(record.action_type).to eq(described_class.actions[:block])
       end
 
       it 'lets action_type be overridden' do
         record = described_class.watch(ip_address, action_type: described_class.actions[:do_nothing])
-        record.should_not be_new_record
-        record.action_type.should == described_class.actions[:do_nothing]
+        expect(record).not_to be_new_record
+        expect(record.action_type).to eq(described_class.actions[:do_nothing])
       end
 
       it "a record with subnet mask exists, but doesn't match" do
@@ -123,7 +123,7 @@ describe ScreenedIpAddress do
         end
 
         it 'returns the existing record' do
-          described_class.watch(ip_address_arg).should == existing
+          expect(described_class.watch(ip_address_arg)).to eq(existing)
         end
       end
 
@@ -149,91 +149,91 @@ describe ScreenedIpAddress do
     end
 
     it "doesn't block 10.0.0.0/8" do
-      described_class.watch('10.0.0.0').action_type.should == described_class.actions[:do_nothing]
-      described_class.watch('10.0.0.1').action_type.should == described_class.actions[:do_nothing]
-      described_class.watch('10.10.125.111').action_type.should == described_class.actions[:do_nothing]
+      expect(described_class.watch('10.0.0.0').action_type).to eq(described_class.actions[:do_nothing])
+      expect(described_class.watch('10.0.0.1').action_type).to eq(described_class.actions[:do_nothing])
+      expect(described_class.watch('10.10.125.111').action_type).to eq(described_class.actions[:do_nothing])
     end
 
     it "doesn't block 192.168.0.0/16" do
-      described_class.watch('192.168.0.5').action_type.should == described_class.actions[:do_nothing]
-      described_class.watch('192.168.10.1').action_type.should == described_class.actions[:do_nothing]
+      expect(described_class.watch('192.168.0.5').action_type).to eq(described_class.actions[:do_nothing])
+      expect(described_class.watch('192.168.10.1').action_type).to eq(described_class.actions[:do_nothing])
     end
 
     it "doesn't block 127.0.0.0/8" do
-      described_class.watch('127.0.0.1').action_type.should == described_class.actions[:do_nothing]
+      expect(described_class.watch('127.0.0.1').action_type).to eq(described_class.actions[:do_nothing])
     end
 
     it "doesn't block fc00::/7 addresses (IPv6)" do
-      described_class.watch('fd12:db8::ff00:42:8329').action_type.should == described_class.actions[:do_nothing]
+      expect(described_class.watch('fd12:db8::ff00:42:8329').action_type).to eq(described_class.actions[:do_nothing])
     end
   end
 
   describe '#should_block?' do
     it 'returns false when record does not exist' do
-      described_class.should_block?(ip_address).should eq(false)
+      expect(described_class.should_block?(ip_address)).to eq(false)
     end
 
     it 'returns false when no record matches' do
       Fabricate(:screened_ip_address, ip_address: '111.234.23.11', action_type: described_class.actions[:block])
-      described_class.should_block?('222.12.12.12').should eq(false)
+      expect(described_class.should_block?('222.12.12.12')).to eq(false)
     end
 
     context 'IPv4' do
       it 'returns false when when record matches and action is :do_nothing' do
         Fabricate(:screened_ip_address, ip_address: '111.234.23.11', action_type: described_class.actions[:do_nothing])
-        described_class.should_block?('111.234.23.11').should eq(false)
+        expect(described_class.should_block?('111.234.23.11')).to eq(false)
       end
 
       it 'returns true when when record matches and action is :block' do
         Fabricate(:screened_ip_address, ip_address: '111.234.23.11', action_type: described_class.actions[:block])
-        described_class.should_block?('111.234.23.11').should eq(true)
+        expect(described_class.should_block?('111.234.23.11')).to eq(true)
       end
     end
 
     context 'IPv6' do
       it 'returns false when when record matches and action is :do_nothing' do
         Fabricate(:screened_ip_address, ip_address: '2001:db8::ff00:42:8329', action_type: described_class.actions[:do_nothing])
-        described_class.should_block?('2001:db8::ff00:42:8329').should eq(false)
+        expect(described_class.should_block?('2001:db8::ff00:42:8329')).to eq(false)
       end
 
       it 'returns true when when record matches and action is :block' do
         Fabricate(:screened_ip_address, ip_address: '2001:db8::ff00:42:8329', action_type: described_class.actions[:block])
-        described_class.should_block?('2001:db8::ff00:42:8329').should eq(true)
+        expect(described_class.should_block?('2001:db8::ff00:42:8329')).to eq(true)
       end
     end
   end
 
   describe '#is_whitelisted?' do
     it 'returns false when record does not exist' do
-      described_class.is_whitelisted?(ip_address).should eq(false)
+      expect(described_class.is_whitelisted?(ip_address)).to eq(false)
     end
 
     it 'returns false when no record matches' do
       Fabricate(:screened_ip_address, ip_address: '111.234.23.11', action_type: described_class.actions[:do_nothing])
-      described_class.is_whitelisted?('222.12.12.12').should eq(false)
+      expect(described_class.is_whitelisted?('222.12.12.12')).to eq(false)
     end
 
     context 'IPv4' do
       it 'returns true when when record matches and action is :do_nothing' do
         Fabricate(:screened_ip_address, ip_address: '111.234.23.11', action_type: described_class.actions[:do_nothing])
-        described_class.is_whitelisted?('111.234.23.11').should eq(true)
+        expect(described_class.is_whitelisted?('111.234.23.11')).to eq(true)
       end
 
       it 'returns false when when record matches and action is :block' do
         Fabricate(:screened_ip_address, ip_address: '111.234.23.11', action_type: described_class.actions[:block])
-        described_class.is_whitelisted?('111.234.23.11').should eq(false)
+        expect(described_class.is_whitelisted?('111.234.23.11')).to eq(false)
       end
     end
 
     context 'IPv6' do
       it 'returns true when when record matches and action is :do_nothing' do
         Fabricate(:screened_ip_address, ip_address: '2001:db8::ff00:42:8329', action_type: described_class.actions[:do_nothing])
-        described_class.is_whitelisted?('2001:db8::ff00:42:8329').should eq(true)
+        expect(described_class.is_whitelisted?('2001:db8::ff00:42:8329')).to eq(true)
       end
 
       it 'returns false when when record matches and action is :block' do
         Fabricate(:screened_ip_address, ip_address: '2001:db8::ff00:42:8329', action_type: described_class.actions[:block])
-        described_class.is_whitelisted?('2001:db8::ff00:42:8329').should eq(false)
+        expect(described_class.is_whitelisted?('2001:db8::ff00:42:8329')).to eq(false)
       end
     end
   end
@@ -241,19 +241,19 @@ describe ScreenedIpAddress do
   describe '#block_login?' do
     context 'no allow_admin records exist' do
       it "returns false when user is nil" do
-        described_class.block_login?(nil, '123.12.12.12').should == false
+        expect(described_class.block_login?(nil, '123.12.12.12')).to eq(false)
       end
 
       it "returns false for non-admin user" do
-        described_class.block_login?(Fabricate.build(:user), '123.12.12.12').should == false
+        expect(described_class.block_login?(Fabricate.build(:user), '123.12.12.12')).to eq(false)
       end
 
       it "returns false for admin user" do
-        described_class.block_login?(Fabricate.build(:admin), '123.12.12.12').should == false
+        expect(described_class.block_login?(Fabricate.build(:admin), '123.12.12.12')).to eq(false)
       end
 
       it "returns false for admin user and ip_address arg is nil" do
-        described_class.block_login?(Fabricate.build(:admin), nil).should == false
+        expect(described_class.block_login?(Fabricate.build(:admin), nil)).to eq(false)
       end
     end
 
@@ -264,23 +264,23 @@ describe ScreenedIpAddress do
       end
 
       it "returns false when user is nil" do
-        described_class.block_login?(nil, @permitted_ip_address).should == false
+        expect(described_class.block_login?(nil, @permitted_ip_address)).to eq(false)
       end
 
       it "returns false for an admin user at the allowed ip address" do
-        described_class.block_login?(Fabricate.build(:admin), @permitted_ip_address).should == false
+        expect(described_class.block_login?(Fabricate.build(:admin), @permitted_ip_address)).to eq(false)
       end
 
       it "returns true for an admin user at another ip address" do
-        described_class.block_login?(Fabricate.build(:admin), '123.12.12.12').should == true
+        expect(described_class.block_login?(Fabricate.build(:admin), '123.12.12.12')).to eq(true)
       end
 
       it "returns false for regular user at allowed ip address" do
-        described_class.block_login?(Fabricate.build(:user), @permitted_ip_address).should == false
+        expect(described_class.block_login?(Fabricate.build(:user), @permitted_ip_address)).to eq(false)
       end
 
       it "returns false for regular user at another ip address" do
-        described_class.block_login?(Fabricate.build(:user), '123.12.12.12').should == false
+        expect(described_class.block_login?(Fabricate.build(:user), '123.12.12.12')).to eq(false)
       end
     end
   end
diff --git a/spec/models/screened_url_spec.rb b/spec/models/screened_url_spec.rb
index 54c8a045b1..bdebf88ecc 100644
--- a/spec/models/screened_url_spec.rb
+++ b/spec/models/screened_url_spec.rb
@@ -9,11 +9,11 @@ describe ScreenedUrl do
 
   describe "new record" do
     it "sets a default action_type" do
-      described_class.create(valid_params).action_type.should == described_class.actions[:do_nothing]
+      expect(described_class.create(valid_params).action_type).to eq(described_class.actions[:do_nothing])
     end
 
     it "last_match_at is null" do
-      described_class.create(valid_params).last_match_at.should == nil
+      expect(described_class.create(valid_params).last_match_at).to eq(nil)
     end
 
     it "normalizes the url and domain" do
@@ -30,46 +30,46 @@ describe ScreenedUrl do
     ['http://', 'HTTP://', 'https://', 'HTTPS://'].each do |prefix|
       it "strips #{prefix}" do
         @params = valid_params.merge(url: url.gsub('http://', prefix))
-        subject.url.should == url.gsub('http://', '')
+        expect(subject.url).to eq(url.gsub('http://', ''))
       end
     end
 
     it "strips trailing slash" do
       @params = valid_params.merge(url: 'silverbullet.in/')
-      subject.url.should == 'silverbullet.in'
+      expect(subject.url).to eq('silverbullet.in')
     end
 
     it "strips trailing slashes" do
       @params = valid_params.merge(url: 'silverbullet.in/buy///')
-      subject.url.should == 'silverbullet.in/buy'
+      expect(subject.url).to eq('silverbullet.in/buy')
     end
 
     it "downcases domains" do
       record1 = described_class.new(valid_params.merge(domain: 'DuB30.com', url: 'DuB30.com/Gems/Gems-of-Power'))
       record1.normalize
-      record1.domain.should == 'dub30.com'
-      record1.url.should == 'dub30.com/Gems/Gems-of-Power'
-      record1.should be_valid
+      expect(record1.domain).to eq('dub30.com')
+      expect(record1.url).to eq('dub30.com/Gems/Gems-of-Power')
+      expect(record1).to be_valid
 
       record2 = described_class.new(valid_params.merge(domain: 'DuB30.com', url: 'DuB30.com'))
       record2.normalize
-      record2.domain.should == 'dub30.com'
-      record2.url.should == 'dub30.com'
-      record2.should be_valid
+      expect(record2.domain).to eq('dub30.com')
+      expect(record2.url).to eq('dub30.com')
+      expect(record2).to be_valid
     end
 
     it "strips www. from domains" do
       record1 = described_class.new(valid_params.merge(domain: 'www.DuB30.com', url: 'www.DuB30.com/Gems/Gems-of-Power'))
       record1.normalize
-      record1.domain.should == 'dub30.com'
+      expect(record1.domain).to eq('dub30.com')
 
       record2 = described_class.new(valid_params.merge(domain: 'WWW.DuB30.cOM', url: 'WWW.DuB30.com/Gems/Gems-of-Power'))
       record2.normalize
-      record2.domain.should == 'dub30.com'
+      expect(record2.domain).to eq('dub30.com')
 
       record3 = described_class.new(valid_params.merge(domain: 'www.trolls.spammers.com', url: 'WWW.DuB30.com/Gems/Gems-of-Power'))
       record3.normalize
-      record3.domain.should == 'trolls.spammers.com'
+      expect(record3.domain).to eq('trolls.spammers.com')
     end
 
     it "doesn't modify the url argument" do
@@ -88,17 +88,17 @@ describe ScreenedUrl do
 
   describe 'find_match' do
     it 'returns nil when there is no match' do
-      described_class.find_match('http://spamspot.com/buy/it').should == nil
+      expect(described_class.find_match('http://spamspot.com/buy/it')).to eq(nil)
     end
 
     it 'returns the record when there is an exact match' do
       match = described_class.create(valid_params)
-      described_class.find_match(valid_params[:url]).should == match
+      expect(described_class.find_match(valid_params[:url])).to eq(match)
     end
 
     it 'ignores case of the domain' do
       match = described_class.create(valid_params.merge(url: 'spamexchange.com/Good/Things'))
-      described_class.find_match("http://SPAMExchange.com/Good/Things").should == match
+      expect(described_class.find_match("http://SPAMExchange.com/Good/Things")).to eq(match)
     end
   end
 
@@ -106,14 +106,14 @@ describe ScreenedUrl do
     context 'url is not being blocked' do
       it 'creates a new record with default action of :do_nothing' do
         record = described_class.watch(url, domain)
-        record.should_not be_new_record
-        record.action_type.should == described_class.actions[:do_nothing]
+        expect(record).not_to be_new_record
+        expect(record.action_type).to eq(described_class.actions[:do_nothing])
       end
 
       it 'lets action_type be overriden' do
         record = described_class.watch(url, domain, action_type: described_class.actions[:block])
-        record.should_not be_new_record
-        record.action_type.should == described_class.actions[:block]
+        expect(record).not_to be_new_record
+        expect(record.action_type).to eq(described_class.actions[:block])
       end
     end
 
@@ -125,7 +125,7 @@ describe ScreenedUrl do
       end
 
       it "returns the existing record" do
-        described_class.watch(url, domain).should == existing
+        expect(described_class.watch(url, domain)).to eq(existing)
       end
     end
   end
diff --git a/spec/models/search_observer_spec.rb b/spec/models/search_observer_spec.rb
index ee4101471c..34455c4155 100644
--- a/spec/models/search_observer_spec.rb
+++ b/spec/models/search_observer_spec.rb
@@ -13,12 +13,12 @@ describe SearchObserver do
   it 'correctly indexes chinese' do
     SiteSetting.default_locale = 'zh_CN'
     data = "你好世界"
-    data.split(" ").length.should == 1
+    expect(data.split(" ").length).to eq(1)
 
     SearchObserver.update_posts_index(99, "你好世界", "", nil)
 
     row = get_row(99)
-    row.raw_data.split(' ').length.should == 2
+    expect(row.raw_data.split(' ').length).to eq(2)
   end
 
   it 'correctly indexes a post' do
@@ -28,13 +28,13 @@ describe SearchObserver do
 
     row = get_row(99)
 
-    row.raw_data.should == "This is a test"
-    row.locale.should == "en"
+    expect(row.raw_data).to eq("This is a test")
+    expect(row.locale).to eq("en")
 
     SearchObserver.update_posts_index(99, "tester", "", nil)
 
     row = get_row(99)
 
-    row.raw_data.should == "tester"
+    expect(row.raw_data).to eq("tester")
   end
 end
diff --git a/spec/models/site_customization_spec.rb b/spec/models/site_customization_spec.rb
index ce967487fa..b3ab1a8459 100644
--- a/spec/models/site_customization_spec.rb
+++ b/spec/models/site_customization_spec.rb
@@ -2,6 +2,10 @@ require 'spec_helper'
 
 describe SiteCustomization do
 
+  before do
+    SiteCustomization.clear_cache!
+  end
+
   let :user do
     Fabricate(:user)
   end
@@ -20,171 +24,72 @@ describe SiteCustomization do
 
   it 'should set default key when creating a new customization' do
     s = SiteCustomization.create!(name: 'my name', user_id: user.id)
-    s.key.should_not == nil
+    expect(s.key).not_to eq(nil)
   end
 
-  context 'caching' do
+  it 'can enable more than one style at once' do
+    c1 = SiteCustomization.create!(name: '2', user_id: user.id, header: 'World',
+                              enabled: true, mobile_header: 'hi', footer: 'footer',
+                              stylesheet: '.hello{.world {color: blue;}}')
 
-    context 'enabled style' do
-      before do
-        @customization = customization
-      end
+    SiteCustomization.create!(name: '1', user_id: user.id, header: 'Hello',
+                              enabled: true, mobile_footer: 'mfooter',
+                              mobile_stylesheet: '.hello{margin: 1px;}',
+                              stylesheet: 'p{width: 1px;}'
+                             )
 
-      it 'finds no style when none enabled' do
-        SiteCustomization.enabled_style_key.should == nil
-      end
+    expect(SiteCustomization.custom_header).to eq("Hello\nWorld")
+    expect(SiteCustomization.custom_header(nil, :mobile)).to eq("hi")
+    expect(SiteCustomization.custom_footer(nil, :mobile)).to eq("mfooter")
+    expect(SiteCustomization.custom_footer).to eq("footer")
 
+    desktop_css = SiteCustomization.custom_stylesheet
+    expect(desktop_css).to match(Regexp.new("#{SiteCustomization::ENABLED_KEY}.css\\?target=desktop"))
 
-      it 'finds the enabled style' do
-        @customization.enabled = true
-        @customization.save
-        SiteCustomization.enabled_style_key.should == @customization.key
-      end
+    mobile_css = SiteCustomization.custom_stylesheet(nil, :mobile)
+    expect(mobile_css).to match(Regexp.new("#{SiteCustomization::ENABLED_KEY}.css\\?target=mobile"))
 
-      it 'finds no enabled style on other sites' do
-        @customization.enabled = true
-        @customization.save
+    expect(SiteCustomization.enabled_stylesheet_contents).to match(/\.hello \.world/)
 
-        RailsMultisite::ConnectionManagement.expects(:current_db).returns("foo").twice
-        # the mocking is tricky, lets remove the record so we can properly pretend we are on another db
-        #  this bypasses the before / after stuff
-        SiteCustomization.exec_sql('delete from site_customizations')
+    # cache expiry
+    c1.enabled = false
+    c1.save
 
-        SiteCustomization.enabled_style_key.should == nil
-      end
-    end
+    expect(SiteCustomization.custom_stylesheet).not_to eq(desktop_css)
+    expect(SiteCustomization.enabled_stylesheet_contents).not_to match(/\.hello \.world/)
+  end
 
-    it 'ensure stylesheet is on disk on first fetch' do
-      c = customization
-      c.remove_from_cache!
-      File.delete(c.stylesheet_fullpath)
-      File.delete(c.stylesheet_fullpath(:mobile))
+  it 'should be able to look up stylesheets by key' do
+    c = SiteCustomization.create!(name: '2', user_id: user.id,
+                              enabled: true,
+                              stylesheet: '.hello{.world {color: blue;}}',
+                              mobile_stylesheet: '.world{.hello{color: black;}}')
 
-      SiteCustomization.custom_stylesheet(c.key)
-      File.exists?(c.stylesheet_fullpath).should == true
-      File.exists?(c.stylesheet_fullpath(:mobile)).should == true
-    end
-
-    context '#custom_stylesheet' do
-      it 'should allow me to lookup a filename containing my preview stylesheet' do
-        SiteCustomization.custom_stylesheet(customization.key).should ==
-          ""
-      end
-
-      it "should return blank link tag for mobile if mobile_stylesheet is blank" do
-        SiteCustomization.custom_stylesheet(customization.key, :mobile).should == ""
-      end
-
-      it "should return link tag for mobile custom stylesheet" do
-        SiteCustomization.custom_stylesheet(customization_with_mobile.key, :mobile).should ==
-          ""
-      end
-    end
-
-    context '#custom_header' do
-      it "returns empty string when there is no custom header" do
-        c = SiteCustomization.create!(customization_params.merge(header: ''))
-        SiteCustomization.custom_header(c.key).should == ''
-      end
-
-      it "can return the custom header html" do
-        SiteCustomization.custom_header(customization.key).should == customization_params[:header]
-      end
-
-      it "returns empty string for mobile header when there's no custom mobile header" do
-        SiteCustomization.custom_header(customization.key, :mobile).should == ''
-      end
-
-      it "can return the custom mobile header html" do
-        SiteCustomization.custom_header(customization_with_mobile.key, :mobile).should == customization_with_mobile.mobile_header
-      end
-    end
-
-    it 'should fix stylesheet files after changing the stylesheet' do
-      old_file = customization.stylesheet_fullpath
-      original = SiteCustomization.custom_stylesheet(customization.key)
-
-      File.exists?(old_file).should == true
-      customization.stylesheet = "div { clear:both; }"
-      customization.save
-
-      SiteCustomization.custom_stylesheet(customization.key).should_not == original
-    end
-
-    it 'should fix mobile stylesheet files after changing the mobile_stylesheet' do
-      old_file = customization_with_mobile.stylesheet_fullpath(:mobile)
-      original = SiteCustomization.custom_stylesheet(customization_with_mobile.key, :mobile)
-
-      File.exists?(old_file).should == true
-      customization_with_mobile.mobile_stylesheet = "div { clear:both; }"
-      customization_with_mobile.save
-
-      SiteCustomization.custom_stylesheet(customization_with_mobile.key).should_not == original
-    end
-
-    it 'should delete old stylesheet files after deleting' do
-      old_file = customization.stylesheet_fullpath
-      customization.ensure_stylesheets_on_disk!
-      customization.destroy
-      File.exists?(old_file).should == false
-    end
-
-    it 'should delete old mobile stylesheet files after deleting' do
-      old_file = customization_with_mobile.stylesheet_fullpath(:mobile)
-      customization_with_mobile.ensure_stylesheets_on_disk!
-      customization_with_mobile.destroy
-      File.exists?(old_file).should == false
-    end
-
-    it 'should nuke old revs out of the cache' do
-      old_style = SiteCustomization.custom_stylesheet(customization.key)
-
-      customization.stylesheet = "hello worldz"
-      customization.save
-      SiteCustomization.custom_stylesheet(customization.key).should_not == old_style
-    end
-
-    it 'should nuke old revs out of the cache for mobile too' do
-      old_style = SiteCustomization.custom_stylesheet(customization_with_mobile.key)
-
-      customization_with_mobile.mobile_stylesheet = "hello worldz"
-      customization_with_mobile.save
-      SiteCustomization.custom_stylesheet(customization.key, :mobile).should_not == old_style
-    end
-
-
-    it 'should compile scss' do
-      c = SiteCustomization.create!(user_id: user.id, name: "test", stylesheet: '$black: #000; #a { color: $black; }', header: '')
-      s = c.stylesheet_baked.gsub(' ', '').gsub("\n", '')
-      (s.include?("#a{color:#000;}") || s.include?("#a{color:black;}")).should == true
-    end
-
-    it 'should compile mobile scss' do
-      c = SiteCustomization.create!(user_id: user.id, name: "test", stylesheet: '', header: '', mobile_stylesheet: '$black: #000; #a { color: $black; }', mobile_header: '')
-      s = c.mobile_stylesheet_baked.gsub(' ', '').gsub("\n", '')
-      (s.include?("#a{color:#000;}") || s.include?("#a{color:black;}")).should == true
-    end
-
-    it 'should allow including discourse styles' do
-      c = SiteCustomization.create!(user_id: user.id, name: "test", stylesheet: '@import "desktop";', mobile_stylesheet: '@import "mobile";')
-      c.stylesheet_baked.should_not =~ /Syntax error/
-      c.stylesheet_baked.length.should > 1000
-      c.mobile_stylesheet_baked.should_not =~ /Syntax error/
-      c.mobile_stylesheet_baked.length.should > 1000
-    end
-
-    it 'should provide an awesome error on failure' do
-      c = SiteCustomization.create!(user_id: user.id, name: "test", stylesheet: "$black: #000; #a { color: $black; }\n\n\nboom", header: '')
-      c.stylesheet_baked.should =~ /Syntax error/
-      c.mobile_stylesheet_baked.should_not be_present
-    end
-
-    it 'should provide an awesome error on failure for mobile too' do
-      c = SiteCustomization.create!(user_id: user.id, name: "test", stylesheet: '', header: '', mobile_stylesheet: "$black: #000; #a { color: $black; }\n\n\nboom", mobile_header: '')
-      c.mobile_stylesheet_baked.should =~ /Syntax error/
-      c.stylesheet_baked.should_not be_present
-    end
+    expect(SiteCustomization.custom_stylesheet(c.key, :mobile)).to match(Regexp.new("#{c.key}.css\\?target=mobile"))
+    expect(SiteCustomization.custom_stylesheet(c.key)).to match(Regexp.new("#{c.key}.css\\?target=desktop"))
 
   end
 
+
+  it 'should allow including discourse styles' do
+    c = SiteCustomization.create!(user_id: user.id, name: "test", stylesheet: '@import "desktop";', mobile_stylesheet: '@import "mobile";')
+    expect(c.stylesheet_baked).not_to match(/Syntax error/)
+    expect(c.stylesheet_baked.length).to be > 1000
+    expect(c.mobile_stylesheet_baked).not_to match(/Syntax error/)
+    expect(c.mobile_stylesheet_baked.length).to be > 1000
+  end
+
+  it 'should provide an awesome error on failure' do
+    c = SiteCustomization.create!(user_id: user.id, name: "test", stylesheet: "$black: #000; #a { color: $black; }\n\n\nboom", header: '')
+    expect(c.stylesheet_baked).to match(/Syntax error/)
+    expect(c.mobile_stylesheet_baked).not_to be_present
+  end
+
+  it 'should provide an awesome error on failure for mobile too' do
+    c = SiteCustomization.create!(user_id: user.id, name: "test", stylesheet: '', header: '', mobile_stylesheet: "$black: #000; #a { color: $black; }\n\n\nboom", mobile_header: '')
+    expect(c.mobile_stylesheet_baked).to match(/Syntax error/)
+    expect(c.stylesheet_baked).not_to be_present
+  end
+
+
 end
diff --git a/spec/models/site_setting_spec.rb b/spec/models/site_setting_spec.rb
index 778cc3536b..ced13c11f5 100644
--- a/spec/models/site_setting_spec.rb
+++ b/spec/models/site_setting_spec.rb
@@ -7,30 +7,31 @@ describe SiteSetting do
   describe "normalized_embeddable_host" do
     it 'returns the `embeddable_host` value' do
       SiteSetting.stubs(:embeddable_host).returns("eviltrout.com")
-      SiteSetting.normalized_embeddable_host.should == "eviltrout.com"
+      expect(SiteSetting.normalized_embeddable_host).to eq("eviltrout.com")
     end
 
     it 'strip http from `embeddable_host` value' do
       SiteSetting.stubs(:embeddable_host).returns("http://eviltrout.com")
-      SiteSetting.normalized_embeddable_host.should == "eviltrout.com"
+      expect(SiteSetting.normalized_embeddable_host).to eq("eviltrout.com")
     end
 
     it 'strip https from `embeddable_host` value' do
       SiteSetting.stubs(:embeddable_host).returns("https://eviltrout.com")
-      SiteSetting.normalized_embeddable_host.should == "eviltrout.com"
+      expect(SiteSetting.normalized_embeddable_host).to eq("eviltrout.com")
     end
   end
 
   describe 'topic_title_length' do
     it 'returns a range of min/max topic title length' do
-      SiteSetting.topic_title_length.should ==
+      expect(SiteSetting.topic_title_length).to eq(
         (SiteSetting.defaults[:min_topic_title_length]..SiteSetting.defaults[:max_topic_title_length])
+      )
     end
   end
 
   describe 'post_length' do
     it 'returns a range of min/max post length' do
-      SiteSetting.post_length.should == (SiteSetting.defaults[:min_post_length]..SiteSetting.defaults[:max_post_length])
+      expect(SiteSetting.post_length).to eq(SiteSetting.defaults[:min_post_length]..SiteSetting.defaults[:max_post_length])
     end
   end
 
@@ -82,13 +83,13 @@ describe SiteSetting do
   describe "scheme" do
 
     it "returns http when ssl is disabled" do
-      SiteSetting.expects(:use_https).returns(false)
-      SiteSetting.scheme.should == "http"
+      SiteSetting.use_https = false
+      expect(SiteSetting.scheme).to eq("http")
     end
 
     it "returns https when using ssl" do
       SiteSetting.expects(:use_https).returns(true)
-      SiteSetting.scheme.should == "https"
+      expect(SiteSetting.scheme).to eq("https")
     end
 
   end
diff --git a/spec/models/site_spec.rb b/spec/models/site_spec.rb
index 717c6e9ab6..392d1bf47a 100644
--- a/spec/models/site_spec.rb
+++ b/spec/models/site_spec.rb
@@ -6,26 +6,26 @@ describe Site do
     category = Fabricate(:category)
     user = Fabricate(:user)
 
-    Site.new(Guardian.new(user)).categories.count.should == 2
+    expect(Site.new(Guardian.new(user)).categories.count).to eq(2)
 
     category.set_permissions(:everyone => :create_post)
     category.save
 
     guardian = Guardian.new(user)
 
-    Site.new(guardian)
+    expect(Site.new(guardian)
         .categories
         .keep_if{|c| c.name == category.name}
         .first
-        .permission
-        .should_not == CategoryGroup.permission_types[:full]
+        .permission)
+        .not_to eq(CategoryGroup.permission_types[:full])
 
     # If a parent category is not visible, the child categories should not be returned
     category.set_permissions(:staff => :full)
     category.save
 
     sub_category = Fabricate(:category, parent_category_id: category.id)
-    Site.new(guardian).categories.should_not include(sub_category)
+    expect(Site.new(guardian).categories).not_to include(sub_category)
   end
 
 end
diff --git a/spec/models/site_text_spec.rb b/spec/models/site_text_spec.rb
index f775b1a2e5..2245683593 100644
--- a/spec/models/site_text_spec.rb
+++ b/spec/models/site_text_spec.rb
@@ -2,23 +2,23 @@ require 'spec_helper'
 
 describe SiteText do
 
-  it { should validate_presence_of :value }
+  it { is_expected.to validate_presence_of :value }
 
 
   describe "#text_for" do
 
     it "returns an empty string for a missing text_type" do
-      SiteText.text_for('something_random').should == ""
+      expect(SiteText.text_for('something_random')).to eq("")
     end
 
     it "returns the default value for a text` type with a default" do
-      SiteText.text_for("usage_tips").should be_present
+      expect(SiteText.text_for("usage_tips")).to be_present
     end
 
     it "correctly expires and bypasses cache" do
       SiteSetting.enable_sso = false
       text = SiteText.create!(text_type: "got.sso", value: "got sso: %{enable_sso}")
-      SiteText.text_for("got.sso").should == "got sso: false"
+      expect(SiteText.text_for("got.sso")).to eq("got sso: false")
       SiteText.text_for("got.sso").frozen? == true
 
       SiteSetting.enable_sso = true
@@ -33,14 +33,14 @@ describe SiteText do
         SiteText.text_for("got.sso") == "I gots sso: true"
       end
 
-      SiteText.text_for("got.sso", enable_sso: "frog").should == "I gots sso: frog"
+      expect(SiteText.text_for("got.sso", enable_sso: "frog")).to eq("I gots sso: frog")
     end
 
     context "without replacements" do
       let!(:site_text) { Fabricate(:site_text_basic) }
 
       it "returns the simple string" do
-        SiteText.text_for('breaking.bad').should == "best show ever"
+        expect(SiteText.text_for('breaking.bad')).to eq("best show ever")
       end
 
     end
@@ -50,15 +50,15 @@ describe SiteText do
       let(:replacements) { {flower: 'roses', food: 'grapes'} }
 
       it "returns the correct string with replacements" do
-        SiteText.text_for('great.poem', replacements).should == "roses are red. grapes are blue."
+        expect(SiteText.text_for('great.poem', replacements)).to eq("roses are red. grapes are blue.")
       end
 
       it "doesn't mind extra keys in the replacements" do
-        SiteText.text_for('great.poem', replacements.merge(extra: 'key')).should == "roses are red. grapes are blue."
+        expect(SiteText.text_for('great.poem', replacements.merge(extra: 'key'))).to eq("roses are red. grapes are blue.")
       end
 
       it "ignores missing keys" do
-        SiteText.text_for('great.poem', flower: 'roses').should == "roses are red. %{food} are blue."
+        expect(SiteText.text_for('great.poem', flower: 'roses')).to eq("roses are red. %{food} are blue.")
       end
     end
 
@@ -68,12 +68,12 @@ describe SiteText do
 
       it "replaces site_settings by default" do
         SiteSetting.title = "Evil Trout"
-        SiteText.text_for('site.replacement').should == "Evil Trout is evil."
+        expect(SiteText.text_for('site.replacement')).to eq("Evil Trout is evil.")
       end
 
       it "allows us to override the default site settings" do
         SiteSetting.title = "Evil Trout"
-        SiteText.text_for('site.replacement', title: 'Good Tuna').should == "Good Tuna is evil."
+        expect(SiteText.text_for('site.replacement', title: 'Good Tuna')).to eq("Good Tuna is evil.")
       end
 
     end
diff --git a/spec/models/topic_allowed_user_spec.rb b/spec/models/topic_allowed_user_spec.rb
index 5f45cb0edb..c3a8a85c25 100644
--- a/spec/models/topic_allowed_user_spec.rb
+++ b/spec/models/topic_allowed_user_spec.rb
@@ -1,6 +1,6 @@
 require 'spec_helper'
 
 describe TopicAllowedUser do
-  it { should belong_to :user }
-  it { should belong_to :topic }
+  it { is_expected.to belong_to :user }
+  it { is_expected.to belong_to :topic }
 end
diff --git a/spec/models/topic_embed_spec.rb b/spec/models/topic_embed_spec.rb
index fcb44248d4..c1bf49c7e9 100644
--- a/spec/models/topic_embed_spec.rb
+++ b/spec/models/topic_embed_spec.rb
@@ -2,9 +2,9 @@ require 'spec_helper'
 
 describe TopicEmbed do
 
-  it { should belong_to :topic }
-  it { should belong_to :post }
-  it { should validate_presence_of :embed_url }
+  it { is_expected.to belong_to :topic }
+  it { is_expected.to belong_to :post }
+  it { is_expected.to validate_presence_of :embed_url }
 
   context '.import' do
 
@@ -14,42 +14,42 @@ describe TopicEmbed do
     let(:contents) { "hello world new post hello " }
 
     it "returns nil when the URL is malformed" do
-      TopicEmbed.import(user, "invalid url", title, contents).should == nil
-      TopicEmbed.count.should == 0
+      expect(TopicEmbed.import(user, "invalid url", title, contents)).to eq(nil)
+      expect(TopicEmbed.count).to eq(0)
     end
 
     context 'creation of a post' do
       let!(:post) { TopicEmbed.import(user, url, title, contents) }
 
       it "works as expected with a new URL" do
-        post.should be_present
+        expect(post).to be_present
 
         # It uses raw_html rendering
-        post.cook_method.should == Post.cook_methods[:raw_html]
-        post.cooked.should == post.raw
+        expect(post.cook_method).to eq(Post.cook_methods[:raw_html])
+        expect(post.cooked).to eq(post.raw)
 
         # It converts relative URLs to absolute
-        post.cooked.start_with?("hello world new post hello ").should == true
+        expect(post.cooked.start_with?("hello world new post hello ")).to eq(true)
 
-        post.topic.has_topic_embed?.should == true
-        TopicEmbed.where(topic_id: post.topic_id).should be_present
+        expect(post.topic.has_topic_embed?).to eq(true)
+        expect(TopicEmbed.where(topic_id: post.topic_id)).to be_present
       end
 
       it "Supports updating the post" do
         post = TopicEmbed.import(user, url, title, "muhahaha new contents!")
-        post.cooked.should =~ /new contents/
+        expect(post.cooked).to match(/new contents/)
       end
 
       it "Should leave uppercase Feed Entry URL untouched in content" do
         cased_url = 'http://eviltrout.com/ABCD'
         post = TopicEmbed.import(user, cased_url, title, "some random content")
-        post.cooked.should =~ /#{cased_url}/
+        expect(post.cooked).to match(/#{cased_url}/)
       end
 
       it "Should leave lowercase Feed Entry URL untouched in content" do
         cased_url = 'http://eviltrout.com/abcd'
         post = TopicEmbed.import(user, cased_url, title, "some random content")
-        post.cooked.should =~ /#{cased_url}/
+        expect(post.cooked).to match(/#{cased_url}/)
       end
     end
 
diff --git a/spec/models/topic_featured_users_spec.rb b/spec/models/topic_featured_users_spec.rb
index 2603d96508..593cda22ed 100644
--- a/spec/models/topic_featured_users_spec.rb
+++ b/spec/models/topic_featured_users_spec.rb
@@ -21,10 +21,10 @@ describe TopicFeaturedUsers do
 
     t.reload
 
-    t.featured_user1_id.should == p2.user_id
-    t.featured_user2_id.should == p4.user_id
-    t.featured_user3_id.should == nil
-    t.featured_user4_id.should == nil
+    expect(t.featured_user1_id).to eq(p2.user_id)
+    expect(t.featured_user2_id).to eq(p4.user_id)
+    expect(t.featured_user3_id).to eq(nil)
+    expect(t.featured_user4_id).to eq(nil)
 
 
   end
diff --git a/spec/models/topic_invite_spec.rb b/spec/models/topic_invite_spec.rb
index 620fd149d6..d93e9eb34f 100644
--- a/spec/models/topic_invite_spec.rb
+++ b/spec/models/topic_invite_spec.rb
@@ -2,9 +2,9 @@ require 'spec_helper'
 
 describe TopicInvite do
 
-  it { should belong_to :topic }
-  it { should belong_to :invite }
-  it { should validate_presence_of :topic_id }
-  it { should validate_presence_of :invite_id }
+  it { is_expected.to belong_to :topic }
+  it { is_expected.to belong_to :invite }
+  it { is_expected.to validate_presence_of :topic_id }
+  it { is_expected.to validate_presence_of :invite_id }
 
 end
diff --git a/spec/models/topic_link_click_spec.rb b/spec/models/topic_link_click_spec.rb
index 9fd7d1f24a..2fd11c063e 100644
--- a/spec/models/topic_link_click_spec.rb
+++ b/spec/models/topic_link_click_spec.rb
@@ -2,9 +2,9 @@ require 'spec_helper'
 
 describe TopicLinkClick do
 
-  it { should belong_to :topic_link }
-  it { should belong_to :user }
-  it { should validate_presence_of :topic_link_id }
+  it { is_expected.to belong_to :topic_link }
+  it { is_expected.to belong_to :user }
+  it { is_expected.to validate_presence_of :topic_link_id }
 
   def test_uri
     URI.parse('http://test.host')
@@ -19,7 +19,7 @@ describe TopicLinkClick do
     end
 
     it 'has 0 clicks at first' do
-      @topic_link.clicks.should == 0
+      expect(@topic_link.clicks).to eq(0)
     end
 
     context 'create' do
@@ -28,16 +28,16 @@ describe TopicLinkClick do
       end
 
       it 'creates the forum topic link click' do
-        TopicLinkClick.count.should == 1
+        expect(TopicLinkClick.count).to eq(1)
       end
 
       it 'has 0 clicks at first' do
         @topic_link.reload
-        @topic_link.clicks.should == 1
+        expect(@topic_link.clicks).to eq(1)
       end
 
       it 'serializes and deserializes the IP' do
-        TopicLinkClick.first.ip_address.to_s.should == '192.168.1.1'
+        expect(TopicLinkClick.first.ip_address.to_s).to eq('192.168.1.1')
       end
 
     end
@@ -48,15 +48,15 @@ describe TopicLinkClick do
         let(:click) { TopicLinkClick.create_from(url: "url that doesn't exist", post_id: @post.id, ip: '127.0.0.1') }
 
         it "returns nil" do
-          click.should == nil
+          expect(click).to eq(nil)
         end
       end
 
       context 'clicking on your own link' do
         it "should not record the click" do
-          lambda {
+          expect {
             TopicLinkClick.create_from(url: @topic_link.url, post_id: @post.id, ip: '127.0.0.0', user_id: @post.user_id)
-          }.should_not change(TopicLinkClick, :count)
+          }.not_to change(TopicLinkClick, :count)
         end
       end
 
@@ -67,18 +67,39 @@ describe TopicLinkClick do
         end
 
         it 'creates a click' do
-          @click.should be_present
-          @click.topic_link.should == @topic_link
-          @url.should == @topic_link.url
+          expect(@click).to be_present
+          expect(@click.topic_link).to eq(@topic_link)
+          expect(@url).to eq(@topic_link.url)
         end
 
         context "clicking again" do
           it "should not record the click due to rate limiting" do
-            -> { TopicLinkClick.create_from(url: @topic_link.url, post_id: @post.id, ip: '127.0.0.1') }.should_not change(TopicLinkClick, :count)
+            expect { TopicLinkClick.create_from(url: @topic_link.url, post_id: @post.id, ip: '127.0.0.1') }.not_to change(TopicLinkClick, :count)
           end
         end
       end
 
+      context "relative urls" do
+        let(:host) { URI.parse(Discourse.base_url).host }
+
+        it 'returns the url' do
+          url = TopicLinkClick.create_from(url: '/relative-url', post_id: @post.id, ip: '127.0.0.1')
+          expect(url).to eq("/relative-url")
+        end
+
+        it 'finds a protocol relative urls with a host' do
+          url = "//#{host}/relative-url"
+          redirect = TopicLinkClick.create_from(url: url)
+          expect(redirect).to eq(url)
+        end
+
+        it "returns the url if it's on our host" do
+          url = "http://#{host}/relative-url"
+          redirect = TopicLinkClick.create_from(url: url)
+          expect(redirect).to eq(url)
+        end
+      end
+
       context 'with a HTTPS version of the same URL' do
         before do
           @url = TopicLinkClick.create_from(url: 'https://twitter.com', topic_id: @topic.id, ip: '127.0.0.3')
@@ -86,9 +107,9 @@ describe TopicLinkClick do
         end
 
         it 'creates a click' do
-          @click.should be_present
-          @click.topic_link.should == @topic_link
-          @url.should == 'https://twitter.com'
+          expect(@click).to be_present
+          expect(@click.topic_link).to eq(@topic_link)
+          expect(@url).to eq('https://twitter.com')
         end
       end
 
@@ -99,9 +120,9 @@ describe TopicLinkClick do
         end
 
         it 'creates a click' do
-          @click.should be_present
-          @click.topic_link.should == @topic_link
-          @url.should == @topic_link.url
+          expect(@click).to be_present
+          expect(@click.topic_link).to eq(@topic_link)
+          expect(@url).to eq(@topic_link.url)
         end
 
       end
diff --git a/spec/models/topic_link_spec.rb b/spec/models/topic_link_spec.rb
index cbe5744aad..41eca976f6 100644
--- a/spec/models/topic_link_spec.rb
+++ b/spec/models/topic_link_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
 
 describe TopicLink do
 
-  it { should validate_presence_of :url }
+  it { is_expected.to validate_presence_of :url }
 
   def test_uri
     URI.parse(Discourse.base_url)
@@ -20,7 +20,7 @@ describe TopicLink do
     ftl = TopicLink.new(url: "/t/#{topic.id}",
                               topic_id: topic.id,
                               link_topic_id: topic.id)
-    ftl.valid?.should == false
+    expect(ftl.valid?).to eq(false)
   end
 
   describe 'external links' do
@@ -37,13 +37,13 @@ http://b.com/#{'a'*500}
 
     it 'works' do
       # has the forum topic links
-      topic.topic_links.count.should == 2
+      expect(topic.topic_links.count).to eq(2)
 
       # works with markdown links
-      topic.topic_links.exists?(url: "http://a.com/").should == true
+      expect(topic.topic_links.exists?(url: "http://a.com/")).to eq(true)
 
       #works with markdown links followed by a period
-      topic.topic_links.exists?(url: "http://b.com/b").should == true
+      expect(topic.topic_links.exists?(url: "http://b.com/b")).to eq(true)
     end
 
   end
@@ -66,9 +66,9 @@ http://b.com/#{'a'*500}
 
       link = topic.topic_links.first
       # should have a link
-      link.should be_present
+      expect(link).to be_present
       # should be the canonical URL
-      link.url.should == url
+      expect(link.url).to eq(url)
     end
 
 
@@ -95,24 +95,24 @@ http://b.com/#{'a'*500}
         TopicLink.extract_from(linked_post)
 
         link = topic.topic_links.first
-        link.should be_present
-        link.should be_internal
-        link.url.should == url
-        link.domain.should == test_uri.host
+        expect(link).to be_present
+        expect(link).to be_internal
+        expect(link.url).to eq(url)
+        expect(link.domain).to eq(test_uri.host)
         link.link_topic_id == other_topic.id
-        link.should_not be_reflection
+        expect(link).not_to be_reflection
 
         reflection = other_topic.topic_links.first
 
-        reflection.should be_present
-        reflection.should be_reflection
-        reflection.post_id.should be_present
-        reflection.domain.should == test_uri.host
-        reflection.url.should == "http://#{test_uri.host}/t/unique-topic-name/#{topic.id}/#{linked_post.post_number}"
-        reflection.link_topic_id.should == topic.id
-        reflection.link_post_id.should == linked_post.id
+        expect(reflection).to be_present
+        expect(reflection).to be_reflection
+        expect(reflection.post_id).to be_present
+        expect(reflection.domain).to eq(test_uri.host)
+        expect(reflection.url).to eq("http://#{test_uri.host}/t/unique-topic-name/#{topic.id}/#{linked_post.post_number}")
+        expect(reflection.link_topic_id).to eq(topic.id)
+        expect(reflection.link_post_id).to eq(linked_post.id)
 
-        reflection.user_id.should == link.user_id
+        expect(reflection.user_id).to eq(link.user_id)
       end
 
       context 'removing a link' do
@@ -123,9 +123,9 @@ http://b.com/#{'a'*500}
         end
 
         it 'should remove the link' do
-          topic.topic_links.where(post_id: post.id).should be_blank
+          expect(topic.topic_links.where(post_id: post.id)).to be_blank
           # should remove the reflected link
-          other_topic.topic_links.should be_blank
+          expect(other_topic.topic_links).to be_blank
         end
       end
     end
@@ -137,7 +137,7 @@ http://b.com/#{'a'*500}
       end
 
       it 'does not extract a link' do
-        topic.topic_links.should be_blank
+        expect(topic.topic_links).to be_blank
       end
     end
 
@@ -148,7 +148,7 @@ http://b.com/#{'a'*500}
       end
 
       it 'does not extract a link' do
-        topic.topic_links.should be_present
+        expect(topic.topic_links).to be_present
       end
     end
 
@@ -160,7 +160,7 @@ http://b.com/#{'a'*500}
       end
 
       it 'does not extract a link' do
-        topic.topic_links.should be_blank
+        expect(topic.topic_links).to be_blank
       end
     end
 
@@ -172,8 +172,8 @@ http://b.com/#{'a'*500}
         TopicLink.extract_from(quoting_post)
         link = quoting_post.topic.topic_links.first
 
-        link.link_post_id.should == linked_post.id
-        link.quote.should == true
+        expect(link.link_post_id).to eq(linked_post.id)
+        expect(link.quote).to eq(true)
       end
     end
 
@@ -184,13 +184,13 @@ http://b.com/#{'a'*500}
         TopicLink.extract_from(post)
         link = topic.topic_links.first
         # extracted the link
-        link.should be_present
+        expect(link).to be_present
         # is set to internal
-        link.should be_internal
+        expect(link).to be_internal
         # has the correct url
-        link.url.should == "/uploads/default/208/87bb3d8428eb4783.rb"
+        expect(link.url).to eq("/uploads/default/208/87bb3d8428eb4783.rb")
         # should not be the reflection
-        link.should_not be_reflection
+        expect(link).not_to be_reflection
       end
 
     end
@@ -202,13 +202,13 @@ http://b.com/#{'a'*500}
         TopicLink.extract_from(post)
         link = topic.topic_links.first
         # extracted the link
-        link.should be_present
+        expect(link).to be_present
         # is not internal
-        link.should_not be_internal
+        expect(link).not_to be_internal
         # has the correct url
-        link.url.should == "//s3.amazonaws.com/bucket/2104a0211c9ce41ed67989a1ed62e9a394c1fbd1446.rb"
+        expect(link.url).to eq("//s3.amazonaws.com/bucket/2104a0211c9ce41ed67989a1ed62e9a394c1fbd1446.rb")
         # should not be the reflection
-        link.should_not be_reflection
+        expect(link).not_to be_reflection
       end
 
     end
@@ -227,8 +227,8 @@ http://b.com/#{'a'*500}
 
       TopicLink.extract_from(linked_post)
 
-      topic.topic_links.first.should == nil
-      pm.topic_links.first.should_not == nil
+      expect(topic.topic_links.first).to eq(nil)
+      expect(pm.topic_links.first).not_to eq(nil)
     end
 
   end
@@ -245,13 +245,13 @@ http://b.com/#{'a'*500}
       TopicLink.extract_from(post)
       reflection = other_topic.topic_links.first
 
-      reflection.url.should == "http://#{alternate_uri.host}:5678/t/unique-topic-name/#{topic.id}"
+      expect(reflection.url).to eq("http://#{alternate_uri.host}:5678/t/unique-topic-name/#{topic.id}")
     end
   end
 
   describe 'counts_for and topic_map' do
     it 'returns blank without posts' do
-      TopicLink.counts_for(Guardian.new, nil, nil).should be_blank
+      expect(TopicLink.counts_for(Guardian.new, nil, nil)).to be_blank
     end
 
     context 'with data' do
@@ -271,13 +271,13 @@ http://b.com/#{'a'*500}
         topic_link = post.topic.topic_links.first
         TopicLinkClick.create(topic_link: topic_link, ip_address: '192.168.1.1')
 
-        counts_for[post.id].should be_present
-        counts_for[post.id].find {|l| l[:url] == 'http://google.com'}[:clicks].should == 0
-        counts_for[post.id].first[:clicks].should == 1
+        expect(counts_for[post.id]).to be_present
+        expect(counts_for[post.id].find {|l| l[:url] == 'http://google.com'}[:clicks]).to eq(0)
+        expect(counts_for[post.id].first[:clicks]).to eq(1)
 
         array = TopicLink.topic_map(Guardian.new, post.topic_id)
-        array.length.should == 4
-        array[0]["clicks"].should == "1"
+        expect(array.length).to eq(4)
+        expect(array[0]["clicks"]).to eq("1")
       end
 
       it 'secures internal links correctly' do
@@ -288,19 +288,19 @@ http://b.com/#{'a'*500}
         post = Fabricate(:post, raw: "hello test topic #{url}")
         TopicLink.extract_from(post)
 
-        TopicLink.topic_map(Guardian.new, post.topic_id).count.should == 1
-        TopicLink.counts_for(Guardian.new, post.topic, [post]).length.should == 1
+        expect(TopicLink.topic_map(Guardian.new, post.topic_id).count).to eq(1)
+        expect(TopicLink.counts_for(Guardian.new, post.topic, [post]).length).to eq(1)
 
         category.set_permissions(:staff => :full)
         category.save
 
         admin = Fabricate(:admin)
 
-        TopicLink.topic_map(Guardian.new, post.topic_id).count.should == 0
-        TopicLink.topic_map(Guardian.new(admin), post.topic_id).count.should == 1
+        expect(TopicLink.topic_map(Guardian.new, post.topic_id).count).to eq(0)
+        expect(TopicLink.topic_map(Guardian.new(admin), post.topic_id).count).to eq(1)
 
-        TopicLink.counts_for(Guardian.new, post.topic, [post]).length.should == 0
-        TopicLink.counts_for(Guardian.new(admin), post.topic, [post]).length.should == 1
+        expect(TopicLink.counts_for(Guardian.new, post.topic, [post]).length).to eq(0)
+        expect(TopicLink.counts_for(Guardian.new(admin), post.topic, [post]).length).to eq(1)
       end
 
     end
diff --git a/spec/models/topic_spec.rb b/spec/models/topic_spec.rb
index d78d7369fb..0f49c41431 100644
--- a/spec/models/topic_spec.rb
+++ b/spec/models/topic_spec.rb
@@ -7,9 +7,9 @@ describe Topic do
 
   let(:now) { Time.zone.local(2013,11,20,8,0) }
 
-  it { should validate_presence_of :title }
+  it { is_expected.to validate_presence_of :title }
 
-  it { should rate_limit }
+  it { is_expected.to rate_limit }
 
   context 'slug' do
 
@@ -18,7 +18,7 @@ describe Topic do
 
     it "returns a Slug for a title" do
       Slug.expects(:for).with(title).returns(slug)
-      Fabricate.build(:topic, title: title).slug.should == slug
+      expect(Fabricate.build(:topic, title: title).slug).to eq(slug)
     end
 
     let(:chinese_title) { "习近平:中企承建港口电站等助斯里兰卡发展" }
@@ -26,12 +26,12 @@ describe Topic do
 
     it "returns a symbolized slug for a chinese title" do
       SiteSetting.default_locale = 'zh_CN'
-      Fabricate.build(:topic, title: chinese_title).slug.should == chinese_slug
+      expect(Fabricate.build(:topic, title: chinese_title).slug).to eq(chinese_slug)
     end
 
     it "returns 'topic' when the slug is empty (say, non-english chars)" do
       Slug.expects(:for).with(title).returns("")
-      Fabricate.build(:topic, title: title).slug.should == "topic"
+      expect(Fabricate.build(:topic, title: title).slug).to eq("topic")
     end
 
   end
@@ -41,7 +41,7 @@ describe Topic do
 
     it "doesn't update it to be shorter due to cleaning using TextCleaner" do
       topic.title = 'unread    glitch'
-      topic.save.should == false
+      expect(topic.save).to eq(false)
     end
   end
 
@@ -87,22 +87,22 @@ describe Topic do
       end
 
       it "won't allow another topic to be created with the same name" do
-        new_topic.should_not be_valid
+        expect(new_topic).not_to be_valid
       end
 
       it "won't allow another topic with an upper case title to be created" do
         new_topic.title = new_topic.title.upcase
-        new_topic.should_not be_valid
+        expect(new_topic).not_to be_valid
       end
 
       it "allows it when the topic is deleted" do
         topic.destroy
-        new_topic.should be_valid
+        expect(new_topic).to be_valid
       end
 
       it "allows a private message to be created with the same topic" do
         new_topic.archetype = Archetype.private_message
-        new_topic.should be_valid
+        expect(new_topic).to be_valid
       end
     end
 
@@ -112,7 +112,7 @@ describe Topic do
       end
 
       it "will allow another topic to be created with the same name" do
-        new_topic.should be_valid
+        expect(new_topic).to be_valid
       end
     end
 
@@ -129,15 +129,15 @@ describe Topic do
     let(:topic_script) { build_topic_with_title("Topic with  script in its title" ) }
 
     it "escapes script contents" do
-      topic_script.fancy_title.should == "Topic with <script>alert(‘title’)</script> script in its title"
+      expect(topic_script.fancy_title).to eq("Topic with <script>alert(‘title’)</script> script in its title")
     end
 
     it "escapes bold contents" do
-      topic_bold.fancy_title.should == "Topic with <b>bold</b> text in its title"
+      expect(topic_bold.fancy_title).to eq("Topic with <b>bold</b> text in its title")
     end
 
     it "escapes image contents" do
-      topic_image.fancy_title.should == "Topic with <img src=‘something’> image in its title"
+      expect(topic_image.fancy_title).to eq("Topic with <img src=‘something’> image in its title")
     end
 
   end
@@ -151,7 +151,7 @@ describe Topic do
       end
 
       it "doesn't add entities to the title" do
-        topic.fancy_title.should == ""this topic" -- has ``fancy stuff''"
+        expect(topic.fancy_title).to eq(""this topic" -- has ``fancy stuff''")
       end
     end
 
@@ -161,7 +161,7 @@ describe Topic do
       end
 
       it "converts the title to have fancy entities" do
-        topic.fancy_title.should == "“this topic” – has “fancy stuff”"
+        expect(topic.fancy_title).to eq("“this topic” – has “fancy stuff”")
       end
     end
   end
@@ -174,17 +174,17 @@ describe Topic do
 
       it "does not allow nil category" do
         topic = Fabricate.build(:topic, category: nil)
-        topic.should_not be_valid
-        topic.errors[:category_id].should be_present
+        expect(topic).not_to be_valid
+        expect(topic.errors[:category_id]).to be_present
       end
 
       it "allows PMs" do
         topic = Fabricate.build(:topic, category: nil, archetype: Archetype.private_message)
-        topic.should be_valid
+        expect(topic).to be_valid
       end
 
       it 'passes for topics with a category' do
-        Fabricate.build(:topic, category: Fabricate(:category)).should be_valid
+        expect(Fabricate.build(:topic, category: Fabricate(:category))).to be_valid
       end
     end
 
@@ -194,11 +194,11 @@ describe Topic do
       end
 
       it "passes for topics with nil category" do
-        Fabricate.build(:topic, category: nil).should be_valid
+        expect(Fabricate.build(:topic, category: nil)).to be_valid
       end
 
       it 'passes for topics with a category' do
-        Fabricate.build(:topic, category: Fabricate(:category)).should be_valid
+        expect(Fabricate.build(:topic, category: Fabricate(:category))).to be_valid
       end
     end
   end
@@ -207,14 +207,14 @@ describe Topic do
   context 'similar_to' do
 
     it 'returns blank with nil params' do
-      Topic.similar_to(nil, nil).should be_blank
+      expect(Topic.similar_to(nil, nil)).to be_blank
     end
 
     context "with a category definition" do
       let!(:category) { Fabricate(:category) }
 
       it "excludes the category definition topic from similar_to" do
-        Topic.similar_to('category definition for', "no body").should be_blank
+        expect(Topic.similar_to('category definition for', "no body")).to be_blank
       end
     end
 
@@ -226,7 +226,7 @@ describe Topic do
       }
 
       it 'returns the similar topic if the title is similar' do
-        Topic.similar_to("has evil trout made any topics?", "i am wondering has evil trout made any topics?").should == [topic]
+        expect(Topic.similar_to("has evil trout made any topics?", "i am wondering has evil trout made any topics?")).to eq([topic])
       end
 
       context "secure categories" do
@@ -260,10 +260,10 @@ describe Topic do
     let!(:p3) { Fabricate(:post, topic: topic, user: topic.user) }
 
     it "returns the post numbers of the topic" do
-      topic.post_numbers.should == [1, 2, 3]
+      expect(topic.post_numbers).to eq([1, 2, 3])
       p2.destroy
       topic.reload
-      topic.post_numbers.should == [1, 3]
+      expect(topic.post_numbers).to eq([1, 3])
     end
 
   end
@@ -275,14 +275,14 @@ describe Topic do
     let(:topic) { Fabricate(:private_message_topic) }
 
     it "should integrate correctly" do
-      Guardian.new(topic.user).can_see?(topic).should == true
-      Guardian.new.can_see?(topic).should == false
-      Guardian.new(evil_trout).can_see?(topic).should == false
-      Guardian.new(coding_horror).can_see?(topic).should == true
-      TopicQuery.new(evil_trout).list_latest.topics.should_not include(topic)
+      expect(Guardian.new(topic.user).can_see?(topic)).to eq(true)
+      expect(Guardian.new.can_see?(topic)).to eq(false)
+      expect(Guardian.new(evil_trout).can_see?(topic)).to eq(false)
+      expect(Guardian.new(coding_horror).can_see?(topic)).to eq(true)
+      expect(TopicQuery.new(evil_trout).list_latest.topics).not_to include(topic)
 
       # invites
-      topic.invite(topic.user, 'duhhhhh').should == false
+      expect(topic.invite(topic.user, 'duhhhhh')).to eq(false)
     end
 
     context 'invite' do
@@ -293,26 +293,26 @@ describe Topic do
         context 'by username' do
 
           it 'adds and removes walter to the allowed users' do
-            topic.invite(topic.user, walter.username).should == true
-            topic.allowed_users.include?(walter).should == true
+            expect(topic.invite(topic.user, walter.username)).to eq(true)
+            expect(topic.allowed_users.include?(walter)).to eq(true)
 
-            topic.remove_allowed_user(walter.username).should == true
+            expect(topic.remove_allowed_user(walter.username)).to eq(true)
             topic.reload
-            topic.allowed_users.include?(walter).should == false
+            expect(topic.allowed_users.include?(walter)).to eq(false)
           end
 
           it 'creates a notification' do
-            lambda { topic.invite(topic.user, walter.username) }.should change(Notification, :count)
+            expect { topic.invite(topic.user, walter.username) }.to change(Notification, :count)
           end
         end
 
         context 'by email' do
 
           it 'adds user correctly' do
-            lambda {
-              topic.invite(topic.user, walter.email).should == true
-            }.should change(Notification, :count)
-            topic.allowed_users.include?(walter).should == true
+            expect {
+              expect(topic.invite(topic.user, walter.email)).to eq(true)
+            }.to change(Notification, :count)
+            expect(topic.allowed_users.include?(walter)).to eq(true)
           end
 
         end
@@ -326,9 +326,9 @@ describe Topic do
       it "should set up actions correctly" do
         ActiveRecord::Base.observers.enable :all
 
-        actions.map{|a| a.action_type}.should_not include(UserAction::NEW_TOPIC)
-        actions.map{|a| a.action_type}.should include(UserAction::NEW_PRIVATE_MESSAGE)
-        coding_horror.user_actions.map{|a| a.action_type}.should include(UserAction::GOT_PRIVATE_MESSAGE)
+        expect(actions.map{|a| a.action_type}).not_to include(UserAction::NEW_TOPIC)
+        expect(actions.map{|a| a.action_type}).to include(UserAction::NEW_PRIVATE_MESSAGE)
+        expect(coding_horror.user_actions.map{|a| a.action_type}).to include(UserAction::GOT_PRIVATE_MESSAGE)
       end
 
     end
@@ -343,11 +343,11 @@ describe Topic do
     end
 
     it 'updates the bumped_at field when a new post is made' do
-      @topic.bumped_at.should be_present
-      lambda {
+      expect(@topic.bumped_at).to be_present
+      expect {
         create_post(topic: @topic, user: @topic.user)
         @topic.reload
-      }.should change(@topic, :bumped_at)
+      }.to change(@topic, :bumped_at)
     end
 
     context 'editing posts' do
@@ -358,25 +358,25 @@ describe Topic do
       end
 
       it "doesn't bump the topic on an edit to the last post that doesn't result in a new version" do
-        lambda {
+        expect {
           SiteSetting.expects(:ninja_edit_window).returns(5.minutes)
           @last_post.revise(@last_post.user, { raw: 'updated contents' }, revised_at: @last_post.created_at + 10.seconds)
           @topic.reload
-        }.should_not change(@topic, :bumped_at)
+        }.not_to change(@topic, :bumped_at)
       end
 
       it "bumps the topic when a new version is made of the last post" do
-        lambda {
+        expect {
           @last_post.revise(Fabricate(:moderator), { raw: 'updated contents' })
           @topic.reload
-        }.should change(@topic, :bumped_at)
+        }.to change(@topic, :bumped_at)
       end
 
       it "doesn't bump the topic when a post that isn't the last post receives a new version" do
-        lambda {
+        expect {
           @earlier_post.revise(Fabricate(:moderator), { raw: 'updated contents' })
           @topic.reload
-        }.should_not change(@topic, :bumped_at)
+        }.not_to change(@topic, :bumped_at)
       end
     end
   end
@@ -389,13 +389,13 @@ describe Topic do
     end
 
     it 'creates a moderator post' do
-      @mod_post.should be_present
-      @mod_post.post_type.should == Post.types[:moderator_action]
-      @mod_post.post_number.should == 999
-      @mod_post.sort_order.should == 999
-      @topic.topic_links.count.should == 1
+      expect(@mod_post).to be_present
+      expect(@mod_post.post_type).to eq(Post.types[:moderator_action])
+      expect(@mod_post.post_number).to eq(999)
+      expect(@mod_post.sort_order).to eq(999)
+      expect(@topic.topic_links.count).to eq(1)
       @topic.reload
-      @topic.moderator_posts_count.should == 1
+      expect(@topic.moderator_posts_count).to eq(1)
     end
   end
 
@@ -417,9 +417,9 @@ describe Topic do
         end
 
         it 'should not be visible and have correct counts' do
-          @topic.should_not be_visible
-          @topic.moderator_posts_count.should == 1
-          @topic.bumped_at.to_f.should == @original_bumped_at
+          expect(@topic).not_to be_visible
+          expect(@topic.moderator_posts_count).to eq(1)
+          expect(@topic.bumped_at.to_f).to eq(@original_bumped_at)
         end
       end
 
@@ -431,9 +431,9 @@ describe Topic do
         end
 
         it 'should be visible with correct counts' do
-          @topic.should be_visible
-          @topic.moderator_posts_count.should == 1
-          @topic.bumped_at.to_f.should == @original_bumped_at
+          expect(@topic).to be_visible
+          expect(@topic.moderator_posts_count).to eq(1)
+          expect(@topic.bumped_at.to_f).to eq(@original_bumped_at)
         end
       end
     end
@@ -446,9 +446,9 @@ describe Topic do
         end
 
         it "doesn't have a pinned_at but has correct dates" do
-          @topic.pinned_at.should be_blank
-          @topic.moderator_posts_count.should == 1
-          @topic.bumped_at.to_f.should == @original_bumped_at
+          expect(@topic.pinned_at).to be_blank
+          expect(@topic.moderator_posts_count).to eq(1)
+          expect(@topic.bumped_at.to_f).to eq(@original_bumped_at)
         end
       end
 
@@ -460,9 +460,9 @@ describe Topic do
         end
 
         it 'should enable correctly' do
-          @topic.pinned_at.should be_present
-          @topic.bumped_at.to_f.should == @original_bumped_at
-          @topic.moderator_posts_count.should == 1
+          expect(@topic.pinned_at).to be_present
+          expect(@topic.bumped_at.to_f).to eq(@original_bumped_at)
+          expect(@topic.moderator_posts_count).to eq(1)
         end
 
       end
@@ -476,9 +476,9 @@ describe Topic do
         end
 
         it 'should archive correctly' do
-          @topic.should_not be_archived
-          @topic.bumped_at.to_f.should == @original_bumped_at
-          @topic.moderator_posts_count.should == 1
+          expect(@topic).not_to be_archived
+          expect(@topic.bumped_at.to_f).to eq(@original_bumped_at)
+          expect(@topic.moderator_posts_count).to eq(1)
         end
       end
 
@@ -490,9 +490,9 @@ describe Topic do
         end
 
         it 'should be archived' do
-          @topic.should be_archived
-          @topic.moderator_posts_count.should == 1
-          @topic.bumped_at.to_f.should == @original_bumped_at
+          expect(@topic).to be_archived
+          expect(@topic.moderator_posts_count).to eq(1)
+          expect(@topic.bumped_at.to_f).to eq(@original_bumped_at)
         end
 
       end
@@ -506,9 +506,9 @@ describe Topic do
         end
 
         it 'should not be pinned' do
-          @topic.should_not be_closed
-          @topic.moderator_posts_count.should == 1
-          @topic.bumped_at.to_f.should_not == @original_bumped_at
+          expect(@topic).not_to be_closed
+          expect(@topic.moderator_posts_count).to eq(1)
+          expect(@topic.bumped_at.to_f).not_to eq(@original_bumped_at)
         end
 
       end
@@ -521,9 +521,9 @@ describe Topic do
         end
 
         it 'should be closed' do
-          @topic.should be_closed
-          @topic.bumped_at.to_f.should == @original_bumped_at
-          @topic.moderator_posts_count.should == 1
+          expect(@topic).to be_closed
+          expect(@topic.bumped_at.to_f).to eq(@original_bumped_at)
+          expect(@topic.moderator_posts_count).to eq(1)
         end
       end
     end
@@ -562,70 +562,6 @@ describe Topic do
     end
   end
 
-  describe 'toggle_star' do
-
-    shared_examples_for "adding a star to a topic" do
-      it 'triggers a forum topic user change with true' do
-        # otherwise no chance the mock will work
-        freeze_time
-        TopicUser.expects(:change).with(@user, @topic.id, starred: true, starred_at: DateTime.now, unstarred_at: nil)
-        @topic.toggle_star(@user, true)
-      end
-
-      it 'increases the star_count of the forum topic' do
-        lambda {
-          @topic.toggle_star(@user, true)
-          @topic.reload
-        }.should change(@topic, :star_count).by(1)
-      end
-
-      it 'triggers the rate limiter' do
-        Topic::StarLimiter.any_instance.expects(:performed!)
-        @topic.toggle_star(@user, true)
-      end
-    end
-
-    before do
-      @topic = Fabricate(:topic)
-      @user = @topic.user
-    end
-
-    it_should_behave_like "adding a star to a topic"
-
-    describe 'removing a star' do
-      before do
-        @topic.toggle_star(@user, true)
-        @topic.reload
-      end
-
-      it 'rolls back the rate limiter' do
-        Topic::StarLimiter.any_instance.expects(:rollback!)
-        @topic.toggle_star(@user, false)
-      end
-
-      it 'triggers a forum topic user change with false' do
-        freeze_time
-        TopicUser.expects(:change).with(@user, @topic.id, starred: false, unstarred_at: DateTime.now)
-        @topic.toggle_star(@user, false)
-      end
-
-      it 'reduces the star_count' do
-        lambda {
-          @topic.toggle_star(@user, false)
-          @topic.reload
-        }.should change(@topic, :star_count).by(-1)
-      end
-
-      describe 'and adding a star again' do
-        before do
-          @topic.toggle_star(@user, false)
-          @topic.reload
-        end
-        it_should_behave_like "adding a star to a topic"
-      end
-    end
-  end
-
   describe "banner" do
 
     let(:topic) { Fabricate(:topic) }
@@ -639,20 +575,20 @@ describe Topic do
       it "changes the topic archetype to 'banner'" do
         messages = MessageBus.track_publish do
           topic.make_banner!(user)
-          topic.archetype.should == Archetype.banner
+          expect(topic.archetype).to eq(Archetype.banner)
         end
 
         channels = messages.map(&:channel)
-        channels.should include('/site/banner')
-        channels.should include('/distributed_hash')
+        expect(channels).to include('/site/banner')
+        expect(channels).to include('/distributed_hash')
       end
 
       it "ensures only one banner topic at all time" do
         _banner_topic = Fabricate(:banner_topic)
-        Topic.where(archetype: Archetype.banner).count.should == 1
+        expect(Topic.where(archetype: Archetype.banner).count).to eq(1)
 
         topic.make_banner!(user)
-        Topic.where(archetype: Archetype.banner).count.should == 1
+        expect(Topic.where(archetype: Archetype.banner).count).to eq(1)
       end
 
     end
@@ -663,7 +599,7 @@ describe Topic do
         topic.expects(:add_moderator_post)
         MessageBus.expects(:publish).with("/site/banner", nil)
         topic.remove_banner!(user)
-        topic.archetype.should == Archetype.default
+        expect(topic.archetype).to eq(Archetype.default)
       end
 
     end
@@ -680,7 +616,7 @@ describe Topic do
     end
 
     it 'initially has the last_post_user_id of the OP' do
-      @topic.last_post_user_id.should == @user.id
+      expect(@topic.last_post_user_id).to eq(@user.id)
     end
 
     context 'after a second post' do
@@ -691,10 +627,10 @@ describe Topic do
       end
 
       it 'updates the last_post_user_id to the second_user' do
-        @topic.last_post_user_id.should == @second_user.id
-        @topic.last_posted_at.to_i.should == @new_post.created_at.to_i
+        expect(@topic.last_post_user_id).to eq(@second_user.id)
+        expect(@topic.last_posted_at.to_i).to eq(@new_post.created_at.to_i)
         topic_user = @second_user.topic_users.find_by(topic_id: @topic.id)
-        topic_user.posted?.should == true
+        expect(topic_user.posted?).to eq(true)
       end
 
     end
@@ -707,11 +643,11 @@ describe Topic do
     end
 
     it "should not increase the topic_count with no category" do
-      -> { Fabricate(:topic, user: @category.user); @category.reload }.should_not change(@category, :topic_count)
+      expect { Fabricate(:topic, user: @category.user); @category.reload }.not_to change(@category, :topic_count)
     end
 
     it "should increase the category's topic_count" do
-      -> { Fabricate(:topic, user: @category.user, category_id: @category.id); @category.reload }.should change(@category, :topic_count).by(1)
+      expect { Fabricate(:topic, user: @category.user, category_id: @category.id); @category.reload }.to change(@category, :topic_count).by(1)
     end
   end
 
@@ -719,7 +655,7 @@ describe Topic do
     let(:topic) { Fabricate(:topic, meta_data: {'hello' => 'world'}) }
 
     it 'allows us to create a topic with meta data' do
-      topic.meta_data['hello'].should == 'world'
+      expect(topic.meta_data['hello']).to eq('world')
     end
 
     context 'updating' do
@@ -730,7 +666,7 @@ describe Topic do
         end
 
         it 'updates the key' do
-          topic.meta_data['hello'].should == 'bane'
+          expect(topic.meta_data['hello']).to eq('bane')
         end
       end
 
@@ -740,8 +676,8 @@ describe Topic do
         end
 
         it 'adds the new key' do
-          topic.meta_data['city'].should == 'gotham'
-          topic.meta_data['hello'].should == 'world'
+          expect(topic.meta_data['city']).to eq('gotham')
+          expect(topic.meta_data['hello']).to eq('world')
         end
 
       end
@@ -753,11 +689,11 @@ describe Topic do
         end
 
         it "can be loaded" do
-          Topic.find(topic.id).meta_data["other"].should == "key"
+          expect(Topic.find(topic.id).meta_data["other"]).to eq("key")
         end
 
         it "is in sync with custom_fields" do
-          Topic.find(topic.id).custom_fields["other"].should == "key"
+          expect(Topic.find(topic.id).custom_fields["other"]).to eq("key")
         end
       end
 
@@ -771,21 +707,21 @@ describe Topic do
     let(:topic) { Fabricate(:topic) }
 
     it 'is a regular topic by default' do
-      topic.archetype.should == Archetype.default
-      topic.has_summary.should == false
-      topic.percent_rank.should == 1.0
-      topic.should be_visible
-      topic.pinned_at.should be_blank
-      topic.should_not be_closed
-      topic.should_not be_archived
-      topic.moderator_posts_count.should == 0
+      expect(topic.archetype).to eq(Archetype.default)
+      expect(topic.has_summary).to eq(false)
+      expect(topic.percent_rank).to eq(1.0)
+      expect(topic).to be_visible
+      expect(topic.pinned_at).to be_blank
+      expect(topic).not_to be_closed
+      expect(topic).not_to be_archived
+      expect(topic.moderator_posts_count).to eq(0)
     end
 
     context 'post' do
       let(:post) { Fabricate(:post, topic: topic, user: topic.user) }
 
       it 'has the same archetype as the topic' do
-        post.archetype.should == topic.archetype
+        expect(post.archetype).to eq(topic.archetype)
       end
     end
   end
@@ -801,7 +737,7 @@ describe Topic do
     describe 'without a previous category' do
 
       it 'should not change the topic_count when not changed' do
-       lambda { @topic.change_category_to_id(@topic.category.id); @category.reload }.should_not change(@category, :topic_count)
+       expect { @topic.change_category_to_id(@topic.category.id); @category.reload }.not_to change(@category, :topic_count)
       end
 
       describe 'changed category' do
@@ -811,15 +747,15 @@ describe Topic do
         end
 
         it 'changes the category' do
-          @topic.category.should == @category
-          @category.topic_count.should == 1
+          expect(@topic.category).to eq(@category)
+          expect(@category.topic_count).to eq(1)
         end
 
       end
 
       it "doesn't change the category when it can't be found" do
         @topic.change_category_to_id(12312312)
-        @topic.category_id.should == SiteSetting.uncategorized_category_id
+        expect(@topic.category_id).to eq(SiteSetting.uncategorized_category_id)
       end
     end
 
@@ -831,16 +767,16 @@ describe Topic do
       end
 
       it 'increases the topic_count' do
-        @category.topic_count.should == 1
+        expect(@category.topic_count).to eq(1)
       end
 
       it "doesn't change the topic_count when the value doesn't change" do
-        lambda { @topic.change_category_to_id(@category.id); @category.reload }.should_not change(@category, :topic_count)
+        expect { @topic.change_category_to_id(@category.id); @category.reload }.not_to change(@category, :topic_count)
       end
 
       it "doesn't reset the category when given a name that doesn't exist" do
         @topic.change_category_to_id(55556)
-        @topic.category_id.should be_present
+        expect(@topic.category_id).to be_present
       end
 
       describe 'to a different category' do
@@ -853,11 +789,11 @@ describe Topic do
         end
 
         it "should increase the new category's topic count" do
-          @new_category.topic_count.should == 1
+          expect(@new_category.topic_count).to eq(1)
         end
 
         it "should lower the original category's topic count" do
-          @category.topic_count.should == 0
+          expect(@category.topic_count).to eq(0)
         end
       end
 
@@ -869,7 +805,7 @@ describe Topic do
         let!(:topic) { Fabricate(:topic, category: Fabricate(:category)) }
 
         it 'returns false' do
-          topic.change_category_to_id(nil).should eq(false) # don't use "== false" here because it would also match nil
+          expect(topic.change_category_to_id(nil)).to eq(false) # don't use "== false" here because it would also match nil
         end
       end
 
@@ -880,8 +816,8 @@ describe Topic do
         end
 
         it "resets the category" do
-          @topic.category_id.should == SiteSetting.uncategorized_category_id
-          @category.topic_count.should == 0
+          expect(@topic.category_id).to eq(SiteSetting.uncategorized_category_id)
+          expect(@category.topic_count).to eq(0)
         end
       end
 
@@ -897,7 +833,7 @@ describe Topic do
         b = Fabricate(:topic, created_at: now)
         c = Fabricate(:topic, created_at: now)
         d = Fabricate(:topic, created_at: now - 2.minutes)
-        Topic.by_newest.should == [c,b,d,a]
+        expect(Topic.by_newest).to eq([c,b,d,a])
       end
     end
 
@@ -909,11 +845,11 @@ describe Topic do
         c = Fabricate(:topic, created_at: now)
         d = Fabricate(:topic, created_at: now + 1.minute)
         e = Fabricate(:topic, created_at: now + 2.minutes)
-        Topic.created_since(now).should_not include a
-        Topic.created_since(now).should_not include b
-        Topic.created_since(now).should_not include c
-        Topic.created_since(now).should include d
-        Topic.created_since(now).should include e
+        expect(Topic.created_since(now)).not_to include a
+        expect(Topic.created_since(now)).not_to include b
+        expect(Topic.created_since(now)).not_to include c
+        expect(Topic.created_since(now)).to include d
+        expect(Topic.created_since(now)).to include e
       end
     end
 
@@ -922,9 +858,9 @@ describe Topic do
         a = Fabricate(:topic, visible: false)
         b = Fabricate(:topic, visible: true)
         c = Fabricate(:topic, visible: true)
-        Topic.visible.should_not include a
-        Topic.visible.should include b
-        Topic.visible.should include c
+        expect(Topic.visible).not_to include a
+        expect(Topic.visible).to include b
+        expect(Topic.visible).to include c
       end
     end
   end
@@ -983,7 +919,7 @@ describe Topic do
           topic = Fabricate(:topic)
           Jobs.expects(:enqueue_at).with(12.hours.from_now, :close_topic, has_entries(topic_id: topic.id, user_id: topic.user_id))
           topic.auto_close_at = 12.hours.from_now
-          topic.save.should == true
+          expect(topic.save).to eq(true)
         end
       end
 
@@ -994,7 +930,7 @@ describe Topic do
           Jobs.expects(:enqueue_at).with(12.hours.from_now, :close_topic, has_entries(topic_id: topic.id, user_id: closer.id))
           topic.auto_close_at = 12.hours.from_now
           topic.auto_close_user = closer
-          topic.save.should == true
+          expect(topic.save).to eq(true)
         end
       end
 
@@ -1003,8 +939,8 @@ describe Topic do
         topic = Fabricate(:topic, auto_close_at: 1.day.from_now)
         Jobs.expects(:cancel_scheduled_job).with(:close_topic, {topic_id: topic.id})
         topic.auto_close_at = nil
-        topic.save.should == true
-        topic.auto_close_user.should == nil
+        expect(topic.save).to eq(true)
+        expect(topic.auto_close_user).to eq(nil)
       end
 
       it 'when auto_close_user is removed, it updates the job' do
@@ -1014,7 +950,7 @@ describe Topic do
           Jobs.expects(:cancel_scheduled_job).with(:close_topic, {topic_id: topic.id})
           Jobs.expects(:enqueue_at).with(1.day.from_now, :close_topic, has_entries(topic_id: topic.id, user_id: topic.user_id))
           topic.auto_close_user = nil
-          topic.save.should == true
+          expect(topic.save).to eq(true)
         end
       end
 
@@ -1025,7 +961,7 @@ describe Topic do
           Jobs.expects(:cancel_scheduled_job).with(:close_topic, {topic_id: topic.id})
           Jobs.expects(:enqueue_at).with(3.days.from_now, :close_topic, has_entry(topic_id: topic.id))
           topic.auto_close_at = 3.days.from_now
-          topic.save.should == true
+          expect(topic.save).to eq(true)
         end
       end
 
@@ -1037,7 +973,7 @@ describe Topic do
           Jobs.expects(:cancel_scheduled_job).with(:close_topic, {topic_id: topic.id})
           Jobs.expects(:enqueue_at).with(1.day.from_now, :close_topic, has_entries(topic_id: topic.id, user_id: admin.id))
           topic.auto_close_user = admin
-          topic.save.should == true
+          expect(topic.save).to eq(true)
         end
       end
 
@@ -1047,7 +983,7 @@ describe Topic do
           Jobs.expects(:cancel_scheduled_job).never
           topic = Fabricate(:topic, auto_close_at: 1.day.from_now)
           topic.title = 'A new title that is long enough'
-          topic.save.should == true
+          expect(topic.save).to eq(true)
         end
       end
 
@@ -1061,12 +997,12 @@ describe Topic do
           topic.save
 
           topic.reload
-          topic.closed.should == false
+          expect(topic.closed).to eq(false)
 
           Timecop.freeze(24.hours.from_now) do
             Topic.auto_close
             topic.reload
-            topic.closed.should == true
+            expect(topic.closed).to eq(true)
           end
 
         end
@@ -1078,6 +1014,7 @@ describe Topic do
     let(:topic)         { Fabricate.build(:topic) }
     let(:closing_topic) { Fabricate.build(:topic, auto_close_hours: 5, auto_close_at: 5.hours.from_now, auto_close_started_at: 5.hours.from_now) }
     let(:admin)         { Fabricate.build(:user, id: 123) }
+    let(:trust_level_4) { Fabricate.build(:trust_level_4) }
 
     before { Discourse.stubs(:system_user).returns(admin) }
 
@@ -1098,50 +1035,55 @@ describe Topic do
     it "can take a time later in the day" do
       Timecop.freeze(now) do
         topic.set_auto_close('13:00', admin)
-        topic.auto_close_at.should == Time.zone.local(2013,11,20,13,0)
+        expect(topic.auto_close_at).to eq(Time.zone.local(2013,11,20,13,0))
       end
     end
 
     it "can take a time for the next day" do
       Timecop.freeze(now) do
         topic.set_auto_close('5:00', admin)
-        topic.auto_close_at.should == Time.zone.local(2013,11,21,5,0)
+        expect(topic.auto_close_at).to eq(Time.zone.local(2013,11,21,5,0))
       end
     end
 
     it "can take a timestamp for a future time" do
       Timecop.freeze(now) do
         topic.set_auto_close('2013-11-22 5:00', admin)
-        topic.auto_close_at.should == Time.zone.local(2013,11,22,5,0)
+        expect(topic.auto_close_at).to eq(Time.zone.local(2013,11,22,5,0))
       end
     end
 
     it "sets a validation error when given a timestamp in the past" do
       Timecop.freeze(now) do
         topic.set_auto_close('2013-11-19 5:00', admin)
-        topic.auto_close_at.should == Time.zone.local(2013,11,19,5,0)
-        topic.errors[:auto_close_at].should be_present
+        expect(topic.auto_close_at).to eq(Time.zone.local(2013,11,19,5,0))
+        expect(topic.errors[:auto_close_at]).to be_present
       end
     end
 
     it "can take a timestamp with timezone" do
       Timecop.freeze(now) do
         topic.set_auto_close('2013-11-25T01:35:00-08:00', admin)
-        topic.auto_close_at.should == Time.utc(2013,11,25,9,35)
+        expect(topic.auto_close_at).to eq(Time.utc(2013,11,25,9,35))
       end
     end
 
-    it 'sets auto_close_user to given user if it is a staff user' do
+    it 'sets auto_close_user to given user if it is a staff or TL4 user' do
       topic.set_auto_close(3, admin)
       expect(topic.auto_close_user_id).to eq(admin.id)
     end
 
-    it 'sets auto_close_user to system user if given user is not staff' do
+    it 'sets auto_close_user to given user if it is a TL4 user' do
+      topic.set_auto_close(3, trust_level_4)
+      expect(topic.auto_close_user_id).to eq(trust_level_4.id)
+    end
+
+    it 'sets auto_close_user to system user if given user is not staff or a TL4 user' do
       topic.set_auto_close(3, Fabricate.build(:user, id: 444))
       expect(topic.auto_close_user_id).to eq(admin.id)
     end
 
-    it 'sets auto_close_user to system_user if user is not given and topic creator is not staff' do
+    it 'sets auto_close_user to system user if user is not given and topic creator is not staff nor TL4 user' do
       topic.set_auto_close(3)
       expect(topic.auto_close_user_id).to eq(admin.id)
     end
@@ -1152,6 +1094,12 @@ describe Topic do
       expect(staff_topic.auto_close_user_id).to eq(999)
     end
 
+    it 'sets auto_close_user to topic creator if it is a TL4 user' do
+      tl4_topic = Fabricate.build(:topic, user: Fabricate.build(:trust_level_4, id: 998))
+      tl4_topic.set_auto_close(3)
+      expect(tl4_topic.auto_close_user_id).to eq(998)
+    end
+
     it 'clears auto_close_at if arg is nil' do
       closing_topic.set_auto_close(nil)
       expect(closing_topic.auto_close_at).to be_nil
@@ -1180,17 +1128,17 @@ describe Topic do
     let(:user) { Fabricate.build(:user) }
 
     it "returns none when there are no topics" do
-      Topic.for_digest(user, 1.year.ago, top_order: true).should be_blank
+      expect(Topic.for_digest(user, 1.year.ago, top_order: true)).to be_blank
     end
 
     it "doesn't return category topics" do
       Fabricate(:category)
-      Topic.for_digest(user, 1.year.ago, top_order: true).should be_blank
+      expect(Topic.for_digest(user, 1.year.ago, top_order: true)).to be_blank
     end
 
     it "returns regular topics" do
       topic = Fabricate(:topic)
-      Topic.for_digest(user, 1.year.ago, top_order: true).should == [topic]
+      expect(Topic.for_digest(user, 1.year.ago, top_order: true)).to eq([topic])
     end
 
     it "doesn't return topics from muted categories" do
@@ -1200,7 +1148,14 @@ describe Topic do
 
       CategoryUser.set_notification_level_for_category(user, CategoryUser.notification_levels[:muted], category.id)
 
-      Topic.for_digest(user, 1.year.ago, top_order: true).should be_blank
+      expect(Topic.for_digest(user, 1.year.ago, top_order: true)).to be_blank
+    end
+
+    it "doesn't return topics from TL0 users" do
+      new_user = Fabricate(:user, trust_level: 0)
+      topic = Fabricate(:topic, user_id: new_user.id)
+
+      expect(Topic.for_digest(user, 1.year.ago, top_order: true)).to be_blank
     end
 
   end
@@ -1210,13 +1165,33 @@ describe Topic do
       category = Fabricate(:category, read_restricted: true)
       Fabricate(:topic, category: category)
 
-      Topic.secured(Guardian.new(nil)).count.should == 0
-      Topic.secured(Guardian.new(Fabricate(:admin))).count.should == 2
+      expect(Topic.secured(Guardian.new(nil)).count).to eq(0)
+      expect(Topic.secured(Guardian.new(Fabricate(:admin))).count).to eq(2)
 
       # for_digest
 
-      Topic.for_digest(Fabricate(:user), 1.year.ago).count.should == 0
-      Topic.for_digest(Fabricate(:admin), 1.year.ago).count.should == 1
+      expect(Topic.for_digest(Fabricate(:user), 1.year.ago).count).to eq(0)
+      expect(Topic.for_digest(Fabricate(:admin), 1.year.ago).count).to eq(1)
+    end
+  end
+
+  describe '#listable_count_per_day' do
+    before(:each) do
+      Timecop.freeze
+      Fabricate(:topic)
+      Fabricate(:topic, created_at: 1.day.ago)
+      Fabricate(:topic, created_at: 1.day.ago)
+      Fabricate(:topic, created_at: 2.days.ago)
+      Fabricate(:topic, created_at: 4.days.ago)
+    end
+    after(:each) do
+      Timecop.return
+    end
+    let(:listable_topics_count_per_day) { {1.day.ago.to_date => 2, 2.days.ago.to_date => 1, Time.now.utc.to_date => 1 } }
+
+    it 'collect closed interval listable topics count' do
+      expect(Topic.listable_count_per_day(2.days.ago, Time.now)).to include(listable_topics_count_per_day)
+      expect(Topic.listable_count_per_day(2.days.ago, Time.now)).not_to include({4.days.ago.to_date => 1})
     end
   end
 
@@ -1225,16 +1200,16 @@ describe Topic do
 
     it "is true if the category is secure" do
       category.stubs(:read_restricted).returns(true)
-      Topic.new(:category => category).should be_read_restricted_category
+      expect(Topic.new(:category => category)).to be_read_restricted_category
     end
 
     it "is false if the category is not secure" do
       category.stubs(:read_restricted).returns(false)
-      Topic.new(:category => category).should_not be_read_restricted_category
+      expect(Topic.new(:category => category)).not_to be_read_restricted_category
     end
 
     it "is false if there is no category" do
-      Topic.new(:category => nil).should_not be_read_restricted_category
+      expect(Topic.new(:category => nil)).not_to be_read_restricted_category
     end
   end
 
@@ -1277,6 +1252,7 @@ describe Topic do
     SiteSetting.stubs(:client_settings_json).returns(SiteSetting.client_settings_json_uncached)
     RateLimiter.stubs(:rate_limit_create_topic).returns(100)
     RateLimiter.stubs(:disabled?).returns(false)
+    RateLimiter.clear_all!
 
     start = Time.now.tomorrow.beginning_of_day
 
@@ -1286,18 +1262,18 @@ describe Topic do
     topic_id = create_post(user: user).topic_id
 
     freeze_time(start + 10.minutes)
-    lambda {
+    expect {
       create_post(user: user)
-    }.should raise_exception
+    }.to raise_exception
 
     freeze_time(start + 20.minutes)
     create_post(user: user, topic_id: topic_id)
 
     freeze_time(start + 30.minutes)
 
-    lambda {
+    expect {
       create_post(user: user, topic_id: topic_id)
-    }.should raise_exception
+    }.to raise_exception
   end
 
   describe ".count_exceeds_minimun?" do
@@ -1336,44 +1312,44 @@ describe Topic do
     end
 
     it "is true with the correct settings and topic_embed" do
-      topic.expandable_first_post?.should == true
+      expect(topic.expandable_first_post?).to eq(true)
     end
 
     it "is false if embeddable_host is blank" do
       SiteSetting.stubs(:embeddable_host).returns(nil)
-      topic.expandable_first_post?.should == false
+      expect(topic.expandable_first_post?).to eq(false)
     end
 
     it "is false if embed_truncate? is false" do
       SiteSetting.stubs(:embed_truncate?).returns(false)
-      topic.expandable_first_post?.should == false
+      expect(topic.expandable_first_post?).to eq(false)
     end
 
     it "is false if has_topic_embed? is false" do
       topic.stubs(:has_topic_embed?).returns(false)
-      topic.expandable_first_post?.should == false
+      expect(topic.expandable_first_post?).to eq(false)
     end
   end
 
   it "has custom fields" do
     topic = Fabricate(:topic)
-    topic.custom_fields["a"].should == nil
+    expect(topic.custom_fields["a"]).to eq(nil)
 
     topic.custom_fields["bob"] = "marley"
     topic.custom_fields["jack"] = "black"
     topic.save
 
     topic = Topic.find(topic.id)
-    topic.custom_fields.should == {"bob" => "marley", "jack" => "black"}
+    expect(topic.custom_fields).to eq({"bob" => "marley", "jack" => "black"})
   end
 
   it "doesn't validate the title again if it isn't changing" do
     SiteSetting.stubs(:min_topic_title_length).returns(5)
     topic = Fabricate(:topic, title: "Short")
-    topic.should be_valid
+    expect(topic).to be_valid
 
     SiteSetting.stubs(:min_topic_title_length).returns(15)
     topic.last_posted_at = 1.minute.ago
-    topic.save.should == true
+    expect(topic.save).to eq(true)
   end
 end
diff --git a/spec/models/topic_status_update_spec.rb b/spec/models/topic_status_update_spec.rb
index c612757588..39dad5bb34 100644
--- a/spec/models/topic_status_update_spec.rb
+++ b/spec/models/topic_status_update_spec.rb
@@ -19,10 +19,10 @@ describe TopicStatusUpdate do
 
     TopicStatusUpdate.new(post.topic, admin).update!("autoclosed", true)
 
-    post.topic.posts.count.should == 2
+    expect(post.topic.posts.count).to eq(2)
 
     tu = TopicUser.find_by(user_id: user.id)
-    tu.last_read_post_number.should == 2
+    expect(tu.last_read_post_number).to eq(2)
   end
 
   it "adds an autoclosed message" do
@@ -30,7 +30,7 @@ describe TopicStatusUpdate do
 
     TopicStatusUpdate.new(topic, admin).update!("autoclosed", true)
 
-    topic.posts.last.raw.should == I18n.t("topic_statuses.autoclosed_enabled_minutes", count: 0)
+    expect(topic.posts.last.raw).to eq(I18n.t("topic_statuses.autoclosed_enabled_minutes", count: 0))
   end
 
   it "adds an autoclosed message based on last post" do
@@ -39,7 +39,7 @@ describe TopicStatusUpdate do
 
     TopicStatusUpdate.new(topic, admin).update!("autoclosed", true)
 
-    topic.posts.last.raw.should == I18n.t("topic_statuses.autoclosed_enabled_lastpost_minutes", count: 0)
+    expect(topic.posts.last.raw).to eq(I18n.t("topic_statuses.autoclosed_enabled_lastpost_minutes", count: 0))
   end
 
 end
diff --git a/spec/models/topic_tracking_state_spec.rb b/spec/models/topic_tracking_state_spec.rb
index f65a1e4eb7..3c94c26b15 100644
--- a/spec/models/topic_tracking_state_spec.rb
+++ b/spec/models/topic_tracking_state_spec.rb
@@ -17,39 +17,39 @@ describe TopicTrackingState do
 
   it "correctly gets the tracking state" do
     report = TopicTrackingState.report([user.id])
-    report.length.should == 0
+    expect(report.length).to eq(0)
 
     new_post = post
     post.topic.notifier.watch_topic!(post.topic.user_id)
 
     report = TopicTrackingState.report([user.id])
 
-    report.length.should == 1
+    expect(report.length).to eq(1)
     row = report[0]
 
-    row.topic_id.should == post.topic_id
-    row.highest_post_number.should == 1
-    row.last_read_post_number.should == nil
-    row.user_id.should == user.id
+    expect(row.topic_id).to eq(post.topic_id)
+    expect(row.highest_post_number).to eq(1)
+    expect(row.last_read_post_number).to eq(nil)
+    expect(row.user_id).to eq(user.id)
 
     # lets not leak out random users
-    TopicTrackingState.report([post.user_id]).should be_empty
+    expect(TopicTrackingState.report([post.user_id])).to be_empty
 
     # lets not return anything if we scope on non-existing topic
-    TopicTrackingState.report([user.id], post.topic_id + 1).should be_empty
+    expect(TopicTrackingState.report([user.id], post.topic_id + 1)).to be_empty
 
     # when we reply the poster should have an unread row
     create_post(user: user, topic: post.topic)
 
     report = TopicTrackingState.report([post.user_id, user.id])
-    report.length.should == 1
+    expect(report.length).to eq(1)
 
     row = report[0]
 
-    row.topic_id.should == post.topic_id
-    row.highest_post_number.should == 2
-    row.last_read_post_number.should == 1
-    row.user_id.should == post.user_id
+    expect(row.topic_id).to eq(post.topic_id)
+    expect(row.highest_post_number).to eq(2)
+    expect(row.last_read_post_number).to eq(1)
+    expect(row.user_id).to eq(post.user_id)
 
     # when we have no permission to see a category, don't show its stats
     category = Fabricate(:category, read_restricted: true)
@@ -57,6 +57,6 @@ describe TopicTrackingState do
     post.topic.category_id = category.id
     post.topic.save
 
-    TopicTrackingState.report([post.user_id, user.id]).count.should == 0
+    expect(TopicTrackingState.report([post.user_id, user.id]).count).to eq(0)
   end
 end
diff --git a/spec/models/topic_user_spec.rb b/spec/models/topic_user_spec.rb
index c1a62ac4d5..d4712969f9 100644
--- a/spec/models/topic_user_spec.rb
+++ b/spec/models/topic_user_spec.rb
@@ -20,14 +20,14 @@ describe TopicUser do
   let(:topic_new_user) { TopicUser.get(topic, new_user)}
   let(:yesterday) { DateTime.now.yesterday }
 
+  def ensure_topic_user
+    TopicUser.change(user, topic, last_emailed_post_number: 1)
+  end
 
   describe "unpinned" do
 
-    before do
-      TopicUser.change(user, topic, {starred_at: yesterday})
-    end
-
     it "defaults to blank" do
+      ensure_topic_user
       topic_user.cleared_pinned_at.should be_blank
     end
 
@@ -37,19 +37,19 @@ describe TopicUser do
 
     it 'should be set to tracking if auto_track_topics is enabled' do
       user.update_column(:auto_track_topics_after_msecs, 0)
-      TopicUser.change(user, topic, {starred_at: yesterday})
+      ensure_topic_user
       TopicUser.get(topic, user).notification_level.should == TopicUser.notification_levels[:tracking]
     end
 
     it 'should reset regular topics to tracking topics if auto track is changed' do
-      TopicUser.change(user, topic, {starred_at: yesterday})
+      ensure_topic_user
       user.auto_track_topics_after_msecs = 0
       user.save
       topic_user.notification_level.should == TopicUser.notification_levels[:tracking]
     end
 
     it 'should be set to "regular" notifications, by default on non creators' do
-      TopicUser.change(user, topic, {starred_at: yesterday})
+      ensure_topic_user
       TopicUser.get(topic,user).notification_level.should == TopicUser.notification_levels[:regular]
     end
 
@@ -195,37 +195,20 @@ describe TopicUser do
 
   describe 'change a flag' do
 
-    it 'creates a forum topic user record' do
-      user; topic
-
-      lambda {
-        TopicUser.change(user, topic.id, starred: true)
-      }.should change(TopicUser, :count).by(1)
-    end
-
     it "only inserts a row once, even on repeated calls" do
 
       topic; user
 
       lambda {
-        TopicUser.change(user, topic.id, starred: true)
-        TopicUser.change(user, topic.id, starred: false)
-        TopicUser.change(user, topic.id, starred: true)
+        TopicUser.change(user, topic.id, total_msecs_viewed: 1)
+        TopicUser.change(user, topic.id, total_msecs_viewed: 2)
+        TopicUser.change(user, topic.id, total_msecs_viewed: 3)
       }.should change(TopicUser, :count).by(1)
     end
 
-    it 'triggers the observer callbacks when updating' do
-      UserActionObserver.instance.expects(:after_save).twice
-      3.times { TopicUser.change(user, topic.id, starred: true) }
-    end
-
     describe 'after creating a row' do
       before do
-        TopicUser.change(user, topic.id, starred: true)
-      end
-
-      it 'has the correct starred value' do
-        TopicUser.get(topic, user).should be_starred
+        ensure_topic_user
       end
 
       it 'has a lookup' do
diff --git a/spec/models/topic_view_item_spec.rb b/spec/models/topic_view_item_spec.rb
index c2aa1ccc75..fd15a7592b 100644
--- a/spec/models/topic_view_item_spec.rb
+++ b/spec/models/topic_view_item_spec.rb
@@ -14,7 +14,7 @@ describe TopicViewItem do
     TopicViewItem.create!(topic_id: 1, ip_address: "1.1.1.1", viewed_at: 1.day.ago)
     add(1, "1.1.1.1")
 
-    TopicViewItem.count.should == 3
+    expect(TopicViewItem.count).to eq(3)
   end
 
   it "increases a users view count" do
@@ -24,7 +24,7 @@ describe TopicViewItem do
     add(1,  "1.1.1.1", user.id)
 
     user.user_stat.reload
-    user.user_stat.topics_entered.should == 1
+    expect(user.user_stat.topics_entered).to eq(1)
   end
 
 end
diff --git a/spec/models/trust_level3_requirements_spec.rb b/spec/models/trust_level3_requirements_spec.rb
index cc3ed78721..799e09b169 100644
--- a/spec/models/trust_level3_requirements_spec.rb
+++ b/spec/models/trust_level3_requirements_spec.rb
@@ -16,51 +16,51 @@ describe TrustLevel3Requirements do
   describe "requirements" do
     it "min_days_visited uses site setting" do
       SiteSetting.stubs(:tl3_requires_days_visited).returns(66)
-      tl3_requirements.min_days_visited.should == 66
+      expect(tl3_requirements.min_days_visited).to eq(66)
     end
 
     it "min_topics_replied_to uses site setting" do
       SiteSetting.stubs(:tl3_requires_topics_replied_to).returns(12)
-      tl3_requirements.min_topics_replied_to.should == 12
+      expect(tl3_requirements.min_topics_replied_to).to eq(12)
     end
 
     it "min_topics_viewed depends on site setting and number of topics created" do
       SiteSetting.stubs(:tl3_requires_topics_viewed).returns(75)
       described_class.stubs(:num_topics_in_time_period).returns(31)
-      tl3_requirements.min_topics_viewed.should == 23
+      expect(tl3_requirements.min_topics_viewed).to eq(23)
     end
 
     it "min_posts_read depends on site setting and number of posts created" do
       SiteSetting.stubs(:tl3_requires_posts_read).returns(66)
       described_class.stubs(:num_posts_in_time_period).returns(1234)
-      tl3_requirements.min_posts_read.should == 814
+      expect(tl3_requirements.min_posts_read).to eq(814)
     end
 
     it "min_topics_viewed_all_time depends on site setting" do
       SiteSetting.stubs(:tl3_requires_topics_viewed_all_time).returns(75)
-      tl3_requirements.min_topics_viewed_all_time.should == 75
+      expect(tl3_requirements.min_topics_viewed_all_time).to eq(75)
     end
 
     it "min_posts_read_all_time depends on site setting" do
       SiteSetting.stubs(:tl3_requires_posts_read_all_time).returns(1001)
-      tl3_requirements.min_posts_read_all_time.should == 1001
+      expect(tl3_requirements.min_posts_read_all_time).to eq(1001)
     end
 
     it "max_flagged_posts depends on site setting" do
       SiteSetting.stubs(:tl3_requires_max_flagged).returns(3)
-      tl3_requirements.max_flagged_posts.should == 3
+      expect(tl3_requirements.max_flagged_posts).to eq(3)
     end
 
     it "min_likes_given depends on site setting" do
       SiteSetting.stubs(:tl3_requires_likes_given).returns(30)
-      tl3_requirements.min_likes_given.should == 30
+      expect(tl3_requirements.min_likes_given).to eq(30)
     end
 
     it "min_likes_received depends on site setting" do
       SiteSetting.stubs(:tl3_requires_likes_received).returns(20)
-      tl3_requirements.min_likes_received.should == 20
-      tl3_requirements.min_likes_received_days.should == 7
-      tl3_requirements.min_likes_received_users.should == 5
+      expect(tl3_requirements.min_likes_received).to eq(20)
+      expect(tl3_requirements.min_likes_received_days).to eq(7)
+      expect(tl3_requirements.min_likes_received_users).to eq(5)
     end
   end
 
@@ -71,7 +71,7 @@ describe TrustLevel3Requirements do
       user.update_posts_read!(1, 3.days.ago)
       user.update_posts_read!(0, 4.days.ago)
       user.update_posts_read!(3, 101.days.ago)
-      tl3_requirements.days_visited.should == 2
+      expect(tl3_requirements.days_visited).to eq(2)
     end
   end
 
@@ -88,7 +88,7 @@ describe TrustLevel3Requirements do
       topic2      = create_post(created_at: 101.days.ago).topic
       _reply2      = create_post(topic: topic2, user: user, created_at: 101.days.ago) # topic is over 100 days old
 
-      tl3_requirements.num_topics_replied_to.should == 1
+      expect(tl3_requirements.num_topics_replied_to).to eq(1)
     end
   end
 
@@ -99,7 +99,7 @@ describe TrustLevel3Requirements do
       make_view(9, 3.days.ago,   user.id) # same topic, different day
       make_view(3, 4.days.ago,   user.id)
       make_view(2, 101.days.ago, user.id) # too long ago
-      tl3_requirements.topics_viewed.should == 2
+      expect(tl3_requirements.topics_viewed).to eq(2)
     end
   end
 
@@ -110,7 +110,7 @@ describe TrustLevel3Requirements do
       user.update_posts_read!(1, 3.days.ago)
       user.update_posts_read!(0, 4.days.ago)
       user.update_posts_read!(5, 101.days.ago)
-      tl3_requirements.posts_read.should == 4
+      expect(tl3_requirements.posts_read).to eq(4)
     end
   end
 
@@ -120,7 +120,7 @@ describe TrustLevel3Requirements do
       make_view(10, 1.day.ago,    user.id)
       make_view(9,  100.days.ago, user.id)
       make_view(8,  101.days.ago, user.id)
-      tl3_requirements.topics_viewed_all_time.should == 3
+      expect(tl3_requirements.topics_viewed_all_time).to eq(3)
     end
   end
 
@@ -129,7 +129,7 @@ describe TrustLevel3Requirements do
       user.save
       user.update_posts_read!(3, 2.days.ago)
       user.update_posts_read!(1, 101.days.ago)
-      tl3_requirements.posts_read_all_time.should == 4
+      expect(tl3_requirements.posts_read_all_time).to eq(4)
     end
   end
 
@@ -159,8 +159,8 @@ describe TrustLevel3Requirements do
     end
 
     it "num_flagged_posts and num_flagged_by_users count spam and inappropriate agreed flags in the last 100 days" do
-      tl3_requirements.num_flagged_posts.should == 2
-      tl3_requirements.num_flagged_by_users.should == 3
+      expect(tl3_requirements.num_flagged_posts).to eq(2)
+      expect(tl3_requirements.num_flagged_by_users).to eq(3)
     end
   end
 
@@ -176,7 +176,7 @@ describe TrustLevel3Requirements do
       Fabricate(:like, user: user, post: recent_post2, created_at: 5.days.ago)
       Fabricate(:like, user: user, post: old_post,     created_at: 101.days.ago)
 
-      tl3_requirements.num_likes_given.should == 2
+      expect(tl3_requirements.num_likes_given).to eq(2)
     end
   end
 
@@ -196,9 +196,9 @@ describe TrustLevel3Requirements do
       Fabricate(:like, user: liker,  post: recent_post2, created_at: 5.days.ago)
       Fabricate(:like, user: liker,  post: old_post,     created_at: 101.days.ago)
 
-      tl3_requirements.num_likes_received.should == 3
-      tl3_requirements.num_likes_received_days.should == 2
-      tl3_requirements.num_likes_received_users.should == 2
+      expect(tl3_requirements.num_likes_received).to eq(3)
+      expect(tl3_requirements.num_likes_received_days).to eq(2)
+      expect(tl3_requirements.num_likes_received_users).to eq(2)
     end
   end
 
@@ -233,12 +233,12 @@ describe TrustLevel3Requirements do
     end
 
     it "are met when all requirements are met" do
-      tl3_requirements.requirements_met?.should == true
+      expect(tl3_requirements.requirements_met?).to eq(true)
     end
 
     it "are not met if too few days visited" do
       tl3_requirements.stubs(:days_visited).returns(49)
-      tl3_requirements.requirements_met?.should == false
+      expect(tl3_requirements.requirements_met?).to eq(false)
     end
 
     it "are not lost if requirements are close" do
@@ -248,77 +248,77 @@ describe TrustLevel3Requirements do
       tl3_requirements.stubs(:posts_read).returns(23)
       tl3_requirements.stubs(:num_likes_given).returns(29)
       tl3_requirements.stubs(:num_likes_received).returns(19)
-      tl3_requirements.requirements_lost?.should == false
+      expect(tl3_requirements.requirements_lost?).to eq(false)
     end
 
     it "are lost if not enough visited" do
       tl3_requirements.stubs(:days_visited).returns(44)
-      tl3_requirements.requirements_lost?.should == true
+      expect(tl3_requirements.requirements_lost?).to eq(true)
     end
 
     it "are lost if not enough topics replied to" do
       tl3_requirements.stubs(:num_topics_replied_to).returns(8)
-      tl3_requirements.requirements_lost?.should == true
+      expect(tl3_requirements.requirements_lost?).to eq(true)
     end
 
     it "are lost if not enough topics viewed" do
       tl3_requirements.stubs(:topics_viewed).returns(22)
-      tl3_requirements.requirements_lost?.should == true
+      expect(tl3_requirements.requirements_lost?).to eq(true)
     end
 
     it "are lost if not enough posts read" do
       tl3_requirements.stubs(:posts_read).returns(22)
-      tl3_requirements.requirements_lost?.should == true
+      expect(tl3_requirements.requirements_lost?).to eq(true)
     end
 
     it "are not met if not enough likes given" do
       tl3_requirements.stubs(:num_likes_given).returns(29)
-      tl3_requirements.requirements_met?.should == false
+      expect(tl3_requirements.requirements_met?).to eq(false)
     end
 
     it "are not met if not enough likes received" do
       tl3_requirements.stubs(:num_likes_received).returns(19)
-      tl3_requirements.requirements_met?.should == false
+      expect(tl3_requirements.requirements_met?).to eq(false)
     end
 
     it "are not met if not enough likes received on different days" do
       tl3_requirements.stubs(:num_likes_received_days).returns(6)
-      tl3_requirements.requirements_met?.should == false
+      expect(tl3_requirements.requirements_met?).to eq(false)
     end
 
     it "are not met if not enough likes received by different users" do
       tl3_requirements.stubs(:num_likes_received_users).returns(4)
-      tl3_requirements.requirements_met?.should == false
+      expect(tl3_requirements.requirements_met?).to eq(false)
     end
 
     it "are lost if not enough likes given" do
       tl3_requirements.stubs(:num_likes_given).returns(26)
-      tl3_requirements.requirements_lost?.should == true
+      expect(tl3_requirements.requirements_lost?).to eq(true)
     end
 
     it "are lost if not enough likes received" do
       tl3_requirements.stubs(:num_likes_received).returns(17)
-      tl3_requirements.requirements_lost?.should == true
+      expect(tl3_requirements.requirements_lost?).to eq(true)
     end
 
     it "are not met if suspended" do
       user.stubs(:suspended?).returns(true)
-      tl3_requirements.requirements_met?.should == false
+      expect(tl3_requirements.requirements_met?).to eq(false)
     end
 
     it "are lost if not enough likes received on different days" do
       tl3_requirements.stubs(:num_likes_received_days).returns(4)
-      tl3_requirements.requirements_lost?.should == true
+      expect(tl3_requirements.requirements_lost?).to eq(true)
     end
 
     it "are lost if not enough likes received by different users" do
       tl3_requirements.stubs(:num_likes_received_users).returns(3)
-      tl3_requirements.requirements_lost?.should == true
+      expect(tl3_requirements.requirements_lost?).to eq(true)
     end
 
     it "are lost if suspended" do
       user.stubs(:suspended?).returns(true)
-      tl3_requirements.requirements_lost?.should == true
+      expect(tl3_requirements.requirements_lost?).to eq(true)
     end
   end
 
diff --git a/spec/models/twitter_user_info_spec.rb b/spec/models/twitter_user_info_spec.rb
index be6d28a7bb..1ed1457f28 100644
--- a/spec/models/twitter_user_info_spec.rb
+++ b/spec/models/twitter_user_info_spec.rb
@@ -5,6 +5,6 @@ describe TwitterUserInfo do
     id =  22019458041
     info = TwitterUserInfo.create!(user_id: -1, screen_name: 'sam', twitter_user_id: id)
     info.reload
-    info.twitter_user_id.should == id
+    expect(info.twitter_user_id).to eq(id)
   end
 end
diff --git a/spec/models/upload_spec.rb b/spec/models/upload_spec.rb
index 573d53191a..092006b38c 100644
--- a/spec/models/upload_spec.rb
+++ b/spec/models/upload_spec.rb
@@ -2,12 +2,12 @@ require 'spec_helper'
 require 'digest/sha1'
 
 describe Upload do
-  it { should belong_to :user }
+  it { is_expected.to belong_to :user }
 
-  it { should have_many :post_uploads }
-  it { should have_many :posts }
+  it { is_expected.to have_many :post_uploads }
+  it { is_expected.to have_many :posts }
 
-  it { should have_many :optimized_images }
+  it { is_expected.to have_many :optimized_images }
 
   let(:upload) { build(:upload) }
   let(:thumbnail) { build(:optimized_image, upload: upload) }
@@ -44,7 +44,7 @@ describe Upload do
       OptimizedImage.expects(:create_for).returns(thumbnail)
       upload.create_thumbnail!(100, 100)
       upload.reload
-      upload.optimized_images.count.should == 1
+      expect(upload.optimized_images.count).to eq(1)
     end
 
   end
@@ -56,7 +56,7 @@ describe Upload do
     it "does not create another upload if it already exists" do
       Upload.expects(:find_by).with(sha1: image_sha1).returns(upload)
       Upload.expects(:save).never
-      Upload.create_for(user_id, image, image_filename, image_filesize).should == upload
+      expect(Upload.create_for(user_id, image, image_filename, image_filesize)).to eq(upload)
     end
 
     it "fix image orientation" do
@@ -75,13 +75,25 @@ describe Upload do
       FileHelper.expects(:is_image?).returns(true)
       Upload.expects(:save).never
       upload = Upload.create_for(user_id, attachment, attachment_filename, attachment_filesize)
-      upload.errors.size.should > 0
+      expect(upload.errors.size).to be > 0
     end
 
     it "does not compute width & height for non-image" do
       FastImage.any_instance.expects(:size).never
       upload = Upload.create_for(user_id, attachment, attachment_filename, attachment_filesize)
-      upload.errors.size.should > 0
+      expect(upload.errors.size).to be > 0
+    end
+
+    it "generates an error when the image is too large" do
+      SiteSetting.stubs(:max_image_size_kb).returns(1)
+      upload = Upload.create_for(user_id, image, image_filename, image_filesize)
+      expect(upload.errors.size).to be > 0
+    end
+
+    it "generates an error when the attachment is too large" do
+      SiteSetting.stubs(:max_attachment_size_kb).returns(1)
+      upload = Upload.create_for(user_id, attachment, attachment_filename, attachment_filesize)
+      expect(upload.errors.size).to be > 0
     end
 
     it "saves proper information" do
@@ -91,13 +103,13 @@ describe Upload do
 
       upload = Upload.create_for(user_id, image, image_filename, image_filesize)
 
-      upload.user_id.should == user_id
-      upload.original_filename.should == image_filename
-      upload.filesize.should == image_filesize
-      upload.sha1.should == image_sha1
-      upload.width.should == 244
-      upload.height.should == 66
-      upload.url.should == url
+      expect(upload.user_id).to eq(user_id)
+      expect(upload.original_filename).to eq(image_filename)
+      expect(upload.filesize).to eq(image_filesize)
+      expect(upload.sha1).to eq(image_sha1)
+      expect(upload.width).to eq(244)
+      expect(upload.height).to eq(66)
+      expect(upload.url).to eq(url)
     end
 
     context "when svg is authorized" do
@@ -111,12 +123,12 @@ describe Upload do
 
         upload = Upload.create_for(user_id, image_svg, image_svg_filename, image_svg_filesize)
 
-        upload.user_id.should == user_id
-        upload.original_filename.should == image_svg_filename
-        upload.filesize.should == image_svg_filesize
-        upload.width.should == 100
-        upload.height.should == 50
-        upload.url.should == url
+        expect(upload.user_id).to eq(user_id)
+        expect(upload.original_filename).to eq(image_svg_filename)
+        expect(upload.filesize).to eq(image_svg_filesize)
+        expect(upload.width).to eq(100)
+        expect(upload.height).to eq(50)
+        expect(upload.url).to eq(url)
       end
 
     end
diff --git a/spec/models/user_action_spec.rb b/spec/models/user_action_spec.rb
index b38f546f73..e4fd78823e 100644
--- a/spec/models/user_action_spec.rb
+++ b/spec/models/user_action_spec.rb
@@ -6,8 +6,8 @@ describe UserAction do
     ActiveRecord::Base.observers.enable :all
   end
 
-  it { should validate_presence_of :action_type }
-  it { should validate_presence_of :user_id }
+  it { is_expected.to validate_presence_of :action_type }
+  it { is_expected.to validate_presence_of :user_id }
 
   describe 'lists' do
 
@@ -51,18 +51,18 @@ describe UserAction do
     it 'includes the events correctly' do
       mystats = stats_for_user(user)
       expecting = [UserAction::NEW_TOPIC, UserAction::NEW_PRIVATE_MESSAGE, UserAction::GOT_PRIVATE_MESSAGE, UserAction::BOOKMARK].sort
-      mystats.should == expecting
-      stream_count(user).should == 4
+      expect(mystats).to eq(expecting)
+      expect(stream_count(user)).to eq(4)
 
       other_stats = stats_for_user
       expecting = [UserAction::NEW_TOPIC]
-      stream_count.should == 1
+      expect(stream_count).to eq(1)
 
-      other_stats.should == expecting
+      expect(other_stats).to eq(expecting)
 
       public_topic.trash!(user)
-      stats_for_user.should == []
-      stream_count.should == 0
+      expect(stats_for_user).to eq([])
+      expect(stream_count).to eq(0)
 
       # groups
       category = Fabricate(:category, read_restricted: true)
@@ -71,8 +71,8 @@ describe UserAction do
       public_topic.category = category
       public_topic.save
 
-      stats_for_user.should == []
-      stream_count.should == 0
+      expect(stats_for_user).to eq([])
+      expect(stream_count).to eq(0)
 
       group = Fabricate(:group)
       u = Fabricate(:coding_horror)
@@ -82,8 +82,8 @@ describe UserAction do
       category.set_permissions(group => :full)
       category.save
 
-      stats_for_user(u).should == [UserAction::NEW_TOPIC]
-      stream_count(u).should == 1
+      expect(stats_for_user(u)).to eq([UserAction::NEW_TOPIC])
+      expect(stream_count(u)).to eq(1)
 
       # duplicate should not exception out
       log_test_action
@@ -94,8 +94,8 @@ describe UserAction do
       public_post.revise(admin, { category_id: category2.id})
 
       action = UserAction.stream(user_id: public_topic.user_id, guardian: Guardian.new)[0]
-      action.acting_user_id.should == admin.id
-      action.action_type.should == UserAction::EDIT
+      expect(action.acting_user_id).to eq(admin.id)
+      expect(action.action_type).to eq(UserAction::EDIT)
     end
 
   end
@@ -116,7 +116,7 @@ describe UserAction do
 
     it "creates a new stream entry" do
       PostAction.act(liker, post, PostActionType.types[:like])
-      likee_stream.count.should == @old_count + 1
+      expect(likee_stream.count).to eq(@old_count + 1)
     end
 
     context "successful like" do
@@ -127,14 +127,14 @@ describe UserAction do
       end
 
       it 'should result in correct data assignment' do
-        @liker_action.should_not == nil
-        @likee_action.should_not == nil
-        likee.user_stat.reload.likes_received.should == 1
-        liker.user_stat.reload.likes_given.should == 1
+        expect(@liker_action).not_to eq(nil)
+        expect(@likee_action).not_to eq(nil)
+        expect(likee.user_stat.reload.likes_received).to eq(1)
+        expect(liker.user_stat.reload.likes_given).to eq(1)
 
         PostAction.remove_act(liker, post, PostActionType.types[:like])
-        likee.user_stat.reload.likes_received.should == 0
-        liker.user_stat.reload.likes_given.should == 0
+        expect(likee.user_stat.reload.likes_received).to eq(0)
+        expect(liker.user_stat.reload.likes_given).to eq(0)
       end
 
     end
@@ -147,7 +147,7 @@ describe UserAction do
 
       it "doesn't add the entry to the stream" do
         PostAction.act(liker, post, PostActionType.types[:like])
-        likee_stream.count.should_not == @old_count + 1
+        expect(likee_stream.count).not_to eq(@old_count + 1)
       end
 
     end
@@ -169,13 +169,13 @@ describe UserAction do
         @action = @post.user.user_actions.find_by(action_type: UserAction::NEW_TOPIC)
       end
       it 'should exist' do
-        @action.should_not == nil
-        @action.created_at.should be_within(1).of(@post.topic.created_at)
+        expect(@action).not_to eq(nil)
+        expect(@action.created_at).to be_within(1).of(@post.topic.created_at)
       end
     end
 
     it 'should not log a post user action' do
-      @post.user.user_actions.find_by(action_type: UserAction::REPLY).should == nil
+      expect(@post.user.user_actions.find_by(action_type: UserAction::REPLY)).to eq(nil)
     end
 
 
@@ -189,16 +189,16 @@ describe UserAction do
       end
 
       it 'should log user actions correctly' do
-        @response.user.user_actions.find_by(action_type: UserAction::REPLY).should_not == nil
-        @post.user.user_actions.find_by(action_type: UserAction::RESPONSE).should_not == nil
-        @mentioned.user_actions.find_by(action_type: UserAction::MENTION).should_not == nil
-        @post.user.user_actions.joins(:target_post).where('posts.post_number = 2').count.should == 1
+        expect(@response.user.user_actions.find_by(action_type: UserAction::REPLY)).not_to eq(nil)
+        expect(@post.user.user_actions.find_by(action_type: UserAction::RESPONSE)).not_to eq(nil)
+        expect(@mentioned.user_actions.find_by(action_type: UserAction::MENTION)).not_to eq(nil)
+        expect(@post.user.user_actions.joins(:target_post).where('posts.post_number = 2').count).to eq(1)
       end
 
       it 'should not log a double notification for a post edit' do
         @response.raw = "here it goes again"
         @response.save!
-        @response.user.user_actions.where(action_type: UserAction::REPLY).count.should == 1
+        expect(@response.user.user_actions.where(action_type: UserAction::REPLY).count).to eq(1)
       end
 
     end
@@ -213,13 +213,13 @@ describe UserAction do
     end
 
     it 'should create a bookmark action correctly' do
-      @action.action_type.should == UserAction::BOOKMARK
-      @action.target_post_id.should == @post.id
-      @action.acting_user_id.should == @user.id
-      @action.user_id.should == @user.id
+      expect(@action.action_type).to eq(UserAction::BOOKMARK)
+      expect(@action.target_post_id).to eq(@post.id)
+      expect(@action.acting_user_id).to eq(@user.id)
+      expect(@action.user_id).to eq(@user.id)
 
       PostAction.remove_act(@user, @post, PostActionType.types[:bookmark])
-      @user.user_actions.find_by(action_type: UserAction::BOOKMARK).should == nil
+      expect(@user.user_actions.find_by(action_type: UserAction::BOOKMARK)).to eq(nil)
     end
   end
 
@@ -257,38 +257,6 @@ describe UserAction do
 
   end
 
-  describe 'synchronize_starred' do
-    it 'corrects out of sync starred' do
-      post = Fabricate(:post)
-      post.topic.toggle_star(post.user, true)
-      UserAction.delete_all
-
-      UserAction.log_action!(
-        action_type: UserAction::STAR,
-        user_id: post.user.id,
-        acting_user_id: post.user.id,
-        target_topic_id: 99,
-        target_post_id: -1,
-      )
-
-      UserAction.log_action!(
-        action_type: UserAction::STAR,
-        user_id: Fabricate(:user).id,
-        acting_user_id: post.user.id,
-        target_topic_id: post.topic_id,
-        target_post_id: -1,
-      )
-
-      UserAction.synchronize_starred
-
-      actions = UserAction.all.to_a
-
-      actions.length.should == 1
-      actions.first.action_type.should == UserAction::STAR
-      actions.first.user_id.should == post.user.id
-    end
-  end
-
   describe 'synchronize_target_topic_ids' do
     it 'correct target_topic_id' do
       post = Fabricate(:post)
@@ -312,7 +280,7 @@ describe UserAction do
       UserAction.synchronize_target_topic_ids
 
       action.reload
-      action.target_topic_id.should == post.topic_id
+      expect(action.target_topic_id).to eq(post.topic_id)
     end
   end
 end
diff --git a/spec/models/user_avatar_spec.rb b/spec/models/user_avatar_spec.rb
index d084281f9e..a46ce21b48 100644
--- a/spec/models/user_avatar_spec.rb
+++ b/spec/models/user_avatar_spec.rb
@@ -15,6 +15,6 @@ describe UserAvatar do
     FileHelper.expects(:download).returns(temp)
     avatar.update_gravatar!
     temp.unlink
-    avatar.gravatar_upload.should_not == nil
+    expect(avatar.gravatar_upload).not_to eq(nil)
   end
 end
diff --git a/spec/models/user_history_spec.rb b/spec/models/user_history_spec.rb
index 67d137124b..f33a497d68 100644
--- a/spec/models/user_history_spec.rb
+++ b/spec/models/user_history_spec.rb
@@ -11,12 +11,12 @@ describe UserHistory do
 
       it "returns all records for admins" do
         records = described_class.staff_action_records(Fabricate(:admin)).to_a
-        records.size.should == 2
+        expect(records.size).to eq(2)
       end
 
       it "doesn't return records to moderators that only admins should see" do
         records = described_class.staff_action_records(Fabricate(:moderator)).to_a
-        records.should == [@change_trust_level]
+        expect(records).to eq([@change_trust_level])
       end
     end
   end
diff --git a/spec/models/user_open_id_spec.rb b/spec/models/user_open_id_spec.rb
index 43919009ec..f37efc363b 100644
--- a/spec/models/user_open_id_spec.rb
+++ b/spec/models/user_open_id_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
 
 describe UserOpenId do
 
-  it { should belong_to :user }
-  it { should validate_presence_of :email }
-  it { should validate_presence_of :url }
+  it { is_expected.to belong_to :user }
+  it { is_expected.to validate_presence_of :email }
+  it { is_expected.to validate_presence_of :url }
 end
diff --git a/spec/models/user_profile_spec.rb b/spec/models/user_profile_spec.rb
index ad18eb8617..0e7e259360 100644
--- a/spec/models/user_profile_spec.rb
+++ b/spec/models/user_profile_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
 describe UserProfile do
   it 'is created automatically when a user is created' do
     user = Fabricate(:evil_trout)
-    user.user_profile.should be_present
+    expect(user.user_profile).to be_present
   end
 
   describe 'rebaking' do
@@ -12,11 +12,11 @@ describe UserProfile do
       user_profile.update_columns(bio_raw: "test", bio_cooked: "broken", bio_cooked_version: nil)
 
       problems = UserProfile.rebake_old(10)
-      problems.length.should == 0
+      expect(problems.length).to eq(0)
 
       user_profile.reload
-      user_profile.bio_cooked.should == "

test

" - user_profile.bio_cooked_version.should == UserProfile::BAKED_VERSION + expect(user_profile.bio_cooked).to eq("

test

") + expect(user_profile.bio_cooked_version).to eq(UserProfile::BAKED_VERSION) end end @@ -34,7 +34,7 @@ describe UserProfile do it "doesn't support really long bios" do user_profile = Fabricate.build(:user_profile_long) - user_profile.should_not be_valid + expect(user_profile).not_to be_valid end describe 'after save' do @@ -65,7 +65,7 @@ describe UserProfile do end it 'should markdown the raw_bio and put it in cooked_bio' do - user.user_profile.bio_cooked.should == "

turtle power!

" + expect(user.user_profile.bio_cooked).to eq("

turtle power!

") end end @@ -103,6 +103,13 @@ describe UserProfile do expect(user_profile.bio_processed).to eq("

I love http://discourse.org

") end + it 'removes the link if the user is suspended' do + user.suspended_till = 1.month.from_now + user_profile.send(:cook) + expect(user_profile.bio_excerpt).to match_html("I love http://discourse.org") + expect(user_profile.bio_processed).to eq("

I love http://discourse.org

") + end + context 'tl3_links_no_follow is false' do before { SiteSetting.stubs(:tl3_links_no_follow).returns(false) } diff --git a/spec/models/user_search_spec.rb b/spec/models/user_search_spec.rb index 52d8d489fd..13a932c45a 100644 --- a/spec/models/user_search_spec.rb +++ b/spec/models/user_search_spec.rb @@ -37,80 +37,80 @@ describe UserSearch do # normal search results = search_for(user1.name.split(" ").first) - results.size.should == 1 - results.first.should == user1 + expect(results.size).to eq(1) + expect(results.first).to eq(user1) # lower case results = search_for(user1.name.split(" ").first.downcase) - results.size.should == 1 - results.first.should == user1 + expect(results.size).to eq(1) + expect(results.first).to eq(user1) # username results = search_for(user4.username) - results.size.should == 1 - results.first.should == user4 + expect(results.size).to eq(1) + expect(results.first).to eq(user4) # case insensitive results = search_for(user4.username.upcase) - results.size.should == 1 - results.first.should == user4 + expect(results.size).to eq(1) + expect(results.first).to eq(user4) # substrings # only staff members see suspended users in results results = search_for("mr") - results.size.should == 5 - results.should_not include(user6) - search_for("mr", searching_user: user1).size.should == 5 + expect(results.size).to eq(5) + expect(results).not_to include(user6) + expect(search_for("mr", searching_user: user1).size).to eq(5) results = search_for("mr", searching_user: admin) - results.size.should == 6 - results.should include(user6) - search_for("mr", searching_user: moderator).size.should == 6 + expect(results.size).to eq(6) + expect(results).to include(user6) + expect(search_for("mr", searching_user: moderator).size).to eq(6) results = search_for("mrb", searching_user: admin) - results.size.should == 3 + expect(results.size).to eq(3) results = search_for("MR", searching_user: admin) - results.size.should == 6 + expect(results.size).to eq(6) results = search_for("MRB", searching_user: admin, limit: 2) - results.size.should == 2 + expect(results.size).to eq(2) # topic priority results = search_for("mrb", topic_id: topic.id) - results.first.should == user1 + expect(results.first).to eq(user1) results = search_for("mrb", topic_id: topic2.id) - results[1].should == user2 + expect(results[1]).to eq(user2) results = search_for("mrb", topic_id: topic3.id) - results[1].should == user5 + expect(results[1]).to eq(user5) # When searching by name is enabled, it returns the record SiteSetting.enable_names = true results = search_for("Tarantino") - results.size.should == 1 + expect(results.size).to eq(1) results = search_for("coding") - results.size.should == 0 + expect(results.size).to eq(0) results = search_for("z") - results.size.should == 0 + expect(results.size).to eq(0) # When searching by name is disabled, it will not return the record SiteSetting.enable_names = false results = search_for("Tarantino") - results.size.should == 0 + expect(results.size).to eq(0) # find an exact match first results = search_for("mrB") - results.first.should == user1 + expect(results.first).to eq(user1) # don't return inactive users results = search_for("Ghost") - results.should be_blank + expect(results).to be_blank end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index d2d1d79afe..71858f1092 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -3,8 +3,27 @@ require_dependency 'user' describe User do - it { should validate_presence_of :username } - it { should validate_presence_of :email } + it { is_expected.to validate_presence_of :username } + it { is_expected.to validate_presence_of :email } + + describe '#count_by_signup_date' do + before(:each) do + User.destroy_all + Timecop.freeze + Fabricate(:user) + Fabricate(:user, created_at: 1.day.ago) + Fabricate(:user, created_at: 1.day.ago) + Fabricate(:user, created_at: 2.days.ago) + Fabricate(:user, created_at: 4.days.ago) + end + after(:each) { Timecop.return } + let(:signups_by_day) { {1.day.ago.to_date => 2, 2.days.ago.to_date => 1, Time.now.utc.to_date => 1} } + + it 'collect closed interval signups' do + expect(User.count_by_signup_date(2.days.ago, Time.now)).to include(signups_by_day) + expect(User.count_by_signup_date(2.days.ago, Time.now)).not_to include({4.days.ago.to_date => 1}) + end + end context '.enqueue_welcome_message' do let(:user) { Fabricate(:user) } @@ -39,15 +58,15 @@ describe User do end it 'marks the user as approved' do - user.should be_approved + expect(user).to be_approved end it 'has the admin as the approved by' do - user.approved_by.should == admin + expect(user.approved_by).to eq(admin) end it 'has a value for approved_at' do - user.approved_at.should be_present + expect(user.approved_at).to be_present end end end @@ -59,9 +78,9 @@ describe User do end it "creates a bookmark with the true parameter" do - lambda { + expect { PostAction.act(@post.user, @post, PostActionType.types[:bookmark]) - }.should change(PostAction, :count).by(1) + }.to change(PostAction, :count).by(1) end describe 'when removing a bookmark' do @@ -71,9 +90,9 @@ describe User do it 'reduces the bookmark count of the post' do active = PostAction.where(deleted_at: nil) - lambda { + expect { PostAction.remove_act(@post.user, @post, PostActionType.types[:bookmark]) - }.should change(active, :count).by(-1) + }.to change(active, :count).by(-1) end end end @@ -90,17 +109,17 @@ describe User do end it 'returns true' do - @result.should == true + expect(@result).to eq(true) end it 'should change the username' do user.reload - user.username.should == new_username + expect(user.username).to eq(new_username) end it 'should change the username_lower' do user.reload - user.username_lower.should == new_username.downcase + expect(user.username_lower).to eq(new_username.downcase) end end @@ -114,17 +133,17 @@ describe User do end it 'returns false' do - @result.should == false + expect(@result).to eq(false) end it 'should not change the username' do user.reload - user.username.should == username_before_change + expect(user.username).to eq(username_before_change) end it 'should not change the username_lower' do user.reload - user.username_lower.should == username_lower_before_change + expect(user.username_lower).to eq(username_lower_before_change) end end @@ -132,12 +151,12 @@ describe User do let!(:myself) { Fabricate(:user, username: 'hansolo') } it 'should return true' do - myself.change_username('HanSolo').should == true + expect(myself.change_username('HanSolo')).to eq(true) end it 'should change the username' do myself.change_username('HanSolo') - myself.reload.username.should == 'HanSolo' + expect(myself.reload.username).to eq('HanSolo') end end @@ -149,17 +168,17 @@ describe User do it 'should allow a shorter username than default' do result = user.change_username('a' * @custom_min) - result.should_not == false + expect(result).not_to eq(false) end it 'should not allow a shorter username than limit' do result = user.change_username('a' * (@custom_min - 1)) - result.should == false + expect(result).to eq(false) end it 'should not allow a longer username than limit' do result = user.change_username('a' * (User.username_length.end + 1)) - result.should == false + expect(result).to eq(false) end end end @@ -193,8 +212,8 @@ describe User do @posts.each do |p| p.reload - p.should be_present - p.topic.should be_present + expect(p).to be_present + expect(p.topic).to be_present end end end @@ -203,32 +222,32 @@ describe User do subject { Fabricate.build(:user) } - it { should be_valid } - it { should_not be_admin } - it { should_not be_approved } + it { is_expected.to be_valid } + it { is_expected.not_to be_admin } + it { is_expected.not_to be_approved } it "is properly initialized" do - subject.approved_at.should be_blank - subject.approved_by_id.should be_blank - subject.email_private_messages.should == true - subject.email_direct.should == true + expect(subject.approved_at).to be_blank + expect(subject.approved_by_id).to be_blank + expect(subject.email_private_messages).to eq(true) + expect(subject.email_direct).to eq(true) end context 'digest emails' do it 'defaults to digests every week' do - subject.email_digests.should == true - subject.digest_after_days.should == 7 + expect(subject.email_digests).to eq(true) + expect(subject.digest_after_days).to eq(7) end it 'uses default_digest_email_frequency' do SiteSetting.stubs(:default_digest_email_frequency).returns(1) - subject.email_digests.should == true - subject.digest_after_days.should == 1 + expect(subject.email_digests).to eq(true) + expect(subject.digest_after_days).to eq(1) end it 'disables digests by default if site setting says so' do SiteSetting.stubs(:default_digest_email_frequency).returns('') - subject.email_digests.should == false + expect(subject.email_digests).to eq(false) end end @@ -236,14 +255,20 @@ describe User do before { subject.save } it "has an email token" do - subject.email_tokens.should be_present + expect(subject.email_tokens).to be_present end end it "downcases email addresses" do user = Fabricate.build(:user, email: 'Fancy.Caps.4.U@gmail.com') - user.save - user.reload.email.should == 'fancy.caps.4.u@gmail.com' + user.valid? + expect(user.email).to eq('fancy.caps.4.u@gmail.com') + end + + it "strips whitespace from email addresses" do + user = Fabricate.build(:user, email: ' example@gmail.com ') + user.valid? + expect(user.email).to eq('example@gmail.com') end end @@ -269,49 +294,49 @@ describe User do it "sets to the default trust level setting" do SiteSetting.default_trust_level = TrustLevel[4] - User.new.trust_level.should == TrustLevel[4] + expect(User.new.trust_level).to eq(TrustLevel[4]) end describe 'has_trust_level?' do it "raises an error with an invalid level" do - lambda { user.has_trust_level?(:wat) }.should raise_error + expect { user.has_trust_level?(:wat) }.to raise_error end it "is true for your basic level" do - user.has_trust_level?(TrustLevel[0]).should == true + expect(user.has_trust_level?(TrustLevel[0])).to eq(true) end it "is false for a higher level" do - user.has_trust_level?(TrustLevel[2]).should == false + expect(user.has_trust_level?(TrustLevel[2])).to eq(false) end it "is true if you exceed the level" do user.trust_level = TrustLevel[4] - user.has_trust_level?(TrustLevel[1]).should == true + expect(user.has_trust_level?(TrustLevel[1])).to eq(true) end it "is true for an admin even with a low trust level" do user.trust_level = TrustLevel[0] user.admin = true - user.has_trust_level?(TrustLevel[1]).should == true + expect(user.has_trust_level?(TrustLevel[1])).to eq(true) end end describe 'moderator' do it "isn't a moderator by default" do - user.moderator?.should == false + expect(user.moderator?).to eq(false) end it "is a moderator if the user level is moderator" do user.moderator = true - user.has_trust_level?(TrustLevel[4]).should == true + expect(user.has_trust_level?(TrustLevel[4])).to eq(true) end it "is staff if the user is an admin" do user.admin = true - user.staff?.should == true + expect(user.staff?).to eq(true) end end @@ -325,70 +350,47 @@ describe User do describe '#staff?' do subject { user.staff? } - it { should == false } + it { is_expected.to eq(false) } context 'for a moderator user' do before { user.moderator = true } - it { should == true } + it { is_expected.to eq(true) } end context 'for an admin user' do before { user.admin = true } - it { should == true } + it { is_expected.to eq(true) } end end describe '#regular?' do subject { user.regular? } - it { should == true } + it { is_expected.to eq(true) } context 'for a moderator user' do before { user.moderator = true } - it { should == false } + it { is_expected.to eq(false) } end context 'for an admin user' do before { user.admin = true } - it { should == false } + it { is_expected.to eq(false) } end end end - describe 'temporary_key' do - - let(:user) { Fabricate(:user) } - let!(:temporary_key) { user.temporary_key} - - it 'has a temporary key' do - temporary_key.should be_present - end - - describe 'User#find_by_temporary_key' do - - it 'can be used to find the user' do - User.find_by_temporary_key(temporary_key).should == user - end - - it 'returns nil with an invalid key' do - User.find_by_temporary_key('asdfasdf').should be_blank - end - - end - - end - describe 'email_hash' do before do @user = Fabricate(:user) end it 'should have a sane email hash' do - @user.email_hash.should =~ /^[0-9a-f]{32}$/ + expect(@user.email_hash).to match(/^[0-9a-f]{32}$/) end it 'should use downcase email' do @@ -396,7 +398,7 @@ describe User do @user2 = Fabricate(:user) @user2.email = "ExAmPlE@eXaMpLe.com" - @user.email_hash.should == @user2.email_hash + expect(@user.email_hash).to eq(@user2.email_hash) end it 'should trim whitespace before hashing' do @@ -404,14 +406,14 @@ describe User do @user2 = Fabricate(:user) @user2.email = " example@example.com " - @user.email_hash.should == @user2.email_hash + expect(@user.email_hash).to eq(@user2.email_hash) end end describe 'associated_accounts' do it 'should correctly find social associations' do user = Fabricate(:user) - user.associated_accounts.should == I18n.t("user.no_accounts_associated") + expect(user.associated_accounts).to eq(I18n.t("user.no_accounts_associated")) TwitterUserInfo.create(user_id: user.id, screen_name: "sam", twitter_user_id: 1) FacebookUserInfo.create(user_id: user.id, username: "sam", facebook_user_id: 1) @@ -419,14 +421,14 @@ describe User do GithubUserInfo.create(user_id: user.id, screen_name: "sam", github_user_id: 1) user.reload - user.associated_accounts.should == "Twitter(sam), Facebook(sam), Google(sam@sam.com), Github(sam)" + expect(user.associated_accounts).to eq("Twitter(sam), Facebook(sam), Google(sam@sam.com), Github(sam)") end end describe 'name heuristics' do it 'is able to guess a decent name from an email' do - User.suggest_name('sam.saffron@gmail.com').should == 'Sam Saffron' + expect(User.suggest_name('sam.saffron@gmail.com')).to eq('Sam Saffron') end end @@ -434,26 +436,26 @@ describe User do it "should be #{SiteSetting.min_username_length} chars or longer" do @user = Fabricate.build(:user) @user.username = 'ss' - @user.save.should == false + expect(@user.save).to eq(false) end it "should never end with a ." do @user = Fabricate.build(:user) @user.username = 'sam.' - @user.save.should == false + expect(@user.save).to eq(false) end it "should never contain spaces" do @user = Fabricate.build(:user) @user.username = 'sam s' - @user.save.should == false + expect(@user.save).to eq(false) end ['Bad One', 'Giraf%fe', 'Hello!', '@twitter', 'me@example.com', 'no.dots', 'purple.', '.bilbo', '_nope', 'sa$sy'].each do |bad_nickname| it "should not allow username '#{bad_nickname}'" do @user = Fabricate.build(:user) @user.username = bad_nickname - @user.save.should == false + expect(@user.save).to eq(false) end end end @@ -467,102 +469,114 @@ describe User do it "should not allow saving if username is reused" do @codinghorror.username = @user.username - @codinghorror.save.should == false + expect(@codinghorror.save).to eq(false) end it "should not allow saving if username is reused in different casing" do @codinghorror.username = @user.username.upcase - @codinghorror.save.should == false + expect(@codinghorror.save).to eq(false) end end context '.username_available?' do it "returns true for a username that is available" do - User.username_available?('BruceWayne').should == true + expect(User.username_available?('BruceWayne')).to eq(true) end it 'returns false when a username is taken' do - User.username_available?(Fabricate(:user).username).should == false + expect(User.username_available?(Fabricate(:user).username)).to eq(false) end end describe 'email_validator' do it 'should allow good emails' do user = Fabricate.build(:user, email: 'good@gmail.com') - user.should be_valid + expect(user).to be_valid end it 'should reject some emails based on the email_domains_blacklist site setting' do SiteSetting.stubs(:email_domains_blacklist).returns('mailinator.com') - Fabricate.build(:user, email: 'notgood@mailinator.com').should_not be_valid - Fabricate.build(:user, email: 'mailinator@gmail.com').should be_valid + expect(Fabricate.build(:user, email: 'notgood@mailinator.com')).not_to be_valid + expect(Fabricate.build(:user, email: 'mailinator@gmail.com')).to be_valid end it 'should reject some emails based on the email_domains_blacklist site setting' do SiteSetting.stubs(:email_domains_blacklist).returns('mailinator.com|trashmail.net') - Fabricate.build(:user, email: 'notgood@mailinator.com').should_not be_valid - Fabricate.build(:user, email: 'notgood@trashmail.net').should_not be_valid - Fabricate.build(:user, email: 'mailinator.com@gmail.com').should be_valid + expect(Fabricate.build(:user, email: 'notgood@mailinator.com')).not_to be_valid + expect(Fabricate.build(:user, email: 'notgood@trashmail.net')).not_to be_valid + expect(Fabricate.build(:user, email: 'mailinator.com@gmail.com')).to be_valid end it 'should not reject partial matches' do SiteSetting.stubs(:email_domains_blacklist).returns('mail.com') - Fabricate.build(:user, email: 'mailinator@gmail.com').should be_valid + expect(Fabricate.build(:user, email: 'mailinator@gmail.com')).to be_valid end it 'should reject some emails based on the email_domains_blacklist site setting ignoring case' do SiteSetting.stubs(:email_domains_blacklist).returns('trashmail.net') - Fabricate.build(:user, email: 'notgood@TRASHMAIL.NET').should_not be_valid + expect(Fabricate.build(:user, email: 'notgood@TRASHMAIL.NET')).not_to be_valid + end + + it 'blacklist should not reject developer emails' do + Rails.configuration.stubs(:developer_emails).returns('developer@discourse.org') + SiteSetting.stubs(:email_domains_blacklist).returns('discourse.org') + expect(Fabricate.build(:user, email: 'developer@discourse.org')).to be_valid end it 'should not interpret a period as a wildcard' do SiteSetting.stubs(:email_domains_blacklist).returns('trashmail.net') - Fabricate.build(:user, email: 'good@trashmailinet.com').should be_valid + expect(Fabricate.build(:user, email: 'good@trashmailinet.com')).to be_valid end it 'should not be used to validate existing records' do u = Fabricate(:user, email: 'in_before_blacklisted@fakemail.com') SiteSetting.stubs(:email_domains_blacklist).returns('fakemail.com') - u.should be_valid + expect(u).to be_valid end it 'should be used when email is being changed' do SiteSetting.stubs(:email_domains_blacklist).returns('mailinator.com') u = Fabricate(:user, email: 'good@gmail.com') u.email = 'nope@mailinator.com' - u.should_not be_valid + expect(u).not_to be_valid end it 'whitelist should reject some emails based on the email_domains_whitelist site setting' do SiteSetting.stubs(:email_domains_whitelist).returns('vaynermedia.com') - Fabricate.build(:user, email: 'notgood@mailinator.com').should_not be_valid - Fabricate.build(:user, email: 'sbauch@vaynermedia.com').should be_valid + expect(Fabricate.build(:user, email: 'notgood@mailinator.com')).not_to be_valid + expect(Fabricate.build(:user, email: 'sbauch@vaynermedia.com')).to be_valid end it 'should reject some emails based on the email_domains_whitelist site setting when whitelisting multiple domains' do SiteSetting.stubs(:email_domains_whitelist).returns('vaynermedia.com|gmail.com') - Fabricate.build(:user, email: 'notgood@mailinator.com').should_not be_valid - Fabricate.build(:user, email: 'notgood@trashmail.net').should_not be_valid - Fabricate.build(:user, email: 'mailinator.com@gmail.com').should be_valid - Fabricate.build(:user, email: 'mailinator.com@vaynermedia.com').should be_valid + expect(Fabricate.build(:user, email: 'notgood@mailinator.com')).not_to be_valid + expect(Fabricate.build(:user, email: 'notgood@trashmail.net')).not_to be_valid + expect(Fabricate.build(:user, email: 'mailinator.com@gmail.com')).to be_valid + expect(Fabricate.build(:user, email: 'mailinator.com@vaynermedia.com')).to be_valid end it 'should accept some emails based on the email_domains_whitelist site setting ignoring case' do SiteSetting.stubs(:email_domains_whitelist).returns('vaynermedia.com') - Fabricate.build(:user, email: 'good@VAYNERMEDIA.COM').should be_valid + expect(Fabricate.build(:user, email: 'good@VAYNERMEDIA.COM')).to be_valid + end + + it 'whitelist should accept developer emails' do + Rails.configuration.stubs(:developer_emails).returns('developer@discourse.org') + SiteSetting.stubs(:email_domains_whitelist).returns('awesome.org') + expect(Fabricate.build(:user, email: 'developer@discourse.org')).to be_valid end it 'email whitelist should not be used to validate existing records' do u = Fabricate(:user, email: 'in_before_whitelisted@fakemail.com') SiteSetting.stubs(:email_domains_blacklist).returns('vaynermedia.com') - u.should be_valid + expect(u).to be_valid end it 'email whitelist should be used when email is being changed' do SiteSetting.stubs(:email_domains_whitelist).returns('vaynermedia.com') u = Fabricate(:user, email: 'good@vaynermedia.com') u.email = 'nope@mailinator.com' - u.should_not be_valid + expect(u).not_to be_valid end end @@ -574,11 +588,11 @@ describe User do end it "should have a valid password after the initial save" do - @user.confirm_password?("ilovepasta").should == true + expect(@user.confirm_password?("ilovepasta")).to eq(true) end it "should not have an active account after initial save" do - @user.active.should == false + expect(@user.active).to eq(false) end end @@ -595,26 +609,26 @@ describe User do end it "should act correctly" do - user.previous_visit_at.should == nil + expect(user.previous_visit_at).to eq(nil) # first visit user.update_last_seen!(first_visit_date) - user.previous_visit_at.should == nil + expect(user.previous_visit_at).to eq(nil) # updated same time user.update_last_seen!(first_visit_date) user.reload - user.previous_visit_at.should == nil + expect(user.previous_visit_at).to eq(nil) # second visit user.update_last_seen!(second_visit_date) user.reload - user.previous_visit_at.should be_within_one_second_of(first_visit_date) + expect(user.previous_visit_at).to be_within_one_second_of(first_visit_date) # third visit user.update_last_seen!(third_visit_date) user.reload - user.previous_visit_at.should be_within_one_second_of(second_visit_date) + expect(user.previous_visit_at).to be_within_one_second_of(second_visit_date) end end @@ -623,11 +637,11 @@ describe User do let(:user) { Fabricate(:user) } it "should have a blank last seen on creation" do - user.last_seen_at.should == nil + expect(user.last_seen_at).to eq(nil) end it "should have 0 for days_visited" do - user.user_stat.days_visited.should == 0 + expect(user.user_stat.days_visited).to eq(0) end describe 'with no previous values' do @@ -643,16 +657,16 @@ describe User do end it "updates last_seen_at" do - user.last_seen_at.should be_within_one_second_of(date) + expect(user.last_seen_at).to be_within_one_second_of(date) end it "should have 0 for days_visited" do user.reload - user.user_stat.days_visited.should == 1 + expect(user.user_stat.days_visited).to eq(1) end it "should log a user_visit with the date" do - user.user_visits.first.visited_at.should == date.to_date + expect(user.user_visits.first.visited_at).to eq(date.to_date) end context "called twice" do @@ -669,7 +683,7 @@ describe User do end it "doesn't increase days_visited twice" do - user.user_stat.days_visited.should == 1 + expect(user.user_stat.days_visited).to eq(1) end end @@ -687,7 +701,7 @@ describe User do end it "should log a second visited_at record when we log an update later" do - user.user_visits.count.should == 2 + expect(user.user_visits.count).to eq(2) end end @@ -699,7 +713,7 @@ describe User do context 'when email has not been confirmed yet' do it 'should return false' do - user.email_confirmed?.should == false + expect(user.email_confirmed?).to eq(false) end end @@ -707,7 +721,7 @@ describe User do it 'should return true' do token = user.email_tokens.find_by(email: user.email) EmailToken.confirm(token.token) - user.email_confirmed?.should == true + expect(user.email_confirmed?).to eq(true) end end @@ -715,7 +729,7 @@ describe User do it 'should return false' do user.email_tokens.each {|t| t.destroy} user.reload - user.email_confirmed?.should == true + expect(user.email_confirmed?).to eq(true) end end end @@ -731,13 +745,13 @@ describe User do user.flag_linked_posts_as_spam post.reload - post.spam_count.should == 1 + expect(post.spam_count).to eq(1) another_post.reload - another_post.spam_count.should == 1 + expect(another_post.spam_count).to eq(1) post_without_link.reload - post_without_link.spam_count.should == 0 + expect(post_without_link.spam_count).to eq(0) # It doesn't raise an exception if called again user.flag_linked_posts_as_spam @@ -749,17 +763,17 @@ describe User do describe '#readable_name' do context 'when name is missing' do it 'returns just the username' do - Fabricate(:user, username: 'foo', name: nil).readable_name.should == 'foo' + expect(Fabricate(:user, username: 'foo', name: nil).readable_name).to eq('foo') end end context 'when name and username are identical' do it 'returns just the username' do - Fabricate(:user, username: 'foo', name: 'foo').readable_name.should == 'foo' + expect(Fabricate(:user, username: 'foo', name: 'foo').readable_name).to eq('foo') end end context 'when name and username are not identical' do it 'returns the name and username' do - Fabricate(:user, username: 'foo', name: 'Bar Baz').readable_name.should == 'Bar Baz (foo)' + expect(Fabricate(:user, username: 'foo', name: 'Bar Baz').readable_name).to eq('Bar Baz (foo)') end end end @@ -876,11 +890,11 @@ describe User do it "does not return true for staff" do user.stubs(:staff?).returns(true) - user.posted_too_much_in_topic?(topic.id).should == false + expect(user.posted_too_much_in_topic?(topic.id)).to eq(false) end it "returns true when the user has posted too much" do - user.posted_too_much_in_topic?(topic.id).should == true + expect(user.posted_too_much_in_topic?(topic.id)).to eq(true) end context "with a reply" do @@ -889,7 +903,7 @@ describe User do end it "resets the `posted_too_much` threshold" do - user.posted_too_much_in_topic?(topic.id).should == false + expect(user.posted_too_much_in_topic?(topic.id)).to eq(false) end end end @@ -897,7 +911,7 @@ describe User do it "returns false for a user who created the topic" do topic_user = topic.user topic_user.trust_level = TrustLevel[0] - topic.user.posted_too_much_in_topic?(topic.id).should == false + expect(topic.user.posted_too_much_in_topic?(topic.id)).to eq(false) end end @@ -926,7 +940,7 @@ describe User do describe "#gravatar_template" do it "returns a gravatar based template" do - User.gravatar_template("em@il.com").should == "//www.gravatar.com/avatar/6dc2fde946483a1d8a84b89345a1b638.png?s={size}&r=pg&d=identicon" + expect(User.gravatar_template("em@il.com")).to eq("//www.gravatar.com/avatar/6dc2fde946483a1d8a84b89345a1b638.png?s={size}&r=pg&d=identicon") end end @@ -936,7 +950,7 @@ describe User do let(:user) { build(:user, username: 'Sam') } it "returns a 45-pixel-wide avatar" do - user.small_avatar_url.should == "//test.localhost/letter_avatar/sam/45/#{LetterAvatar::VERSION}.png" + expect(user.small_avatar_url).to eq("//test.localhost/letter_avatar/sam/45/#{LetterAvatar::VERSION}.png") end end @@ -946,12 +960,12 @@ describe User do let(:user) { build(:user, uploaded_avatar_id: 99, username: 'Sam') } it "returns a schemaless avatar template with correct id" do - user.avatar_template_url.should == "//test.localhost/user_avatar/test.localhost/sam/{size}/99.png" + expect(user.avatar_template_url).to eq("//test.localhost/user_avatar/test.localhost/sam/{size}/99.png") end it "returns a schemaless cdn-based avatar template" do Rails.configuration.action_controller.stubs(:asset_host).returns("http://my.cdn.com") - user.avatar_template_url.should == "//my.cdn.com/user_avatar/test.localhost/sam/{size}/99.png" + expect(user.avatar_template_url).to eq("//my.cdn.com/user_avatar/test.localhost/sam/{size}/99.png") end end @@ -965,14 +979,14 @@ describe User do it "with existing UserVisit record, increments the posts_read value" do expect { user_visit = user.update_posts_read!(2) - user_visit.posts_read.should == 2 + expect(user_visit.posts_read).to eq(2) }.to_not change { UserVisit.count } end it "with no existing UserVisit record, creates a new UserVisit record and increments the posts_read count" do expect { user_visit = user.update_posts_read!(3, 5.days.ago) - user_visit.posts_read.should == 3 + expect(user_visit.posts_read).to eq(3) }.to change { UserVisit.count }.by(1) end end @@ -982,7 +996,7 @@ describe User do let!(:user) { Fabricate(:user) } it "has no primary_group_id by default" do - user.primary_group_id.should == nil + expect(user.primary_group_id).to eq(nil) end context "when the user has a group" do @@ -997,7 +1011,7 @@ describe User do end it "should allow us to use it as a primary group" do - user.primary_group_id.should == group.id + expect(user.primary_group_id).to eq(group.id) # If we remove the user from the group group.usernames = "" @@ -1005,22 +1019,38 @@ describe User do # It should unset it from the primary_group_id user.reload - user.primary_group_id.should == nil + expect(user.primary_group_id).to eq(nil) end end end + context "group management" do + let!(:user) { Fabricate(:user) } + + it "by default has no managed groups" do + expect(user.managed_groups).to be_empty + end + + it "can manage multiple groups" do + 3.times do |i| + g = Fabricate(:group, name: "group_#{i}") + g.appoint_manager(user) + end + expect(user.managed_groups.count).to eq(3) + end + end + describe "should_be_redirected_to_top" do let!(:user) { Fabricate(:user) } it "should be redirected to top when there is a reason to" do user.expects(:redirected_to_top_reason).returns("42") - user.should_be_redirected_to_top.should == true + expect(user.should_be_redirected_to_top).to eq(true) end it "should not be redirected to top when there is no reason to" do user.expects(:redirected_to_top_reason).returns(nil) - user.should_be_redirected_to_top.should == false + expect(user.should_be_redirected_to_top).to eq(false) end end @@ -1030,7 +1060,7 @@ describe User do it "should have no reason when `SiteSetting.redirect_users_to_top_page` is disabled" do SiteSetting.expects(:redirect_users_to_top_page).returns(false) - user.redirected_to_top_reason.should == nil + expect(user.redirected_to_top_reason).to eq(nil) end context "when `SiteSetting.redirect_users_to_top_page` is enabled" do @@ -1038,7 +1068,7 @@ describe User do it "should have no reason when top is not in the `SiteSetting.top_menu`" do SiteSetting.expects(:top_menu).returns("latest") - user.redirected_to_top_reason.should == nil + expect(user.redirected_to_top_reason).to eq(nil) end context "and when top is in the `SiteSetting.top_menu`" do @@ -1046,7 +1076,7 @@ describe User do it "should have no reason when there aren't enough topics" do SiteSetting.expects(:has_enough_topics_to_redirect_to_top).returns(false) - user.redirected_to_top_reason.should == nil + expect(user.redirected_to_top_reason).to eq(nil) end context "and when there are enough topics" do @@ -1062,14 +1092,14 @@ describe User do user.expects(:last_redirected_to_top_at).returns(nil) user.expects(:update_last_redirected_to_top!).once - user.redirected_to_top_reason.should == I18n.t('redirected_to_top_reasons.new_user') + expect(user.redirected_to_top_reason).to eq(I18n.t('redirected_to_top_reasons.new_user')) end it "should not have a reason for next visits" do user.expects(:last_redirected_to_top_at).returns(10.minutes.ago) user.expects(:update_last_redirected_to_top!).never - user.redirected_to_top_reason.should == nil + expect(user.redirected_to_top_reason).to eq(nil) end end @@ -1080,7 +1110,7 @@ describe User do user.last_seen_at = 2.months.ago user.expects(:update_last_redirected_to_top!).once - user.redirected_to_top_reason.should == I18n.t('redirected_to_top_reasons.not_seen_in_a_month') + expect(user.redirected_to_top_reason).to eq(I18n.t('redirected_to_top_reasons.not_seen_in_a_month')) end end @@ -1094,11 +1124,10 @@ describe User do describe "automatic avatar creation" do it "sets a system avatar for new users" do - SiteSetting.enable_system_avatars = true u = User.create!(username: "bob", email: "bob@bob.com") u.reload - u.uploaded_avatar_id.should == nil - u.avatar_template.should == "/letter_avatar/bob/{size}/#{LetterAvatar::VERSION}.png" + expect(u.uploaded_avatar_id).to eq(nil) + expect(u.avatar_template).to eq("/letter_avatar/bob/{size}/#{LetterAvatar::VERSION}.png") end end @@ -1106,7 +1135,7 @@ describe User do it "allows modification of custom fields" do user = Fabricate(:user) - user.custom_fields["a"].should == nil + expect(user.custom_fields["a"]).to eq(nil) user.custom_fields["bob"] = "marley" user.custom_fields["jack"] = "black" @@ -1114,8 +1143,8 @@ describe User do user = User.find(user.id) - user.custom_fields["bob"].should == "marley" - user.custom_fields["jack"].should == "black" + expect(user.custom_fields["bob"]).to eq("marley") + expect(user.custom_fields["jack"]).to eq("black") user.custom_fields.delete("bob") user.custom_fields["jack"] = "jill" @@ -1123,49 +1152,33 @@ describe User do user.save user = User.find(user.id) - user.custom_fields.should == {"jack" => "jill"} + expect(user.custom_fields).to eq({"jack" => "jill"}) end end describe "refresh_avatar" do - it "picks gravatar if system avatar is picked and gravatar was just downloaded" do - - png = Base64.decode64("R0lGODlhAQABALMAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwICAgP8AAAD/AP//AAAA//8A/wD//wBiZCH5BAEAAA8ALAAAAAABAAEAAAQC8EUAOw==") - FakeWeb.register_uri( :get, - "http://www.gravatar.com/avatar/d10ca8d11301c2f4993ac2279ce4b930.png?s=500&d=404", - body: png ) - - user = User.create!(username: "bob", name: "bob", email: "a@a.com") - user.reload - + it "enqueues the update_gravatar job when automatically downloading gravatars" do SiteSetting.automatically_download_gravatars = true - SiteSetting.enable_system_avatars = true + + user = Fabricate(:user) + + Jobs.expects(:enqueue).with(:update_gravatar, anything) user.refresh_avatar - user.reload - - user.user_avatar.gravatar_upload_id.should == user.uploaded_avatar_id - - user.uploaded_avatar_id = nil - user.save - user.refresh_avatar - - user.reload - user.uploaded_avatar_id.should == nil end end - describe "#purge_inactive" do + describe "#purge_unactivated" do let!(:user) { Fabricate(:user) } let!(:inactive) { Fabricate(:user, active: false) } let!(:inactive_old) { Fabricate(:user, active: false, created_at: 1.month.ago) } - it 'should only remove old, inactive users' do - User.purge_inactive + it 'should only remove old, unactivated users' do + User.purge_unactivated all_users = User.all - all_users.include?(user).should == true - all_users.include?(inactive).should == true - all_users.include?(inactive_old).should == false + expect(all_users.include?(user)).to eq(true) + expect(all_users.include?(inactive)).to eq(true) + expect(all_users.include?(inactive_old)).to eq(false) end end @@ -1178,19 +1191,53 @@ describe User do end it "returns the same hash for the same password and salt" do - hash('poutine', 'gravy').should == hash('poutine', 'gravy') + expect(hash('poutine', 'gravy')).to eq(hash('poutine', 'gravy')) end it "returns a different hash for the same salt and different password" do - hash('poutine', 'gravy').should_not == hash('fries', 'gravy') + expect(hash('poutine', 'gravy')).not_to eq(hash('fries', 'gravy')) end it "returns a different hash for the same password and different salt" do - hash('poutine', 'gravy').should_not == hash('poutine', 'cheese') + expect(hash('poutine', 'gravy')).not_to eq(hash('poutine', 'cheese')) end it "raises an error when passwords are too long" do - -> { hash(too_long, 'gravy') }.should raise_error + expect { hash(too_long, 'gravy') }.to raise_error + end + + end + + describe "automatic group membership" do + + it "is automatically added to a group when the email matches" do + group = Fabricate(:group, automatic_membership_email_domains: "bar.com|wat.com") + user = Fabricate(:user, email: "foo@bar.com") + group.reload + expect(group.users.include?(user)).to eq(true) + end + + end + + describe "number_of_flags_given" do + + let(:user) { Fabricate(:user) } + let(:moderator) { Fabricate(:moderator) } + + it "doesn't count disagreed flags" do + post_agreed = Fabricate(:post) + PostAction.act(user, post_agreed, PostActionType.types[:off_topic]) + PostAction.agree_flags!(post_agreed, moderator) + + post_deferred = Fabricate(:post) + PostAction.act(user, post_deferred, PostActionType.types[:inappropriate]) + PostAction.defer_flags!(post_deferred, moderator) + + post_disagreed = Fabricate(:post) + PostAction.act(user, post_disagreed, PostActionType.types[:spam]) + PostAction.clear_flags!(post_disagreed, moderator) + + expect(user.number_of_flags_given).to eq(2) end end diff --git a/spec/models/user_stat_spec.rb b/spec/models/user_stat_spec.rb index bf402857d0..7a75f45f77 100644 --- a/spec/models/user_stat_spec.rb +++ b/spec/models/user_stat_spec.rb @@ -2,14 +2,14 @@ require 'spec_helper' describe UserStat do - it { should belong_to :user } + it { is_expected.to belong_to :user } it "is created automatically when a user is created" do user = Fabricate(:evil_trout) - user.user_stat.should be_present + expect(user.user_stat).to be_present # It populates the `new_since` field by default - user.user_stat.new_since.should be_present + expect(user.user_stat.new_since).to be_present end context '#update_view_counts' do @@ -20,7 +20,7 @@ describe UserStat do context 'topics_entered' do context 'without any views' do it "doesn't increase the user's topics_entered" do - lambda { UserStat.update_view_counts; stat.reload }.should_not change(stat, :topics_entered) + expect { UserStat.update_view_counts; stat.reload }.not_to change(stat, :topics_entered) end end @@ -35,14 +35,14 @@ describe UserStat do it "adds one to the topics entered" do UserStat.update_view_counts stat.reload - stat.topics_entered.should == 1 + expect(stat.topics_entered).to eq(1) end it "won't record a second view as a different topic" do TopicViewItem.add(topic.id, '127.0.0.1', user.id) UserStat.update_view_counts stat.reload - stat.topics_entered.should == 1 + expect(stat.topics_entered).to eq(1) end end @@ -51,7 +51,7 @@ describe UserStat do context 'posts_read_count' do context 'without any post timings' do it "doesn't increase the user's posts_read_count" do - lambda { UserStat.update_view_counts; stat.reload }.should_not change(stat, :posts_read_count) + expect { UserStat.update_view_counts; stat.reload }.not_to change(stat, :posts_read_count) end end @@ -68,7 +68,7 @@ describe UserStat do it "increases posts_read_count" do UserStat.update_view_counts stat.reload - stat.posts_read_count.should == 1 + expect(stat.posts_read_count).to eq(1) end end end @@ -83,14 +83,14 @@ describe UserStat do stat.expects(:last_seen_cached).returns(nil) stat.update_time_read! stat.reload - stat.time_read.should == 0 + expect(stat.time_read).to eq(0) end it 'makes a change if time read is below threshold' do stat.expects(:last_seen_cached).returns(Time.now - 10) stat.update_time_read! stat.reload - stat.time_read.should == 10 + expect(stat.time_read).to eq(10) end it 'makes no change if time read is above threshold' do @@ -98,7 +98,7 @@ describe UserStat do stat.expects(:last_seen_cached).returns(t) stat.update_time_read! stat.reload - stat.time_read.should == 0 + expect(stat.time_read).to eq(0) end end diff --git a/spec/models/user_visit_spec.rb b/spec/models/user_visit_spec.rb index 07278b46e3..41cc9564b6 100644 --- a/spec/models/user_visit_spec.rb +++ b/spec/models/user_visit_spec.rb @@ -1,21 +1,41 @@ require 'spec_helper' describe UserVisit do + let(:user) { Fabricate(:user) } + let(:other_user) { Fabricate(:user) } + it 'can ensure consistency' do - u = Fabricate(:user) - u.update_visit_record!(2.weeks.ago.to_date) - u.last_seen_at = 2.weeks.ago - u.save - u.update_visit_record!(1.day.ago.to_date) + user.update_visit_record!(2.weeks.ago.to_date) + user.last_seen_at = 2.weeks.ago + user.save + user.update_visit_record!(1.day.ago.to_date) - u.reload - u.user_stat.days_visited.should == 2 + user.reload + expect(user.user_stat.days_visited).to eq(2) - u.user_stat.days_visited = 1 - u.save + user.user_stat.days_visited = 1 + user.save UserVisit.ensure_consistency! - u.reload - u.user_stat.days_visited.should == 2 + user.reload + expect(user.user_stat.days_visited).to eq(2) + end + + describe '#by_day' do + before(:each) do + Timecop.freeze + user.user_visits.create(visited_at: Time.now) + user.user_visits.create(visited_at: 1.day.ago) + other_user.user_visits.create(visited_at: 1.day.ago) + user.user_visits.create(visited_at: 2.days.ago) + user.user_visits.create(visited_at: 4.days.ago) + end + after(:each) { Timecop.return } + let(:visits_by_day) { {1.day.ago.to_date => 2, 2.days.ago.to_date => 1, Time.now.to_date => 1 } } + + it 'collect closed interval visits' do + expect(UserVisit.by_day(2.days.ago, Time.now)).to include(visits_by_day) + expect(UserVisit.by_day(2.days.ago, Time.now)).not_to include({4.days.ago.to_date => 1}) + end end end diff --git a/spec/serializers/basic_post_serializer_spec.rb b/spec/serializers/basic_post_serializer_spec.rb index c7f1388d02..70c09dfbc1 100644 --- a/spec/serializers/basic_post_serializer_spec.rb +++ b/spec/serializers/basic_post_serializer_spec.rb @@ -12,12 +12,12 @@ describe BasicPostSerializer do it "returns the name it when `enable_names` is true" do SiteSetting.stubs(:enable_names?).returns(true) - json[:name].should be_present + expect(json[:name]).to be_present end it "doesn't return the name it when `enable_names` is false" do SiteSetting.stubs(:enable_names?).returns(false) - json[:name].should be_blank + expect(json[:name]).to be_blank end end diff --git a/spec/serializers/post_serializer_spec.rb b/spec/serializers/post_serializer_spec.rb index de17f41925..b024be3f0e 100644 --- a/spec/serializers/post_serializer_spec.rb +++ b/spec/serializers/post_serializer_spec.rb @@ -30,16 +30,16 @@ describe PostSerializer do end it "displays the correct info" do - visible_actions_for(actor).sort.should == [:like,:notify_user,:spam,:vote] - visible_actions_for(post.user).sort.should == [:like,:vote] - visible_actions_for(nil).sort.should == [:like,:vote] - visible_actions_for(admin).sort.should == [:like,:notify_user,:spam,:vote] + expect(visible_actions_for(actor).sort).to eq([:like,:notify_user,:spam,:vote]) + expect(visible_actions_for(post.user).sort).to eq([:like,:vote]) + expect(visible_actions_for(nil).sort).to eq([:like,:vote]) + expect(visible_actions_for(admin).sort).to eq([:like,:notify_user,:spam,:vote]) end it "can't flag your own post to notify yourself" do serializer = PostSerializer.new(post, scope: Guardian.new(post.user), root: false) notify_user_action = serializer.actions_summary.find { |a| a[:id] == PostActionType.types[:notify_user] } - notify_user_action[:can_act].should == false + expect(notify_user_action[:can_act]).to eq(false) end end @@ -55,10 +55,10 @@ describe PostSerializer do it "serializes correctly" do [:name, :username, :display_username, :avatar_template, :user_title, :trust_level].each do |attr| - subject[attr].should be_nil + expect(subject[attr]).to be_nil end [:moderator, :staff, :yours].each do |attr| - subject[attr].should == false + expect(subject[attr]).to eq(false) end end end @@ -71,12 +71,12 @@ describe PostSerializer do it "returns the display_username it when `enable_names` is on" do SiteSetting.stubs(:enable_names).returns(true) - json[:display_username].should be_present + expect(json[:display_username]).to be_present end it "doesn't return the display_username it when `enable_names` is off" do SiteSetting.stubs(:enable_names).returns(false) - json[:display_username].should be_blank + expect(json[:display_username]).to be_blank end end @@ -95,7 +95,7 @@ describe PostSerializer do it "includes the raw post for everyone" do [nil, user, Fabricate(:user), Fabricate(:moderator), Fabricate(:admin)].each do |user| - serialized_post_for_user(user)[:raw].should == raw + expect(serialized_post_for_user(user)[:raw]).to eq(raw) end end end @@ -104,21 +104,21 @@ describe PostSerializer do let(:post) { Fabricate.build(:post, raw: raw, user: user, hidden: true, hidden_reason_id: Post.hidden_reasons[:flag_threshold_reached]) } it "shows the raw post only if authorized to see it" do - serialized_post_for_user(nil)[:raw].should == nil - serialized_post_for_user(Fabricate(:user))[:raw].should == nil + expect(serialized_post_for_user(nil)[:raw]).to eq(nil) + expect(serialized_post_for_user(Fabricate(:user))[:raw]).to eq(nil) - serialized_post_for_user(user)[:raw].should == raw - serialized_post_for_user(Fabricate(:moderator))[:raw].should == raw - serialized_post_for_user(Fabricate(:admin))[:raw].should == raw + expect(serialized_post_for_user(user)[:raw]).to eq(raw) + expect(serialized_post_for_user(Fabricate(:moderator))[:raw]).to eq(raw) + expect(serialized_post_for_user(Fabricate(:admin))[:raw]).to eq(raw) end it "can view edit history only if authorized" do - serialized_post_for_user(nil)[:can_view_edit_history].should == false - serialized_post_for_user(Fabricate(:user))[:can_view_edit_history].should == false + expect(serialized_post_for_user(nil)[:can_view_edit_history]).to eq(false) + expect(serialized_post_for_user(Fabricate(:user))[:can_view_edit_history]).to eq(false) - serialized_post_for_user(user)[:can_view_edit_history].should == true - serialized_post_for_user(Fabricate(:moderator))[:can_view_edit_history].should == true - serialized_post_for_user(Fabricate(:admin))[:can_view_edit_history].should == true + expect(serialized_post_for_user(user)[:can_view_edit_history]).to eq(true) + expect(serialized_post_for_user(Fabricate(:moderator))[:can_view_edit_history]).to eq(true) + expect(serialized_post_for_user(Fabricate(:admin))[:can_view_edit_history]).to eq(true) end end @@ -127,7 +127,7 @@ describe PostSerializer do it "can view edit history" do [nil, user, Fabricate(:user), Fabricate(:moderator), Fabricate(:admin)].each do |user| - serialized_post_for_user(user)[:can_view_edit_history].should == true + expect(serialized_post_for_user(user)[:can_view_edit_history]).to eq(true) end end end @@ -136,12 +136,12 @@ describe PostSerializer do let(:post) { Fabricate.build(:post, raw: raw, user: user, wiki: true, hidden: true, hidden_reason_id: Post.hidden_reasons[:flag_threshold_reached]) } it "can view edit history only if authorized" do - serialized_post_for_user(nil)[:can_view_edit_history].should == false - serialized_post_for_user(Fabricate(:user))[:can_view_edit_history].should == false + expect(serialized_post_for_user(nil)[:can_view_edit_history]).to eq(false) + expect(serialized_post_for_user(Fabricate(:user))[:can_view_edit_history]).to eq(false) - serialized_post_for_user(user)[:can_view_edit_history].should == true - serialized_post_for_user(Fabricate(:moderator))[:can_view_edit_history].should == true - serialized_post_for_user(Fabricate(:admin))[:can_view_edit_history].should == true + expect(serialized_post_for_user(user)[:can_view_edit_history]).to eq(true) + expect(serialized_post_for_user(Fabricate(:moderator))[:can_view_edit_history]).to eq(true) + expect(serialized_post_for_user(Fabricate(:admin))[:can_view_edit_history]).to eq(true) end end diff --git a/spec/serializers/topic_list_item_serializer_spec.rb b/spec/serializers/topic_list_item_serializer_spec.rb index 295d340fe9..3cbdc2e40a 100644 --- a/spec/serializers/topic_list_item_serializer_spec.rb +++ b/spec/serializers/topic_list_item_serializer_spec.rb @@ -13,7 +13,7 @@ describe TopicListItemSerializer do topic.posters = [] serialized = TopicListItemSerializer.new(topic, scope: Guardian.new, root: false).as_json - serialized[:title].should == "test" - serialized[:bumped].should == true + expect(serialized[:title]).to eq("test") + expect(serialized[:bumped]).to eq(true) end end diff --git a/spec/serializers/user_serializer_spec.rb b/spec/serializers/user_serializer_spec.rb index c26bc8217a..0f9acf7dcf 100644 --- a/spec/serializers/user_serializer_spec.rb +++ b/spec/serializers/user_serializer_spec.rb @@ -3,13 +3,25 @@ require_dependency 'user' describe UserSerializer do + context "with a TL0 user seen as anonymous" do + let(:user) { Fabricate.build(:user, trust_level: 0, user_profile: Fabricate.build(:user_profile)) } + let(:serializer) { UserSerializer.new(user, scope: Guardian.new, root: false) } + let(:json) { serializer.as_json } + + let(:untrusted_attributes) { %i{bio_raw bio_cooked bio_excerpt location website profile_background card_background} } + + it "doesn't serialize untrusted attributes" do + untrusted_attributes.each { |attr| expect(json).not_to have_key(attr) } + end + end + context "with a user" do let(:user) { Fabricate.build(:user, user_profile: Fabricate.build(:user_profile) ) } let(:serializer) { UserSerializer.new(user, scope: Guardian.new, root: false) } let(:json) { serializer.as_json } it "produces json" do - json.should be_present + expect(json).to be_present end context "with `enable_names` true" do @@ -18,7 +30,7 @@ describe UserSerializer do end it "has a name" do - json[:name].should be_present + expect(json[:name]).to be_present end end @@ -28,7 +40,7 @@ describe UserSerializer do end it "has a name" do - json[:name].should be_blank + expect(json[:name]).to be_blank end end @@ -90,13 +102,13 @@ describe UserSerializer do it "doesn't serialize the fields by default" do json[:custom_fields] - json[:custom_fields].should be_empty + expect(json[:custom_fields]).to be_empty end it "serializes the fields listed in public_user_custom_fields site setting" do SiteSetting.stubs(:public_user_custom_fields).returns('public_field') - json[:custom_fields]['public_field'].should == user.custom_fields['public_field'] - json[:custom_fields]['secret_field'].should == nil + expect(json[:custom_fields]['public_field']).to eq(user.custom_fields['public_field']) + expect(json[:custom_fields]['secret_field']).to eq(nil) end end end diff --git a/spec/services/staff_action_logger_spec.rb b/spec/services/staff_action_logger_spec.rb index 937e595449..d5fcb126d2 100644 --- a/spec/services/staff_action_logger_spec.rb +++ b/spec/services/staff_action_logger_spec.rb @@ -33,6 +33,20 @@ describe StaffActionLogger do end end + describe "log_show_emails" do + it "logs the user history" do + -> { logger.log_show_emails([admin]) }.should change(UserHistory, :count).by(1) + end + + it "doesn't raise an exception with nothing to log" do + -> { logger.log_show_emails([]) }.should_not raise_error + end + + it "doesn't raise an exception with nil input" do + -> { logger.log_show_emails(nil) }.should_not raise_error + end + end + describe 'log_post_deletion' do let(:deleted_post) { Fabricate(:post) } @@ -220,4 +234,34 @@ describe StaffActionLogger do log_record.details.should == badge.name end end + + describe 'log_roll_up' do + let(:subnets) { ["1.2.3.0/24", "42.42.42.0/24"] } + subject(:log_roll_up) { described_class.new(admin).log_roll_up(subnets) } + + it 'creates a new UserHistory record' do + log_record = logger.log_roll_up(subnets) + log_record.should be_valid + log_record.details.should == subnets.join(", ") + end + end + + describe 'log_custom' do + it "raises an error when `custom_type` is missing" do + expect { logger.log_custom(nil) }.to raise_error(Discourse::InvalidParameters) + end + + it "creates the UserHistory record" do + logged = logger.log_custom('clicked_something', { + evil: 'trout', + clicked_on: 'thing', + topic_id: 1234 + }) + logged.should be_valid + logged.details.should == "evil: trout\nclicked_on: thing" + logged.action.should == UserHistory.actions[:custom_staff] + logged.custom_type.should == 'clicked_something' + logged.topic_id.should === 1234 + end + end end diff --git a/spec/services/user_destroyer_spec.rb b/spec/services/user_destroyer_spec.rb index efb50f0bd2..b7b685a558 100644 --- a/spec/services/user_destroyer_spec.rb +++ b/spec/services/user_destroyer_spec.rb @@ -283,6 +283,17 @@ describe UserDestroyer do end end + context 'user got an email' do + let(:user) { Fabricate(:user) } + let!(:email_log) { Fabricate(:email_log, user: user) } + + it "deletes the email log" do + expect { + UserDestroyer.new(@admin).destroy(user, {delete_posts: true}) + }.to change { EmailLog.count }.by(-1) + end + end + context 'user liked things' do before do @topic = Fabricate(:topic, user: Fabricate(:user)) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 19093ec815..c58755c01c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -35,8 +35,8 @@ Spork.prefork do # let's not run seed_fu every test SeedFu.quiet = true if SeedFu.respond_to? :quiet - SiteSetting.enable_system_avatars = false SiteSetting.automatically_download_gravatars = false + SeedFu.seed RSpec.configure do |config| @@ -90,7 +90,6 @@ Spork.prefork do end # very expensive IO operations - SiteSetting.enable_system_avatars = false SiteSetting.automatically_download_gravatars = false I18n.locale = :en @@ -111,8 +110,11 @@ Spork.prefork do end def freeze_time(now=Time.now) - DateTime.stubs(:now).returns(DateTime.parse(now.to_s)) - Time.stubs(:now).returns(Time.parse(now.to_s)) + datetime = DateTime.parse(now.to_s) + time = Time.parse(now.to_s) + + DateTime.stubs(:now).returns(datetime) + Time.stubs(:now).returns(time) end def file_from_fixtures(filename) diff --git a/spec/support/time_matcher.rb b/spec/support/time_matcher.rb index 6fe445a623..53d97e5f5c 100644 --- a/spec/support/time_matcher.rb +++ b/spec/support/time_matcher.rb @@ -5,4 +5,4 @@ RSpec::Matchers.define :be_within_one_second_of do |expected_time| failure_message_for_should do |actual_time| "#{actual_time} is not within 1 second of #{expected_time}" end -end \ No newline at end of file +end diff --git a/spec/views/omniauth_callbacks/complete.html.erb_spec.rb b/spec/views/omniauth_callbacks/complete.html.erb_spec.rb index f5c4da7343..c9bd3d52ee 100644 --- a/spec/views/omniauth_callbacks/complete.html.erb_spec.rb +++ b/spec/views/omniauth_callbacks/complete.html.erb_spec.rb @@ -17,9 +17,9 @@ describe "users/omniauth_callbacks/complete.html.erb" do render - rendered_data["authenticated"].should eq(false) - rendered_data["awaiting_activation"].should eq(false) - rendered_data["awaiting_approval"].should eq(false) + expect(rendered_data["authenticated"]).to eq(false) + expect(rendered_data["awaiting_activation"]).to eq(false) + expect(rendered_data["awaiting_approval"]).to eq(false) end it "renders cas data " do @@ -32,10 +32,10 @@ describe "users/omniauth_callbacks/complete.html.erb" do render - rendered_data["email"].should eq(result.email) + expect(rendered_data["email"]).to eq(result.email) # TODO this is a bit weird, the upcasing is confusing, # clean it up throughout - rendered_data["auth_provider"].should eq("Cas") + expect(rendered_data["auth_provider"]).to eq("Cas") end end diff --git a/spec/views/omniauth_callbacks/failure.html.erb_spec.rb b/spec/views/omniauth_callbacks/failure.html.erb_spec.rb index 7237399b17..98520a6cb0 100644 --- a/spec/views/omniauth_callbacks/failure.html.erb_spec.rb +++ b/spec/views/omniauth_callbacks/failure.html.erb_spec.rb @@ -6,7 +6,7 @@ describe "users/omniauth_callbacks/failure.html.erb" do flash[:error] = I18n.t("login.omniauth_error", strategy: 'test') render - rendered.match(I18n.t("login.omniauth_error", strategy: 'test')).should_not == nil + expect(rendered.match(I18n.t("login.omniauth_error", strategy: 'test'))).not_to eq(nil) end end diff --git a/test/javascripts/admin/controllers/admin-site-settings-test.js.es6 b/test/javascripts/admin/controllers/admin-site-settings-test.js.es6 index a454bfa996..033ca019a8 100644 --- a/test/javascripts/admin/controllers/admin-site-settings-test.js.es6 +++ b/test/javascripts/admin/controllers/admin-site-settings-test.js.es6 @@ -1,42 +1,31 @@ moduleFor("controller:admin-site-settings"); test("filter", function() { - var allSettings = [Ember.Object.create({ - nameKey: 'users', name: 'users', - siteSettings: [Discourse.SiteSetting.create({"setting":"username_change_period","description":"x","default":3,"type":"fixnum","value":"3","category":"users"})] - }), Ember.Object.create({ - nameKey: 'posting', name: 'posting', - siteSettings: [Discourse.SiteSetting.create({"setting":"display_name_on_posts","description":"x","default":false,"type":"bool","value":"true","category":"posting"})] - })]; - var adminSiteSettingsController = this.subject({ model: allSettings }); - adminSiteSettingsController.set('allSiteSettings', allSettings); + var allSettings = [ + Ember.Object.create({ + nameKey: 'users', name: 'users', + siteSettings: [Discourse.SiteSetting.create({"setting":"username_change_period","description":"x","default":3,"type":"fixnum","value":"3","category":"users"})] + }), + Ember.Object.create({ + nameKey: 'posting', name: 'posting', + siteSettings: [Discourse.SiteSetting.create({"setting":"display_name_on_posts","description":"x","default":false,"type":"bool","value":"true","category":"posting"})] + }) + ]; + var adminSiteSettingsController = this.subject({ model: allSettings }); + sinon.stub(adminSiteSettingsController, "transitionToRoute"); + + adminSiteSettingsController.set('allSiteSettings', allSettings); equal(adminSiteSettingsController.get('content')[0].nameKey, 'users', "Can get first site setting category's name key."); adminSiteSettingsController.set('filter', 'username_change'); + equal(adminSiteSettingsController.get('content').length, 1, "Filter with one match for username_change"); + equal(adminSiteSettingsController.get('content')[0].nameKey, "all_results", "First element is all the results that match"); + equal(adminSiteSettingsController.get('content')[0].siteSettings[0].setting, "username_change_period", "Filter with one match for username_change"); - equal(adminSiteSettingsController.get('content').length, 2, "a. Filter with one match for username_change"); - equal(adminSiteSettingsController.get('content')[0].nameKey, "all_results", "b. First element is all the results that match"); - equal(adminSiteSettingsController.get('content')[1].nameKey, "users", "c. Filter with one match for username_change"); - equal(adminSiteSettingsController.get('content')[1].siteSettings[0].setting, "username_change_period", "d. Filter with one match for username_change"); - - adminSiteSettingsController.set('filter', 'name_on'); - equal(adminSiteSettingsController.get('content').length, 2, "a. Filter with one match for name_on"); - equal(adminSiteSettingsController.get('content')[1].nameKey, "posting", "b. Filter with one match for name_on"); - equal(adminSiteSettingsController.get('content')[1].siteSettings[0].setting, "display_name_on_posts", "c. Filter with one match for name_on"); - - adminSiteSettingsController.set('filter', 'name'); - equal(adminSiteSettingsController.get('content').length, 3, "a. Filter with one match for name"); - equal(adminSiteSettingsController.get('content')[0].nameKey, "all_results", "b. First element is all the results that match"); - equal(adminSiteSettingsController.get('content')[1].nameKey, "users", "c. Filter with one match for name"); - equal(adminSiteSettingsController.get('content')[2].nameKey, "posting", "d. Filter with one match for name"); - equal(adminSiteSettingsController.get('content')[1].siteSettings[0].setting, "username_change_period", "e. Filter with one match for name"); - equal(adminSiteSettingsController.get('content')[2].siteSettings[0].setting, "display_name_on_posts", "f. Filter with one match for name"); - - adminSiteSettingsController.set('filter', ''); - adminSiteSettingsController.set('onlyOverridden', true); - equal(adminSiteSettingsController.get('content').length, 2, "a. onlyOverridden with one match"); - equal(adminSiteSettingsController.get('content')[1].nameKey, "posting", "b. onlyOverridden with one match"); - equal(adminSiteSettingsController.get('content')[1].siteSettings[0].setting, "display_name_on_posts", "c. onlyOverridden with one match"); + adminSiteSettingsController.setProperties({ filter: '', onlyOverridden: true }); + equal(adminSiteSettingsController.get('content').length, 1, "onlyOverridden with one match"); + equal(adminSiteSettingsController.get('content')[0].nameKey, "all_results", "onlyOverridden with one match"); + equal(adminSiteSettingsController.get('content')[0].siteSettings[0].setting, "display_name_on_posts", "onlyOverridden with one match"); }); diff --git a/test/javascripts/admin/controllers/admin-user-badges-test.js.es6 b/test/javascripts/admin/controllers/admin-user-badges-test.js.es6 new file mode 100644 index 0000000000..db4637ce79 --- /dev/null +++ b/test/javascripts/admin/controllers/admin-user-badges-test.js.es6 @@ -0,0 +1,16 @@ +moduleFor('controller:admin-user-badges', 'Admin User Badges Controller', { + needs: ['controller:adminUser'] +}); + +test("grantableBadges", function() { + var badge_first = Discourse.Badge.create({id: 3, name: "A Badge"}); + var badge_middle = Discourse.Badge.create({id: 1, name: "My Badge"}); + var badge_last = Discourse.Badge.create({id: 2, name: "Zoo Badge"}); + var controller = this.subject({ badges: [badge_last, badge_first, badge_middle] }); + var sorted_names = [badge_first.name, badge_middle.name, badge_last.name]; + var badge_names = controller.get('grantableBadges').map(function(badge) { + return badge.name; + }); + + deepEqual(badge_names, sorted_names, "sorts badges by name"); +}); diff --git a/test/javascripts/controllers/site-map-test.js.es6 b/test/javascripts/controllers/site-map-test.js.es6 index 7e3c017dcd..324a8b88cf 100644 --- a/test/javascripts/controllers/site-map-test.js.es6 +++ b/test/javascripts/controllers/site-map-test.js.es6 @@ -1,6 +1,8 @@ var oldMobileView; moduleFor("controller:site-map", "controller:site-map", { + needs: ['controller:application'], + setup: function() { oldMobileView = Discourse.Mobile.mobileView; }, @@ -77,7 +79,7 @@ test("categories", function() { var categoryListStub = ["category1", "category2"]; sandbox.stub(Discourse.Category, "list").returns(categoryListStub); - var controller = this.subject(); + var controller = this.subject({ siteSettings: Discourse.SiteSettings }); deepEqual(controller.get("categories"), categoryListStub, "returns the list of categories"); }); diff --git a/test/javascripts/controllers/topic-test.js.es6 b/test/javascripts/controllers/topic-test.js.es6 index cf853c0d3a..295583db62 100644 --- a/test/javascripts/controllers/topic-test.js.es6 +++ b/test/javascripts/controllers/topic-test.js.es6 @@ -3,8 +3,10 @@ moduleFor('controller:topic', 'controller:topic', { 'controller:search', 'controller:topic-progress', 'controller:application'] }); +import Topic from 'discourse/models/topic'; + var buildTopic = function() { - return Discourse.Topic.create({ + return Topic.create({ title: "Qunit Test Topic", participants: [ {id: 1234, @@ -28,8 +30,8 @@ test("editingMode", function() { topicController.set('model.details.can_edit', true); topicController.send('editTopic'); ok(topicController.get('editingTopic'), "calling editTopic enables editing if the user can edit"); - equal(topicController.get('newTitle'), topic.get('title')); - equal(topicController.get('newCategoryId'), topic.get('category_id')); + equal(topicController.get('buffered.title'), topic.get('title')); + equal(topicController.get('buffered.category_id'), topic.get('category_id')); topicController.send('cancelEditingTopic'); ok(!topicController.get('editingTopic'), "cancelling edit mode reverts the property value"); diff --git a/test/javascripts/ember/resolver-test.js.es6 b/test/javascripts/ember/resolver-test.js.es6 index 4ca9ca5c47..14ed970949 100644 --- a/test/javascripts/ember/resolver-test.js.es6 +++ b/test/javascripts/ember/resolver-test.js.es6 @@ -4,7 +4,8 @@ var originalTemplates, originalMobileViewFlag; var resolver = DiscourseResolver.create(); function lookupTemplate(name, expectedTemplate, message) { - var result = resolver.resolveTemplate(resolver.parseName(name)); + var parseName = resolver.parseName(name); + var result = resolver.resolveTemplate(parseName); equal(result, expectedTemplate, message); } diff --git a/test/javascripts/fixtures/group-fixtures.js.es6 b/test/javascripts/fixtures/group-fixtures.js.es6 new file mode 100644 index 0000000000..03382b9a74 --- /dev/null +++ b/test/javascripts/fixtures/group-fixtures.js.es6 @@ -0,0 +1,6 @@ +export default { + "/groups/discourse.json": {"basic_group":{"id":47,"automatic":false,"name":"discourse","user_count":8,"alias_level":0,"visible":true}}, + "/groups/discourse/counts.json": {"counts":{"posts":17829,"members":7}}, + "/groups/discourse/members.json": {"members":[{"id":2770,"username":"awesomerobot","uploaded_avatar_id":33872,"avatar_template":"/user_avatar/meta.discourse.org/awesomerobot/{size}/33872.png","name":"","last_seen_at":"2015-01-23T15:53:17.844Z"},{"id":32,"username":"codinghorror","uploaded_avatar_id":5297,"avatar_template":"/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png","name":"Jeff Atwood","last_seen_at":"2015-01-23T06:05:25.457Z"},{"id":19,"username":"eviltrout","uploaded_avatar_id":5275,"avatar_template":"/user_avatar/meta.discourse.org/eviltrout/{size}/5275.png","name":"Robin Ward","last_seen_at":"2015-01-23T16:03:45.098Z"},{"id":2,"username":"neil","uploaded_avatar_id":5245,"avatar_template":"/user_avatar/meta.discourse.org/neil/{size}/5245.png","name":"Neil Lalonde","last_seen_at":"2015-01-23T15:22:10.244Z"},{"id":1,"username":"sam","uploaded_avatar_id":5243,"avatar_template":"/user_avatar/meta.discourse.org/sam/{size}/5243.png","name":"Sam Saffron","last_seen_at":"2015-01-23T11:07:06.233Z"},{"id":3,"username":"supermathie","uploaded_avatar_id":34097,"avatar_template":"/user_avatar/meta.discourse.org/supermathie/{size}/34097.png","name":"Michael Brown","last_seen_at":"2015-01-22T05:16:42.254Z"},{"id":1995,"username":"zogstrip","uploaded_avatar_id":8630,"avatar_template":"/user_avatar/meta.discourse.org/zogstrip/{size}/8630.png","name":"Régis Hanol","last_seen_at":"2015-01-23T15:45:34.196Z"}],"meta":{"total":7,"limit":50,"offset":0}}, + "/groups/discourse/posts.json": [{"id":94607,"cooked":"

Right now we have two entirely different styles for new topics and new posts within a topic... we can probably fix that pretty easily.

\n\n

\n\n

So the simple change would be:

\n\n

\n\n

but... while the dot makes the \"• new\" stand out more... it doesn't communicate any information other than \"look at me\" — can we add more context without adding more noise?

\n\n

","created_at":"2015-01-23T15:13:01.935Z","title":"Consistent new indicator","url":"/t/consistent-new-indicator/24355/1","user_title":"designerator","user_long_name":"","category":{"id":9,"name":"ux","color":"5F497A","topic_id":2628,"topic_count":540,"created_at":"2013-02-10T03:52:21.322Z","updated_at":"2015-01-22T18:05:32.152Z","user_id":32,"topics_year":370,"topics_month":33,"topics_week":3,"slug":"ux","description":"Discussion about the user interface of Discourse, how features are presented to the user in the client, including language and UI elements.","text_color":"FFFFFF","read_restricted":false,"auto_close_hours":null,"post_count":5823,"latest_post_id":94610,"latest_topic_id":24355,"position":25,"parent_category_id":null,"posts_year":4264,"posts_month":609,"posts_week":103,"email_in":null,"email_in_allow_strangers":false,"topics_day":0,"posts_day":28,"logo_url":null,"background_url":null,"allow_badges":true,"name_lower":"ux","auto_close_based_on_last_post":false},"user":{"id":2770,"username":"awesomerobot","uploaded_avatar_id":33872,"avatar_template":"/user_avatar/meta.discourse.org/awesomerobot/{size}/33872.png"}},{"id":94603,"cooked":"

Agree that the markup isn't ideal - it's kind of hacked together at the moment; especially because we have two different styles. I think once we settle on the specifics it can be re-written entirely.

","created_at":"2015-01-23T14:59:21.941Z","title":"The end of Clown Vomit, or, simplified category styles","url":"/t/the-end-of-clown-vomit-or-simplified-category-styles/24249/63","user_title":"designerator","user_long_name":"","category":{"id":9,"name":"ux","color":"5F497A","topic_id":2628,"topic_count":540,"created_at":"2013-02-10T03:52:21.322Z","updated_at":"2015-01-22T18:05:32.152Z","user_id":32,"topics_year":370,"topics_month":33,"topics_week":3,"slug":"ux","description":"Discussion about the user interface of Discourse, how features are presented to the user in the client, including language and UI elements.","text_color":"FFFFFF","read_restricted":false,"auto_close_hours":null,"post_count":5823,"latest_post_id":94610,"latest_topic_id":24355,"position":25,"parent_category_id":null,"posts_year":4264,"posts_month":609,"posts_week":103,"email_in":null,"email_in_allow_strangers":false,"topics_day":0,"posts_day":28,"logo_url":null,"background_url":null,"allow_badges":true,"name_lower":"ux","auto_close_based_on_last_post":false},"user":{"id":2770,"username":"awesomerobot","uploaded_avatar_id":33872,"avatar_template":"/user_avatar/meta.discourse.org/awesomerobot/{size}/33872.png"}},{"id":94601,"cooked":"

Yeah I think this category arrangement is the way to go at the very least - much easier to scan two columns...

\n\n

Also, maybe square off the bars?

\n\n

\n\n

","created_at":"2015-01-23T14:51:55.497Z","title":"The end of Clown Vomit, or, simplified category styles","url":"/t/the-end-of-clown-vomit-or-simplified-category-styles/24249/62","user_title":"designerator","user_long_name":"","category":{"id":9,"name":"ux","color":"5F497A","topic_id":2628,"topic_count":540,"created_at":"2013-02-10T03:52:21.322Z","updated_at":"2015-01-22T18:05:32.152Z","user_id":32,"topics_year":370,"topics_month":33,"topics_week":3,"slug":"ux","description":"Discussion about the user interface of Discourse, how features are presented to the user in the client, including language and UI elements.","text_color":"FFFFFF","read_restricted":false,"auto_close_hours":null,"post_count":5823,"latest_post_id":94610,"latest_topic_id":24355,"position":25,"parent_category_id":null,"posts_year":4264,"posts_month":609,"posts_week":103,"email_in":null,"email_in_allow_strangers":false,"topics_day":0,"posts_day":28,"logo_url":null,"background_url":null,"allow_badges":true,"name_lower":"ux","auto_close_based_on_last_post":false},"user":{"id":2770,"username":"awesomerobot","uploaded_avatar_id":33872,"avatar_template":"/user_avatar/meta.discourse.org/awesomerobot/{size}/33872.png"}},{"id":94577,"cooked":"

Yup, that's the latest version \"wink\"

\n\n

\n\n

(click to view animated version)

","created_at":"2015-01-23T10:50:55.846Z","title":"Quote reply insertion at cursor position","url":"/t/quote-reply-insertion-at-cursor-position/24344/4","user_title":"team","user_long_name":"Régis Hanol","category":{"id":2,"name":"feature","color":"0E76BD","topic_id":11,"topic_count":1592,"created_at":"2013-02-02T21:42:52.552Z","updated_at":"2015-01-22T18:05:32.647Z","user_id":1,"topics_year":919,"topics_month":60,"topics_week":20,"slug":"feature","description":"Discussion about features or potential features of Discourse: how they work, why they work, etc.","text_color":"FFFFFF","read_restricted":false,"auto_close_hours":null,"post_count":14360,"latest_post_id":94600,"latest_topic_id":24344,"position":25,"parent_category_id":null,"posts_year":8617,"posts_month":690,"posts_week":190,"email_in":null,"email_in_allow_strangers":false,"topics_day":2,"posts_day":8,"logo_url":null,"background_url":null,"allow_badges":true,"name_lower":"feature","auto_close_based_on_last_post":false},"user":{"id":1995,"username":"zogstrip","uploaded_avatar_id":8630,"avatar_template":"/user_avatar/meta.discourse.org/zogstrip/{size}/8630.png"}},{"id":94574,"cooked":"\n\n

It used to be that but that was fixed a while ago. Are you running a recent version?

","created_at":"2015-01-23T10:31:29.222Z","title":"Quote reply insertion at cursor position","url":"/t/quote-reply-insertion-at-cursor-position/24344/2","user_title":"team","user_long_name":"Régis Hanol","category":{"id":2,"name":"feature","color":"0E76BD","topic_id":11,"topic_count":1592,"created_at":"2013-02-02T21:42:52.552Z","updated_at":"2015-01-22T18:05:32.647Z","user_id":1,"topics_year":919,"topics_month":60,"topics_week":20,"slug":"feature","description":"Discussion about features or potential features of Discourse: how they work, why they work, etc.","text_color":"FFFFFF","read_restricted":false,"auto_close_hours":null,"post_count":14360,"latest_post_id":94600,"latest_topic_id":24344,"position":25,"parent_category_id":null,"posts_year":8617,"posts_month":690,"posts_week":190,"email_in":null,"email_in_allow_strangers":false,"topics_day":2,"posts_day":8,"logo_url":null,"background_url":null,"allow_badges":true,"name_lower":"feature","auto_close_based_on_last_post":false},"user":{"id":1995,"username":"zogstrip","uploaded_avatar_id":8630,"avatar_template":"/user_avatar/meta.discourse.org/zogstrip/{size}/8630.png"}},{"id":94572,"cooked":"\n\n

That's an Ember update that introduced this change.

","created_at":"2015-01-23T09:46:00.901Z","title":"Translations frequently broken","url":"/t/translations-frequently-broken/22546/27","user_title":"team","user_long_name":"Régis Hanol","category":{"id":27,"name":"translations","color":"808281","topic_id":14549,"topic_count":146,"created_at":"2014-04-07T20:30:17.623Z","updated_at":"2015-01-22T18:05:33.111Z","user_id":2,"topics_year":134,"topics_month":5,"topics_week":3,"slug":"translations","description":"This category is for discussion about localizing Discourse.","text_color":"FFFFFF","read_restricted":false,"auto_close_hours":null,"post_count":1167,"latest_post_id":94575,"latest_topic_id":24301,"position":25,"parent_category_id":7,"posts_year":965,"posts_month":60,"posts_week":29,"email_in":null,"email_in_allow_strangers":false,"topics_day":1,"posts_day":5,"logo_url":null,"background_url":null,"allow_badges":true,"name_lower":"translations","auto_close_based_on_last_post":false},"user":{"id":1995,"username":"zogstrip","uploaded_avatar_id":8630,"avatar_template":"/user_avatar/meta.discourse.org/zogstrip/{size}/8630.png"}},{"id":94555,"cooked":"

I don't know how to pronounce that in English, but this makes me think of the French word \"disquette\" (floppy disk) \"smile\"

","created_at":"2015-01-23T08:17:31.700Z","title":"Introducing Discette - a minimal ember-cli front end to Discourse","url":"/t/introducing-discette-a-minimal-ember-cli-front-end-to-discourse/24321/3","user_title":"team","user_long_name":"Régis Hanol","category":{"id":7,"name":"dev","color":"000","topic_id":1026,"topic_count":574,"created_at":"2013-02-06T08:43:41.550Z","updated_at":"2015-01-22T18:05:32.855Z","user_id":32,"topics_year":298,"topics_month":29,"topics_week":2,"slug":"dev","description":"This category is for topics related to hacking on Discourse: submitting pull requests, configuring development environments, coding conventions, and so forth.","text_color":"FFFFFF","read_restricted":false,"auto_close_hours":null,"post_count":4196,"latest_post_id":94590,"latest_topic_id":24349,"position":25,"parent_category_id":null,"posts_year":2095,"posts_month":172,"posts_week":16,"email_in":null,"email_in_allow_strangers":false,"topics_day":0,"posts_day":3,"logo_url":null,"background_url":null,"allow_badges":true,"name_lower":"dev","auto_close_based_on_last_post":false},"user":{"id":1995,"username":"zogstrip","uploaded_avatar_id":8630,"avatar_template":"/user_avatar/meta.discourse.org/zogstrip/{size}/8630.png"}},{"id":94544,"cooked":"

@techapj fixed this for 1.2.

","created_at":"2015-01-23T05:49:35.881Z","title":"After sign-in, I'm not redirected to the conversation","url":"/t/after-sign-in-im-not-redirected-to-the-conversation/17753/8","user_title":"co-founder","user_long_name":"Jeff Atwood","category":{"id":9,"name":"ux","color":"5F497A","topic_id":2628,"topic_count":540,"created_at":"2013-02-10T03:52:21.322Z","updated_at":"2015-01-22T18:05:32.152Z","user_id":32,"topics_year":370,"topics_month":33,"topics_week":3,"slug":"ux","description":"Discussion about the user interface of Discourse, how features are presented to the user in the client, including language and UI elements.","text_color":"FFFFFF","read_restricted":false,"auto_close_hours":null,"post_count":5823,"latest_post_id":94610,"latest_topic_id":24355,"position":25,"parent_category_id":null,"posts_year":4264,"posts_month":609,"posts_week":103,"email_in":null,"email_in_allow_strangers":false,"topics_day":0,"posts_day":28,"logo_url":null,"background_url":null,"allow_badges":true,"name_lower":"ux","auto_close_based_on_last_post":false},"user":{"id":32,"username":"codinghorror","uploaded_avatar_id":5297,"avatar_template":"/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png"}},{"id":94543,"cooked":"

Oh yes IOS 8.2 -- well, let's see what happens because there is really no fix on our end. Basic HTML / CSS stuff is broken.

","created_at":"2015-01-23T05:45:40.306Z","title":"Dealing with iOS 8 Mobile Safari bugs?","url":"/t/dealing-with-ios-8-mobile-safari-bugs/24101/7","user_title":"co-founder","user_long_name":"Jeff Atwood","category":{"id":2,"name":"feature","color":"0E76BD","topic_id":11,"topic_count":1592,"created_at":"2013-02-02T21:42:52.552Z","updated_at":"2015-01-22T18:05:32.647Z","user_id":1,"topics_year":919,"topics_month":60,"topics_week":20,"slug":"feature","description":"Discussion about features or potential features of Discourse: how they work, why they work, etc.","text_color":"FFFFFF","read_restricted":false,"auto_close_hours":null,"post_count":14360,"latest_post_id":94600,"latest_topic_id":24344,"position":25,"parent_category_id":null,"posts_year":8617,"posts_month":690,"posts_week":190,"email_in":null,"email_in_allow_strangers":false,"topics_day":2,"posts_day":8,"logo_url":null,"background_url":null,"allow_badges":true,"name_lower":"feature","auto_close_based_on_last_post":false},"user":{"id":32,"username":"codinghorror","uploaded_avatar_id":5297,"avatar_template":"/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png"}},{"id":94542,"cooked":"

Hmm that looks like a bug, @techapj can you have a look?

","created_at":"2015-01-23T05:43:55.602Z","title":"RSS is not valid","url":"/t/rss-is-not-valid/24338/2","user_title":"co-founder","user_long_name":"Jeff Atwood","category":{"id":6,"name":"support","color":"CEA9A9","topic_id":389,"topic_count":1781,"created_at":"2013-02-05T22:16:38.672Z","updated_at":"2015-01-22T18:05:33.572Z","user_id":1,"topics_year":1541,"topics_month":167,"topics_week":49,"slug":"support","description":"Support on configuring and using Discourse after it is up and running. For installation questions, use the install category.","text_color":"FFFFFF","read_restricted":false,"auto_close_hours":null,"post_count":12272,"latest_post_id":94602,"latest_topic_id":24346,"position":25,"parent_category_id":null,"posts_year":10571,"posts_month":1254,"posts_week":413,"email_in":null,"email_in_allow_strangers":false,"topics_day":5,"posts_day":70,"logo_url":"","background_url":"","allow_badges":true,"name_lower":"support","auto_close_based_on_last_post":false},"user":{"id":32,"username":"codinghorror","uploaded_avatar_id":5297,"avatar_template":"/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png"}},{"id":94522,"cooked":"

Oh I see. @zogstrip can you have a look?

","created_at":"2015-01-23T03:00:20.485Z","title":"Pasted image upload size error","url":"/t/pasted-image-upload-size-error/24320/4","user_title":"co-founder","user_long_name":"Jeff Atwood","category":{"id":1,"name":"bug","color":"e9dd00","topic_id":2,"topic_count":1729,"created_at":"2013-02-01T04:56:34.914Z","updated_at":"2015-01-22T18:05:33.426Z","user_id":1,"topics_year":1114,"topics_month":69,"topics_week":22,"slug":"bug","description":"A bug report means something is broken, preventing normal/typical use of Discourse. Do be sure to search prior to submitting bugs. Include repro steps, and only describe one bug per topic please.","text_color":"000000","read_restricted":false,"auto_close_hours":null,"post_count":11179,"latest_post_id":94611,"latest_topic_id":24350,"position":25,"parent_category_id":null,"posts_year":7138,"posts_month":397,"posts_week":121,"email_in":null,"email_in_allow_strangers":false,"topics_day":1,"posts_day":6,"logo_url":null,"background_url":null,"allow_badges":true,"name_lower":"bug","auto_close_based_on_last_post":false},"user":{"id":32,"username":"codinghorror","uploaded_avatar_id":5297,"avatar_template":"/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png"}},{"id":94521,"cooked":"\n\n

Yeah probably.

\n\n\n\n

Definitely a good idea. We have seen some eye melting color schemes people have picked for categories.. Much less subcategories.

\n\n\n\n

Sure try http://talk.folksy.com -- it's still too much color in boxes. Particularly anywhere a bunch of categories are displayed together, which is a lot of places considering the topic list is the main form of nav, both on the homepage default of latest and in suggested topics at the bottom of every topic...

\n\n

","created_at":"2015-01-23T02:58:27.451Z","title":"The end of Clown Vomit, or, simplified category styles","url":"/t/the-end-of-clown-vomit-or-simplified-category-styles/24249/57","user_title":"co-founder","user_long_name":"Jeff Atwood","category":{"id":9,"name":"ux","color":"5F497A","topic_id":2628,"topic_count":540,"created_at":"2013-02-10T03:52:21.322Z","updated_at":"2015-01-22T18:05:32.152Z","user_id":32,"topics_year":370,"topics_month":33,"topics_week":3,"slug":"ux","description":"Discussion about the user interface of Discourse, how features are presented to the user in the client, including language and UI elements.","text_color":"FFFFFF","read_restricted":false,"auto_close_hours":null,"post_count":5823,"latest_post_id":94610,"latest_topic_id":24355,"position":25,"parent_category_id":null,"posts_year":4264,"posts_month":609,"posts_week":103,"email_in":null,"email_in_allow_strangers":false,"topics_day":0,"posts_day":28,"logo_url":null,"background_url":null,"allow_badges":true,"name_lower":"ux","auto_close_based_on_last_post":false},"user":{"id":32,"username":"codinghorror","uploaded_avatar_id":5297,"avatar_template":"/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png"}},{"id":94519,"cooked":"

What would you suggest writing here that would be more clear?

","created_at":"2015-01-23T02:45:36.859Z","title":"What is \"Born mobile, born to touch\" supposed to tell me?","url":"/t/what-is-born-mobile-born-to-touch-supposed-to-tell-me/24329/3","user_title":"co-founder","user_long_name":"Jeff Atwood","category":{"id":3,"name":"meta","color":"aaa","topic_id":24,"topic_count":139,"created_at":"2013-02-03T00:00:15.230Z","updated_at":"2015-01-22T18:05:32.797Z","user_id":1,"topics_year":68,"topics_month":5,"topics_week":1,"slug":"meta","description":"Discussion about meta.discourse.org itself, the organization of this forum about Discourse, how it works, and how we can improve this site.","text_color":"FFFFFF","read_restricted":false,"auto_close_hours":null,"post_count":1116,"latest_post_id":94559,"latest_topic_id":24208,"position":25,"parent_category_id":null,"posts_year":553,"posts_month":33,"posts_week":8,"email_in":null,"email_in_allow_strangers":false,"topics_day":0,"posts_day":0,"logo_url":null,"background_url":null,"allow_badges":true,"name_lower":"meta","auto_close_based_on_last_post":false},"user":{"id":32,"username":"codinghorror","uploaded_avatar_id":5297,"avatar_template":"/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png"}},{"id":94518,"cooked":"

You should generally create topics to host things like this, then make them wiki, close them, etc.

","created_at":"2015-01-23T02:42:20.053Z","title":"How to Create Static Pages in Discourse?","url":"/t/how-to-create-static-pages-in-discourse/24313/2","user_title":"co-founder","user_long_name":"Jeff Atwood","category":{"id":6,"name":"support","color":"CEA9A9","topic_id":389,"topic_count":1781,"created_at":"2013-02-05T22:16:38.672Z","updated_at":"2015-01-22T18:05:33.572Z","user_id":1,"topics_year":1541,"topics_month":167,"topics_week":49,"slug":"support","description":"Support on configuring and using Discourse after it is up and running. For installation questions, use the install category.","text_color":"FFFFFF","read_restricted":false,"auto_close_hours":null,"post_count":12272,"latest_post_id":94602,"latest_topic_id":24346,"position":25,"parent_category_id":null,"posts_year":10571,"posts_month":1254,"posts_week":413,"email_in":null,"email_in_allow_strangers":false,"topics_day":5,"posts_day":70,"logo_url":"","background_url":"","allow_badges":true,"name_lower":"support","auto_close_based_on_last_post":false},"user":{"id":32,"username":"codinghorror","uploaded_avatar_id":5297,"avatar_template":"/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png"}},{"id":94517,"cooked":"

Doubtful this is a bug, probably dependent on the PNG encoding.

\n\n

Try using PNGOUT, or converting to 8 bit PNGOUT, to see some of the differences. And PNGOUT is lossless!

","created_at":"2015-01-23T02:41:30.287Z","title":"Pasted image upload size error","url":"/t/pasted-image-upload-size-error/24320/2","user_title":"co-founder","user_long_name":"Jeff Atwood","category":{"id":1,"name":"bug","color":"e9dd00","topic_id":2,"topic_count":1729,"created_at":"2013-02-01T04:56:34.914Z","updated_at":"2015-01-22T18:05:33.426Z","user_id":1,"topics_year":1114,"topics_month":69,"topics_week":22,"slug":"bug","description":"A bug report means something is broken, preventing normal/typical use of Discourse. Do be sure to search prior to submitting bugs. Include repro steps, and only describe one bug per topic please.","text_color":"000000","read_restricted":false,"auto_close_hours":null,"post_count":11179,"latest_post_id":94611,"latest_topic_id":24350,"position":25,"parent_category_id":null,"posts_year":7138,"posts_month":397,"posts_week":121,"email_in":null,"email_in_allow_strangers":false,"topics_day":1,"posts_day":6,"logo_url":null,"background_url":null,"allow_badges":true,"name_lower":"bug","auto_close_based_on_last_post":false},"user":{"id":32,"username":"codinghorror","uploaded_avatar_id":5297,"avatar_template":"/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png"}},{"id":94516,"cooked":"

I would worry about getting your expenses down to $5 per month, that seems more likely over time as hosting for Docker compliant sites gets cheaper.

","created_at":"2015-01-23T02:40:11.726Z","title":"Monetizing Discourse Talk","url":"/t/monetizing-discourse-talk/24316/4","user_title":"co-founder","user_long_name":"Jeff Atwood","category":{"id":6,"name":"support","color":"CEA9A9","topic_id":389,"topic_count":1781,"created_at":"2013-02-05T22:16:38.672Z","updated_at":"2015-01-22T18:05:33.572Z","user_id":1,"topics_year":1541,"topics_month":167,"topics_week":49,"slug":"support","description":"Support on configuring and using Discourse after it is up and running. For installation questions, use the install category.","text_color":"FFFFFF","read_restricted":false,"auto_close_hours":null,"post_count":12272,"latest_post_id":94602,"latest_topic_id":24346,"position":25,"parent_category_id":null,"posts_year":10571,"posts_month":1254,"posts_week":413,"email_in":null,"email_in_allow_strangers":false,"topics_day":5,"posts_day":70,"logo_url":"","background_url":"","allow_badges":true,"name_lower":"support","auto_close_based_on_last_post":false},"user":{"id":32,"username":"codinghorror","uploaded_avatar_id":5297,"avatar_template":"/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png"}},{"id":94515,"cooked":"

Liked just for the word \"Discettes\" which is adorable \"heart_eyes\"

","created_at":"2015-01-23T02:38:29.185Z","title":"Introducing Discette - a minimal ember-cli front end to Discourse","url":"/t/introducing-discette-a-minimal-ember-cli-front-end-to-discourse/24321/2","user_title":"co-founder","user_long_name":"Jeff Atwood","category":{"id":7,"name":"dev","color":"000","topic_id":1026,"topic_count":574,"created_at":"2013-02-06T08:43:41.550Z","updated_at":"2015-01-22T18:05:32.855Z","user_id":32,"topics_year":298,"topics_month":29,"topics_week":2,"slug":"dev","description":"This category is for topics related to hacking on Discourse: submitting pull requests, configuring development environments, coding conventions, and so forth.","text_color":"FFFFFF","read_restricted":false,"auto_close_hours":null,"post_count":4196,"latest_post_id":94590,"latest_topic_id":24349,"position":25,"parent_category_id":null,"posts_year":2095,"posts_month":172,"posts_week":16,"email_in":null,"email_in_allow_strangers":false,"topics_day":0,"posts_day":3,"logo_url":null,"background_url":null,"allow_badges":true,"name_lower":"dev","auto_close_based_on_last_post":false},"user":{"id":32,"username":"codinghorror","uploaded_avatar_id":5297,"avatar_template":"/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png"}},{"id":94514,"cooked":"\n\n

This is a good idea, are the documents public web URLs? Perhaps we could help build this onebox if so.

\n\n\n\n

Hmm. I suspect this could be done via the API. Query all new topics (assuming older topics are already synced), and for those with a certain URL within the topic (first post only? All posts?) ping those URLs.

\n\n

This could potentially be done with a webhook on save on the Discourse side.

\n\n

Let us know how we can help, very interested in public projects like this.

","created_at":"2015-01-23T02:37:39.518Z","title":"How to do \"Object Oriented Discussion\" through Oneboxes?","url":"/t/how-to-do-object-oriented-discussion-through-oneboxes/24328/2","user_title":"co-founder","user_long_name":"Jeff Atwood","category":{"id":5,"name":"extensibility","color":"FE8432","topic_id":28,"topic_count":295,"created_at":"2013-02-03T08:42:06.329Z","updated_at":"2015-01-22T18:05:32.698Z","user_id":1,"topics_year":187,"topics_month":17,"topics_week":7,"slug":"extensibility","description":"Topics about extending the functionality of Discourse with plugins, themes, add-ons, or other mechanisms for extensibility. ","text_color":"FFFFFF","read_restricted":false,"auto_close_hours":null,"post_count":2574,"latest_post_id":94582,"latest_topic_id":24328,"position":25,"parent_category_id":null,"posts_year":1485,"posts_month":196,"posts_week":52,"email_in":null,"email_in_allow_strangers":false,"topics_day":2,"posts_day":8,"logo_url":null,"background_url":null,"allow_badges":true,"name_lower":"extensibility","auto_close_based_on_last_post":false},"user":{"id":32,"username":"codinghorror","uploaded_avatar_id":5297,"avatar_template":"/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png"}},{"id":94512,"cooked":"

Hmm, have not seen problems updating on 1gb instance provided swap is there.

\n\n

Anything else running on the machine?

\n\n

Maybe reboot, then upgrade Docker from command line, then upgrade Discourse from command line.

","created_at":"2015-01-23T02:32:31.383Z","title":"Update Failed and Now Showing Currently Upgrading","url":"/t/update-failed-and-now-showing-currently-upgrading/24332/2","user_title":"co-founder","user_long_name":"Jeff Atwood","category":{"id":6,"name":"support","color":"CEA9A9","topic_id":389,"topic_count":1781,"created_at":"2013-02-05T22:16:38.672Z","updated_at":"2015-01-22T18:05:33.572Z","user_id":1,"topics_year":1541,"topics_month":167,"topics_week":49,"slug":"support","description":"Support on configuring and using Discourse after it is up and running. For installation questions, use the install category.","text_color":"FFFFFF","read_restricted":false,"auto_close_hours":null,"post_count":12272,"latest_post_id":94602,"latest_topic_id":24346,"position":25,"parent_category_id":null,"posts_year":10571,"posts_month":1254,"posts_week":413,"email_in":null,"email_in_allow_strangers":false,"topics_day":5,"posts_day":70,"logo_url":"","background_url":"","allow_badges":true,"name_lower":"support","auto_close_based_on_last_post":false},"user":{"id":32,"username":"codinghorror","uploaded_avatar_id":5297,"avatar_template":"/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png"}},{"id":94511,"cooked":"

Hmm, not sure about that, good odds they will be fixed in iOS 8.1 which is due soon.

","created_at":"2015-01-23T02:27:16.786Z","title":"Dealing with iOS 8 Mobile Safari bugs?","url":"/t/dealing-with-ios-8-mobile-safari-bugs/24101/5","user_title":"co-founder","user_long_name":"Jeff Atwood","category":{"id":2,"name":"feature","color":"0E76BD","topic_id":11,"topic_count":1592,"created_at":"2013-02-02T21:42:52.552Z","updated_at":"2015-01-22T18:05:32.647Z","user_id":1,"topics_year":919,"topics_month":60,"topics_week":20,"slug":"feature","description":"Discussion about features or potential features of Discourse: how they work, why they work, etc.","text_color":"FFFFFF","read_restricted":false,"auto_close_hours":null,"post_count":14360,"latest_post_id":94600,"latest_topic_id":24344,"position":25,"parent_category_id":null,"posts_year":8617,"posts_month":690,"posts_week":190,"email_in":null,"email_in_allow_strangers":false,"topics_day":2,"posts_day":8,"logo_url":null,"background_url":null,"allow_badges":true,"name_lower":"feature","auto_close_based_on_last_post":false},"user":{"id":32,"username":"codinghorror","uploaded_avatar_id":5297,"avatar_template":"/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png"}}] +}; diff --git a/test/javascripts/helpers/create-pretender.js.es6 b/test/javascripts/helpers/create-pretender.js.es6 index a3716d6295..7cc2ff8864 100644 --- a/test/javascripts/helpers/create-pretender.js.es6 +++ b/test/javascripts/helpers/create-pretender.js.es6 @@ -16,7 +16,7 @@ function response(code, obj) { } function success() { - return response(); + return response({ success: true }); } export default function() { diff --git a/test/javascripts/helpers/qunit-helpers.js.es6 b/test/javascripts/helpers/qunit-helpers.js.es6 index 3aa4b2fcc0..111180cc52 100644 --- a/test/javascripts/helpers/qunit-helpers.js.es6 +++ b/test/javascripts/helpers/qunit-helpers.js.es6 @@ -2,7 +2,7 @@ import siteFixtures from 'fixtures/site_fixtures'; -export function integration(name, options) { +function integration(name, options) { module("Integration: " + name, { setup: function() { Ember.run(Discourse, Discourse.advanceReadiness); @@ -39,13 +39,13 @@ export function integration(name, options) { }); } -export function controllerFor(controller, model) { +function controllerFor(controller, model) { controller = Discourse.__container__.lookup('controller:' + controller); if (model) { controller.set('model', model ); } return controller; } -export function asyncTestDiscourse(text, func) { +function asyncTestDiscourse(text, func) { asyncTest(text, function () { var self = this; Ember.run(function () { @@ -54,9 +54,11 @@ export function asyncTestDiscourse(text, func) { }); } -export function fixture(selector) { +function fixture(selector) { if (selector) { return $("#qunit-fixture").find(selector); } return $("#qunit-fixture"); } + +export { integration, controllerFor, asyncTestDiscourse, fixture }; diff --git a/test/javascripts/helpers/site-settings.js b/test/javascripts/helpers/site-settings.js index 5985a9461c..f1266061d8 100644 --- a/test/javascripts/helpers/site-settings.js +++ b/test/javascripts/helpers/site-settings.js @@ -1,3 +1,3 @@ /*jshint maxlen:10000000 */ -Discourse.SiteSettingsOriginal = {"title":"Discourse Meta","logo_url":"/assets/logo.png","logo_small_url":"/assets/logo-single.png","mobile_logo_url":"","favicon_url":"//meta.discourse.org/uploads/default/2499/79d53726406d87af.ico","allow_user_locale":false,"suggested_topics":7,"track_external_right_clicks":false,"ga_universal_tracking_code":"","ga_universal_domain_name":"auto","ga_tracking_code":"UA-33736483-2","ga_domain_name":"","top_menu":"latest|new|unread|starred|categories|top","post_menu":"like|share|flag|edit|bookmark|delete|admin|reply","post_menu_hidden_items":"edit|delete|admin","share_links":"twitter|facebook|google+|email","category_colors":"BF1E2E|F1592A|F7941D|9EB83B|3AB54A|12A89D|25AAE2|0E76BD|652D90|92278F|ED207B|8C6238|231F20|808281|B3B5B4|283890","enable_mobile_theme":true,"relative_date_duration":14,"category_featured_topics":4,"fixed_category_positions":false,"show_subcategory_list":false,"posts_chunksize":20,"enable_badges":true,"invite_only":false,"login_required":false,"must_approve_users":false,"enable_local_logins":true,"allow_new_registrations":true,"enable_google_logins":true,"enable_google_oauth2_logins":false,"enable_yahoo_logins":true,"enable_twitter_logins":true,"enable_facebook_logins":true,"enable_github_logins":true,"enable_sso":false,"min_username_length":3,"max_username_length":20,"min_password_length":8,"enable_names":true,"invites_shown":30,"delete_user_max_post_age":60,"delete_all_posts_max":15,"min_post_length":20,"min_private_message_post_length":10,"max_post_length":32000,"min_topic_title_length":15,"max_topic_title_length":255,"min_private_message_title_length":2,"allow_uncategorized_topics":true,"min_title_similar_length":10,"min_body_similar_length":15,"edit_history_visible_to_public":true,"delete_removed_posts_after":24,"traditional_markdown_linebreaks":false,"suppress_reply_directly_below":true,"suppress_reply_directly_above":true,"newuser_max_images":0,"newuser_max_attachments":0,"display_name_on_posts":true,"short_progress_text_threshold":10000,"default_code_lang":"lang-auto","autohighlight_all_code":false,"email_in":false,"max_image_size_kb":3072,"max_attachment_size_kb":1024,"authorized_extensions":".jpg|.jpeg|.png|.gif|.svg|.txt|.ico|.yml","max_image_width":690,"max_image_height":500,"allow_profile_backgrounds":true,"allow_uploaded_avatars":true,"allow_animated_avatars":false,"tl1_requires_read_posts":30,"enable_long_polling":true,"polling_interval":3000,"anon_polling_interval":30000,"flush_timings_secs":5,"tos_url":"","privacy_policy_url":"","tos_accept_required":false,"faq_url":"","allow_restore":false,"maximum_backups":7,"version_checks":true,"suppress_uncategorized_badge":true,"min_search_term_length":3,"topic_views_heat_low":1000,"topic_views_heat_medium":2000,"topic_views_heat_high":5000,"global_notice":"","show_create_topics_notice":true,"available_locales":"cs|da|de|en|es|fr|he|id|it|ja|ko|nb_NO|nl|pl_PL|pt|pt_BR|ru|sv|uk|zh_CN|zh_TW"}; +Discourse.SiteSettingsOriginal = {"title":"Discourse Meta","logo_url":"/assets/logo.png","logo_small_url":"/assets/logo-single.png","mobile_logo_url":"","favicon_url":"//meta.discourse.org/uploads/default/2499/79d53726406d87af.ico","allow_user_locale":false,"suggested_topics":7,"track_external_right_clicks":false,"ga_universal_tracking_code":"","ga_universal_domain_name":"auto","ga_tracking_code":"UA-33736483-2","ga_domain_name":"","top_menu":"latest|new|unread|categories|top","post_menu":"like|share|flag|edit|bookmark|delete|admin|reply","post_menu_hidden_items":"edit|delete|admin","share_links":"twitter|facebook|google+|email","category_colors":"BF1E2E|F1592A|F7941D|9EB83B|3AB54A|12A89D|25AAE2|0E76BD|652D90|92278F|ED207B|8C6238|231F20|808281|B3B5B4|283890","enable_mobile_theme":true,"relative_date_duration":14,"category_featured_topics":4,"fixed_category_positions":false,"show_subcategory_list":false,"enable_badges":true,"invite_only":false,"login_required":false,"must_approve_users":false,"enable_local_logins":true,"allow_new_registrations":true,"enable_google_logins":true,"enable_google_oauth2_logins":false,"enable_yahoo_logins":true,"enable_twitter_logins":true,"enable_facebook_logins":true,"enable_github_logins":true,"enable_sso":false,"min_username_length":3,"max_username_length":20,"min_password_length":8,"enable_names":true,"invites_shown":30,"delete_user_max_post_age":60,"delete_all_posts_max":15,"min_post_length":20,"min_private_message_post_length":10,"max_post_length":32000,"min_topic_title_length":15,"max_topic_title_length":255,"min_private_message_title_length":2,"allow_uncategorized_topics":true,"min_title_similar_length":10,"min_body_similar_length":15,"edit_history_visible_to_public":true,"delete_removed_posts_after":24,"traditional_markdown_linebreaks":false,"suppress_reply_directly_below":true,"suppress_reply_directly_above":true,"newuser_max_images":0,"newuser_max_attachments":0,"display_name_on_posts":true,"short_progress_text_threshold":10000,"default_code_lang":"lang-auto","autohighlight_all_code":false,"email_in":false,"max_image_size_kb":3072,"max_attachment_size_kb":1024,"authorized_extensions":".jpg|.jpeg|.png|.gif|.svg|.txt|.ico|.yml","max_image_width":690,"max_image_height":500,"allow_profile_backgrounds":true,"allow_uploaded_avatars":true,"allow_animated_avatars":false,"tl1_requires_read_posts":30,"enable_long_polling":true,"polling_interval":3000,"anon_polling_interval":30000,"flush_timings_secs":5,"tos_url":"","privacy_policy_url":"","tos_accept_required":false,"faq_url":"","allow_restore":false,"maximum_backups":7,"version_checks":true,"suppress_uncategorized_badge":true,"min_search_term_length":3,"topic_views_heat_low":1000,"topic_views_heat_medium":2000,"topic_views_heat_high":5000,"global_notice":"","show_create_topics_notice":true,"available_locales":"cs|da|de|en|es|fr|he|id|it|ja|ko|nb_NO|nl|pl_PL|pt|pt_BR|ru|sv|uk|zh_CN|zh_TW"}; Discourse.SiteSettings = jQuery.extend(true, {}, Discourse.SiteSettingsOriginal); diff --git a/test/javascripts/integration/groups-test.js.es6 b/test/javascripts/integration/groups-test.js.es6 new file mode 100644 index 0000000000..3d52deb95c --- /dev/null +++ b/test/javascripts/integration/groups-test.js.es6 @@ -0,0 +1,12 @@ +integration("Groups"); + +test("Browsing Groups", function() { + visit("/groups/discourse"); + andThen(function() { + ok(count('.user-stream .item') > 0, "it has stream items"); + }); + visit("/groups/discourse/members"); + andThen(function() { + ok(count('.group-members tr') > 0, "it lists group members"); + }); +}); diff --git a/test/javascripts/integration/topic-discovery-test.js.es6 b/test/javascripts/integration/topic-discovery-test.js.es6 index c5931f6335..ea4dbac42c 100644 --- a/test/javascripts/integration/topic-discovery-test.js.es6 +++ b/test/javascripts/integration/topic-discovery-test.js.es6 @@ -11,15 +11,20 @@ test("Visit Discovery Pages", function() { andThen(function() { ok(exists(".topic-list"), "The list of topics was rendered"); ok(exists('.topic-list .topic-list-item'), "has topics"); + ok($('body.category-bug').length, "has a custom css class for the category id on the body"); }); visit("/categories"); andThen(function() { + ok($('body.category-bug').length === 0, "removes the custom category class"); + ok(exists('.category'), "has a list of categories"); + ok($('body.categories-list').length, "has a custom class to indicate categories"); }); visit("/top"); andThen(function() { + ok($('body.categories-list').length === 0, "removes the `categories-list` class"); ok(exists('.topic-list .topic-list-item'), "has topics"); }); }); diff --git a/test/javascripts/jshint-test.js.es6.erb b/test/javascripts/jshint-test.js.es6.erb index f966c43c28..cccd67f15c 100644 --- a/test/javascripts/jshint-test.js.es6.erb +++ b/test/javascripts/jshint-test.js.es6.erb @@ -1,38 +1,17 @@ module("JSHint"); -var qHint = function(name, sourceFile, options, globals) { - if (sourceFile === undefined || typeof(sourceFile) == "object") { - // jsHintTest('file.js', [options]) - globals = options; - options = sourceFile; - sourceFile = name; - } +<%= "const JSHINT_OPTS = #{File.read(File.join(Rails.root, '.jshintrc'))};" %> +var qHint = function(name, sourceFile) { return asyncTestDiscourse(name, function() { if (typeof window.__jshintSrc !== "undefined") { var src = window.__jshintSrc[sourceFile]; if (src) { start(); - qHint.validateFile(src, options, globals); + qHint.validateFile(src, JSHINT_OPTS); return; } } - - - console.warn("Using AJAX for JSHint " + sourceFile); - - qHint.sendRequest(sourceFile, function(req) { - start(); - - if (req.status == 200) { - - var text = req.responseText; - qHint.validateFile(text, options, globals); - } else { - ok(false, "HTTP error " + req.status + - " while fetching " + sourceFile); - } - }); }); }; @@ -100,6 +79,7 @@ qHint.sendRequest = function (url, callback) { req.send(); }; + <% TO_IGNORE = File.read("#{Rails.root}/.jshintignore").split("\n") @@ -115,7 +95,7 @@ qHint.sendRequest = function (url, callback) { end depend_on filename - result << "qHint('#{filename}', '/assets/#{filename}', #{File.read(File.join(Rails.root, '.jshintrc'))});\n" if ok + result << "qHint('#{filename}', '/assets/#{filename}', JSHINT_OPTS);\n" if ok end result @@ -124,5 +104,5 @@ qHint.sendRequest = function (url, callback) { <%= jshint("#{Rails.root}/test/**/*.js", "test/javascripts/") %> <%= jshint("#{Rails.root}/app/assets/javascripts/**/*.js", "app/assets/javascripts/") %> - +<%= jshint("#{Rails.root}/app/assets/javascripts/**/*.es6", "app/assets/javascripts/") %> diff --git a/test/javascripts/lib/category-badge-test.js.es6 b/test/javascripts/lib/category-badge-test.js.es6 new file mode 100644 index 0000000000..9dbe2f3f75 --- /dev/null +++ b/test/javascripts/lib/category-badge-test.js.es6 @@ -0,0 +1,41 @@ +module("categoryBadgeHTML"); + +import { categoryBadgeHTML } from "discourse/helpers/category-link"; + +test("categoryBadge without a category", function() { + blank(categoryBadgeHTML(), "it returns no HTML"); +}); + +test("Regular categoryBadge", function() { + var category = Discourse.Category.create({ + name: 'hello', + id: 123, + description_text: 'cool description', + color: 'ff0', + text_color: 'f00' + }), + tag = parseHTML(categoryBadgeHTML(category))[0]; + + equal(tag.name, 'a', 'it creates a `a` wrapper tag'); + equal(tag.attributes['class'].trim(), 'badge-wrapper', 'it has the correct class'); + + var label = tag.children[1]; + equal(label.attributes.title, 'cool description', 'it has the correct title'); + + equal(label.children[0].data, 'hello', 'it has the category name'); +}); + +test("undefined color", function() { + var noColor = Discourse.Category.create({ name: 'hello', id: 123 }), + tag = parseHTML(categoryBadgeHTML(noColor))[0]; + + blank(tag.attributes.style, "it has no color style because there are no colors"); +}); + +test("allowUncategorized", function() { + var uncategorized = Discourse.Category.create({name: 'uncategorized', id: 345}); + sandbox.stub(Discourse.Site, 'currentProp').withArgs('uncategorized_category_id').returns(345); + + blank(categoryBadgeHTML(uncategorized), "it doesn't return HTML for uncategorized by default"); + present(categoryBadgeHTML(uncategorized, {allowUncategorized: true}), "it returns HTML"); +}); diff --git a/test/javascripts/lib/click-track-test.js.es6 b/test/javascripts/lib/click-track-test.js.es6 index 82f7348bd2..dff00e9368 100644 --- a/test/javascripts/lib/click-track-test.js.es6 +++ b/test/javascripts/lib/click-track-test.js.es6 @@ -71,6 +71,16 @@ test("removes the href and put it as a data attribute", function() { ok(Discourse.URL.redirectTo.calledOnce); }); +asyncTest("restores the href after a while", function() { + expect(1); + + track(generateClickEventOn('a')); + + setTimeout(function() { + start(); + equal(fixture('a').attr('href'), "http://www.google.com"); + }, 75); +}); var badgeClickCount = function(id, expected) { track(generateClickEventOn('#' + id)); diff --git a/test/javascripts/lib/html-test.js.es6 b/test/javascripts/lib/html-test.js.es6 index 57a4328f37..07ef2906a1 100644 --- a/test/javascripts/lib/html-test.js.es6 +++ b/test/javascripts/lib/html-test.js.es6 @@ -2,46 +2,6 @@ module("Discourse.HTML"); var html = Discourse.HTML; -test("categoryBadge without a category", function() { - blank(html.categoryBadge(), "it returns no HTML"); -}); - -test("Regular categoryBadge", function() { - var category = Discourse.Category.create({ - name: 'hello', - id: 123, - description_text: 'cool description', - color: 'ff0', - text_color: 'f00' - }), - tag = parseHTML(html.categoryBadge(category))[0]; - - equal(tag.name, 'a', 'it creates an `a` tag'); - equal(tag.attributes['class'], 'badge-category', 'it has the correct class'); - equal(tag.attributes.title, 'cool description', 'it has the correct title'); - - ok(tag.attributes.style.indexOf('#ff0') !== -1, "it has the color style"); - ok(tag.attributes.style.indexOf('#f00') !== -1, "it has the textColor style"); - - equal(tag.children[0].data, 'hello', 'it has the category name'); -}); - -test("undefined color", function() { - var noColor = Discourse.Category.create({ name: 'hello', id: 123 }), - tag = parseHTML(html.categoryBadge(noColor))[0]; - - blank(tag.attributes.style, "it has no color style because there are no colors"); -}); - -test("allowUncategorized", function() { - var uncategorized = Discourse.Category.create({name: 'uncategorized', id: 345}); - sandbox.stub(Discourse.Site, 'currentProp').withArgs('uncategorized_category_id').returns(345); - - blank(html.categoryBadge(uncategorized), "it doesn't return HTML for uncategorized by default"); - present(html.categoryBadge(uncategorized, {allowUncategorized: true}), "it returns HTML"); -}); - - test("customHTML", function() { blank(html.getCustomHTML('evil'), "there is no custom HTML for a key by default"); diff --git a/test/javascripts/lib/markdown-test.js.es6 b/test/javascripts/lib/markdown-test.js.es6 index fcd2ee7b83..7c6cf4e408 100644 --- a/test/javascripts/lib/markdown-test.js.es6 +++ b/test/javascripts/lib/markdown-test.js.es6 @@ -150,6 +150,10 @@ test("Links", function() { cooked("[Link](http://www.example.com) (with an outer \"description\")", "

Link (with an outer \"description\")

", "it doesn't consume closing parens as part of the url"); + + cooked("[ul][1]\n\n[1]: http://eviltrout.com", + "

ul

", + "it can use `ul` as a link name"); }); test("simple quotes", function() { @@ -398,6 +402,8 @@ test("Code Blocks", function() { cooked(" [quote]test[/quote]", "
[quote]test[/quote]
", "it does not parse other block types in markdown code blocks"); + + cooked("## a\nb\n```\nc\n```", "

a

\n\n

c

", "it handles headings with code blocks after them."); }); test("sanitize", function() { diff --git a/test/javascripts/lib/utilities-test.js.es6 b/test/javascripts/lib/utilities-test.js.es6 index 0052767c4b..15ee484673 100644 --- a/test/javascripts/lib/utilities-test.js.es6 +++ b/test/javascripts/lib/utilities-test.js.es6 @@ -60,14 +60,16 @@ test("prevents files that are too big from being uploaded", function() { ok(bootbox.alert.calledWith(I18n.t('post.errors.file_too_large', { max_size_kb: 5 }))); }); +var imageSize = 10 * 1024; + var dummyBlob = function() { var BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder; if (BlobBuilder) { var bb = new BlobBuilder(); - bb.append([1]); + bb.append([new Int8Array(imageSize)]); return bb.getBlob("image/png"); } else { - return new Blob([1], { "type" : "image\/png" }); + return new Blob([new Int8Array(imageSize)], { "type" : "image\/png" }); } }; @@ -75,10 +77,11 @@ test("allows valid uploads to go through", function() { Discourse.User.resetCurrent(Discourse.User.create()); Discourse.User.currentProp("trust_level", 1); Discourse.SiteSettings.max_image_size_kb = 15; + Discourse.SiteSettings.max_attachment_size_kb = 1; sandbox.stub(bootbox, "alert"); // image - var image = { name: "image.png", size: 10 * 1024 }; + var image = { name: "image.png", size: imageSize }; ok(validUpload([image])); // pasted image var pastedImage = dummyBlob(); diff --git a/test/javascripts/mixins/selected-posts-count-test.js.es6 b/test/javascripts/mixins/selected-posts-count-test.js.es6 index d8ecc084e4..830d9033ee 100644 --- a/test/javascripts/mixins/selected-posts-count-test.js.es6 +++ b/test/javascripts/mixins/selected-posts-count-test.js.es6 @@ -1,5 +1,7 @@ module("Discourse.SelectedPostsCount"); +import Topic from 'discourse/models/topic'; + var buildTestObj = function(params) { return Ember.Object.createWithMixins(Discourse.SelectedPostsCount, params || {}); }; @@ -26,7 +28,7 @@ test("when all posts are selected and there is a posts_count", function() { test("when all posts are selected and there is topic with a posts_count", function() { var testObj = buildTestObj({ allPostsSelected: true, - topic: Discourse.Topic.create({ posts_count: 3456 }) + topic: Topic.create({ posts_count: 3456 }) }); equal(testObj.get('selectedPostsCount'), 3456, "It returns the topic's posts_count"); diff --git a/test/javascripts/models/composer-test.js.es6 b/test/javascripts/models/composer-test.js.es6 index afcb156cd2..cf359e9976 100644 --- a/test/javascripts/models/composer-test.js.es6 +++ b/test/javascripts/models/composer-test.js.es6 @@ -129,19 +129,6 @@ test("Title length for private messages", function() { ok(composer.get('titleLengthValid'), "in the range is okay"); }); -test('importQuote with no data', function() { - sandbox.stub(Discourse.Post, 'load'); - var composer = Discourse.Composer.create(); - composer.importQuote(); - blank(composer.get('reply'), 'importing with no topic adds nothing'); - ok(!Discourse.Post.load.calledOnce, "load is not called"); - - composer = Discourse.Composer.create({topic: Discourse.Topic.create()}); - composer.importQuote(); - blank(composer.get('reply'), 'importing with no posts in a topic adds nothing'); - ok(!Discourse.Post.load.calledOnce, "load is not called"); -}); - test('editingFirstPost', function() { var composer = Discourse.Composer.create(); ok(!composer.get('editingFirstPost'), "it's false by default"); @@ -155,36 +142,6 @@ test('editingFirstPost', function() { }); -asyncTestDiscourse('importQuote with a post', function() { - expect(1); - - sandbox.stub(Discourse.Post, 'load').withArgs(123).returns(new Ember.RSVP.Promise(function (resolve) { - resolve(Discourse.Post.create({raw: "let's quote"})); - })); - - var composer = Discourse.Composer.create({post: Discourse.Post.create({id: 123})}); - composer.importQuote().then(function () { - start(); - ok(composer.get('reply').indexOf("let's quote") > -1, "it quoted the post"); - }); -}); - -asyncTestDiscourse('importQuote with no post', function() { - expect(1); - - sandbox.stub(Discourse.Post, 'load').withArgs(4).returns(new Ember.RSVP.Promise(function (resolve) { - resolve(Discourse.Post.create({raw: 'quote me'})); - })); - - var composer = Discourse.Composer.create({topic: Discourse.Topic.create()}); - composer.set('topic.postStream.stream', [4, 5]); - composer.importQuote().then(function () { - start(); - ok(composer.get('reply').indexOf('quote me') > -1, "it contains the word quote me"); - }); - -}); - test('clearState', function() { var composer = Discourse.Composer.create({ originalText: 'asdf', diff --git a/test/javascripts/models/post-stream-test.js.es6 b/test/javascripts/models/post-stream-test.js.es6 index dcb06d1305..7162b49c96 100644 --- a/test/javascripts/models/post-stream-test.js.es6 +++ b/test/javascripts/models/post-stream-test.js.es6 @@ -1,7 +1,10 @@ -module("Discourse.PostStream"); +module("model:post-stream"); + +import PostStream from 'discourse/models/post-stream'; +import Topic from 'discourse/models/topic'; var buildStream = function(id, stream) { - var topic = Discourse.Topic.create({id: id}); + var topic = Topic.create({id: id, chunk_size: 5}); var ps = topic.get('postStream'); if (stream) { ps.set('stream', stream); @@ -12,7 +15,7 @@ var buildStream = function(id, stream) { var participant = {username: 'eviltrout'}; test('create', function() { - ok(Discourse.PostStream.create(), 'it can be created with no parameters'); + ok(PostStream.create(), 'it can be created with no parameters'); }); test('defaults', function() { @@ -20,7 +23,6 @@ test('defaults', function() { blank(postStream.get('posts'), "there are no posts in a stream by default"); ok(!postStream.get('loaded'), "it has never loaded"); present(postStream.get('topic')); - }); test('appending posts', function() { @@ -122,6 +124,17 @@ test("cancelFilter", function() { blank(postStream.get('userFilters'), "cancelling the filters clears the userFilters"); }); +test("findPostIdForPostNumber", function() { + var postStream = buildStream(1234, [10, 20, 30, 40, 50, 60, 70]); + postStream.set('gaps', { before: { 60: [55, 58] } }); + + equal(postStream.findPostIdForPostNumber(500), null, 'it returns null when the post cannot be found'); + equal(postStream.findPostIdForPostNumber(1), 10, 'it finds the postId at the beginning'); + equal(postStream.findPostIdForPostNumber(5), 50, 'it finds the postId in the middle'); + equal(postStream.findPostIdForPostNumber(8), 60, 'it respects gaps'); + +}); + test("toggleParticipant", function() { var postStream = buildStream(1236); sandbox.stub(postStream, "refresh"); @@ -182,7 +195,6 @@ test("loading", function() { }); test("nextWindow", function() { - Discourse.SiteSettings.posts_chunksize = 5; var postStream = buildStream(1234, [1,2,3,5,8,9,10,11,13,14,15,16]); blank(postStream.get('nextWindow'), 'With no posts loaded, the window is blank'); @@ -199,7 +211,6 @@ test("nextWindow", function() { }); test("previousWindow", function() { - Discourse.SiteSettings.posts_chunksize = 5; var postStream = buildStream(1234, [1,2,3,5,8,9,10,11,13,14,15,16]); blank(postStream.get('previousWindow'), 'With no posts loaded, the window is blank'); @@ -233,7 +244,7 @@ test("storePost", function() { var postWithoutId = Discourse.Post.create({raw: 'hello world'}); stored = postStream.storePost(postWithoutId); equal(stored, postWithoutId, "it returns the same post back"); - equal(postStream.get('postIdentityMap.length'), 1, "it does not add a new entry into the identity map"); + equal(postStream.get('postIdentityMap.size'), 1, "it does not add a new entry into the identity map"); }); diff --git a/test/javascripts/models/session-test.js.es6 b/test/javascripts/models/session-test.js.es6 index 84589a9c7f..d4d3ff5a55 100644 --- a/test/javascripts/models/session-test.js.es6 +++ b/test/javascripts/models/session-test.js.es6 @@ -1,6 +1,8 @@ +import Session from "discourse/models/session"; + module("Discourse.Session"); test('highestSeenByTopic', function() { - var session = Discourse.Session.current(); + var session = Session.current(); deepEqual(session.get('highestSeenByTopic'), {}, "by default it returns an empty object"); }); diff --git a/test/javascripts/models/topic-details-test.js.es6 b/test/javascripts/models/topic-details-test.js.es6 index 7ca709f400..7fb7fc2f25 100644 --- a/test/javascripts/models/topic-details-test.js.es6 +++ b/test/javascripts/models/topic-details-test.js.es6 @@ -1,7 +1,9 @@ -module("Discourse.TopicDetails"); +module("model:topic-details"); + +import Topic from 'discourse/models/topic'; var buildDetails = function(id) { - var topic = Discourse.Topic.create({id: id}); + var topic = Topic.create({id: id}); return topic.get('details'); }; @@ -20,13 +22,9 @@ test('updateFromJson', function() { }); equal(details.get('suggested_topics.length'), 2, 'it loaded the suggested_topics'); - containsInstance(details.get('suggested_topics'), Discourse.Topic); + containsInstance(details.get('suggested_topics'), Topic); equal(details.get('allowed_users.length'), 1, 'it loaded the allowed users'); containsInstance(details.get('allowed_users'), Discourse.User); }); - - - - diff --git a/test/javascripts/models/topic-test.js.es6 b/test/javascripts/models/topic-test.js.es6 index e557cf7c1b..3bd2230171 100644 --- a/test/javascripts/models/topic-test.js.es6 +++ b/test/javascripts/models/topic-test.js.es6 @@ -1,20 +1,22 @@ -module("Discourse.Topic"); +module("model:topic"); + +import Topic from 'discourse/models/topic'; test("defaults", function() { - var topic = Discourse.Topic.create({id: 1234}); + var topic = Topic.create({id: 1234}); blank(topic.get('deleted_at'), 'deleted_at defaults to blank'); blank(topic.get('deleted_by'), 'deleted_by defaults to blank'); }); test('has details', function() { - var topic = Discourse.Topic.create({id: 1234}); + var topic = Topic.create({id: 1234}); var topicDetails = topic.get('details'); present(topicDetails, "a topic has topicDetails after we create it"); equal(topicDetails.get('topic'), topic, "the topicDetails has a reference back to the topic"); }); test('has a postStream', function() { - var topic = Discourse.Topic.create({id: 1234}); + var topic = Topic.create({id: 1234}); var postStream = topic.get('postStream'); present(postStream, "a topic has a postStream after we create it"); equal(postStream.get('topic'), topic, "the postStream has a reference back to the topic"); @@ -24,13 +26,13 @@ test('has a postStream', function() { test('category relationship', function() { // It finds the category by id var category = Discourse.Category.list()[0], - topic = Discourse.Topic.create({id: 1111, category_id: category.get('id') }); + topic = Topic.create({id: 1111, category_id: category.get('id') }); equal(topic.get('category'), category); }); test("updateFromJson", function() { - var topic = Discourse.Topic.create({id: 1234}), + var topic = Topic.create({id: 1234}), category = Discourse.Category.list()[0]; topic.updateFromJson({ @@ -48,7 +50,7 @@ test("updateFromJson", function() { test("destroy", function() { var user = Discourse.User.create({username: 'eviltrout'}); - var topic = Discourse.Topic.create({id: 1234}); + var topic = Topic.create({id: 1234}); sandbox.stub(Discourse, 'ajax'); @@ -60,7 +62,7 @@ test("destroy", function() { test("recover", function() { var user = Discourse.User.create({username: 'eviltrout'}); - var topic = Discourse.Topic.create({id: 1234, deleted_at: new Date(), deleted_by: user}); + var topic = Topic.create({id: 1234, deleted_at: new Date(), deleted_by: user}); sandbox.stub(Discourse, 'ajax'); diff --git a/test/javascripts/models/user-stream-test.js.es6 b/test/javascripts/models/user-stream-test.js.es6 index 727938da40..135ee6f22e 100644 --- a/test/javascripts/models/user-stream-test.js.es6 +++ b/test/javascripts/models/user-stream-test.js.es6 @@ -25,6 +25,6 @@ test('filterParam', function() { equal(stream.get('filterParam'), Discourse.UserAction.TYPES.likes_given); stream.set('filter', Discourse.UserAction.TYPES.replies); - equal(stream.get('filterParam'), '6,7,9'); + equal(stream.get('filterParam'), '6,9'); }); diff --git a/test/javascripts/test_helper.js b/test/javascripts/test_helper.js index 863d76e452..a9bd9b6646 100644 --- a/test/javascripts/test_helper.js +++ b/test/javascripts/test_helper.js @@ -20,7 +20,6 @@ //= require pretender //= require ../../app/assets/javascripts/locales/i18n -//= require ../../app/assets/javascripts/discourse/helpers/i18n_helpers //= require ../../app/assets/javascripts/locales/en // Pagedown customizations diff --git a/test/javascripts/views/view-test.js.es6 b/test/javascripts/views/view-test.js.es6 index 1595c9bc7f..f9ea42dfe4 100644 --- a/test/javascripts/views/view-test.js.es6 +++ b/test/javascripts/views/view-test.js.es6 @@ -30,47 +30,3 @@ test("registerHelper: enables embedding a child view in a parent view via dedica equal(parentView.$("#child").length, 1, "child view registered as helper is appended to the parent view"); equal(parentView.$("#child").text(), "foo", "child view registered as helper gets parameters provided during helper invocation in parent's template"); }); - -test("renderIfChanged: rerenders the whole view template when one of registered view fields changes", function() { - var view, rerenderSpy; - - var viewRerendersOnceWhen = function(message, changeCallback) { - rerenderSpy.reset(); - Ember.run(function() { changeCallback(); }); - ok(rerenderSpy.calledOnce, "view rerenders when " + message); - }; - - var viewDoesNotRerenderWhen = function(message, changeCallback) { - rerenderSpy.reset(); - Ember.run(function() { changeCallback(); }); - ok(!rerenderSpy.called, "view does not rerender when " + message); - }; - - - view = Ember.View.extend({ - shouldRerender: Discourse.View.renderIfChanged("simple", "complex.@each.nested") - }).create({ - simple: "initial value", - complex: [Ember.Object.create({nested: "initial value"})], - unregistered: "initial value" - }); - - rerenderSpy = sinon.spy(view, "rerender"); - - Ember.run(function() { - view.appendTo("#qunit-fixture"); - }); - - - viewRerendersOnceWhen("a simple field (holding a string) changes", function() { - view.set("simple", "updated value"); - }); - - viewRerendersOnceWhen("a nested sub-field of a complex field (holding an array of objects) changes", function() { - view.get("complex").objectAt(0).set("nested", "updated value"); - }); - - viewDoesNotRerenderWhen("unregistered field changes", function() { - view.set("unregistered", "updated value"); - }); -}); diff --git a/vendor/assets/javascripts/better_markdown.js b/vendor/assets/javascripts/better_markdown.js index 77e30b3004..7a9e74b8c2 100644 --- a/vendor/assets/javascripts/better_markdown.js +++ b/vendor/assets/javascripts/better_markdown.js @@ -825,7 +825,6 @@ // TODO: Cache this regexp for certain depths. // Create a regexp suitable for matching an li for a given stack depth function regex_for_depth( depth ) { - return new RegExp( // m[1] = indent, m[2] = list_type "(?:^(" + indent_re + "{0," + depth + "} {0,3})(" + any_list + ")\\s+)|" + @@ -871,8 +870,11 @@ replace = new RegExp("^" + indent_re + "{" + depth + "}", "gm"), ret = []; + while ( blocks.length > 0 ) { - if ( re.exec( blocks[0] ) ) { + // HACK: Fixes a v8 issue + test = blocks[0].replace(/^ {8,}/, ' '); + if ( re.exec( test ) ) { var b = blocks.shift(), // Now remove that indent x = b.replace( replace, ""); @@ -1185,6 +1187,16 @@ res = this.dialect.inline[ m[1] ].call( this, text.substr( m.index ), m, previous_nodes || [] ); + + // If no inline code executed, fallback + if (!res) { + var fn = this.dialect.inline[m[1][0]]; + if (fn) { + res = fn.call( + this, + text.substr( m.index ), m, previous_nodes || [] ); + } + } } // Default for now to make dev easier. just slurp special and output it. res = res || [ m[1].length, m[1] ]; diff --git a/vendor/assets/javascripts/development/ember.js b/vendor/assets/javascripts/development/ember.js index 4e25fe0987..0b4deefa8d 100644 --- a/vendor/assets/javascripts/development/ember.js +++ b/vendor/assets/javascripts/development/ember.js @@ -5,102 +5,102 @@ * Portions Copyright 2008-2011 Apple Inc. All rights reserved. * @license Licensed under MIT license * See https://raw.github.com/emberjs/ember.js/master/LICENSE - * @version 1.7.1 + * @version 1.9.0 */ (function() { -var define, requireModule, require, requirejs, Ember; +var enifed, requireModule, eriuqer, requirejs, Ember; (function() { Ember = this.Ember = this.Ember || {}; - if (typeof Ember === 'undefined') { Ember = {} }; + if (typeof Ember === 'undefined') { Ember = {}; }; + function UNDEFINED() { } if (typeof Ember.__loader === 'undefined') { var registry = {}, seen = {}; - define = function(name, deps, callback) { + enifed = function(name, deps, callback) { registry[name] = { deps: deps, callback: callback }; }; - requirejs = require = requireModule = function(name) { - if (seen.hasOwnProperty(name)) { return seen[name]; } + requirejs = eriuqer = requireModule = function(name) { + var s = seen[name]; + + if (s !== undefined) { return seen[name]; } + if (s === UNDEFINED) { return undefined; } + seen[name] = {}; if (!registry[name]) { throw new Error("Could not find module " + name); } - var mod = registry[name], - deps = mod.deps, - callback = mod.callback, - reified = [], - exports; + var mod = registry[name]; + var deps = mod.deps; + var callback = mod.callback; + var reified = []; + var exports; + var length = deps.length; - for (var i=0, l=deps.length; i 3 ? slice.call(arguments, 3) : undefined; + var stack = this.DEBUG ? new Error() : undefined; + var length = arguments.length; + var args; + + if (length > 3) { + args = new Array(length - 3); + for (var i = 3; i < length; i++) { + args[i-3] = arguments[i]; + } + } else { + args = undefined; + } + if (!this.currentInstance) { createAutorun(this); } return this.currentInstance.schedule(queueName, target, method, args, false, stack); }, @@ -231,15 +255,34 @@ define("backburner", method = target[method]; } - var stack = this.DEBUG ? new Error() : undefined, - args = arguments.length > 3 ? slice.call(arguments, 3) : undefined; - if (!this.currentInstance) { createAutorun(this); } + var stack = this.DEBUG ? new Error() : undefined; + var length = arguments.length; + var args; + + if (length > 3) { + args = new Array(length - 3); + for (var i = 3; i < length; i++) { + args[i-3] = arguments[i]; + } + } else { + args = undefined; + } + + if (!this.currentInstance) { + createAutorun(this); + } return this.currentInstance.schedule(queueName, target, method, args, true, stack); }, setTimeout: function() { - var args = slice.call(arguments), - length = args.length, + var l = arguments.length; + var args = new Array(l); + + for (var x = 0; x < l; x++) { + args[x] = arguments[x]; + } + + var length = args.length, method, wait, target, methodOrTarget, methodOrWait, methodOrArgs; @@ -285,7 +328,7 @@ define("backburner", } } - var executeAt = (+new Date()) + parseInt(wait, 10); + var executeAt = now() + parseInt(wait, 10); if (isString(method)) { method = target[method]; @@ -306,9 +349,9 @@ define("backburner", } // find position to insert - var i = searchTimer(executeAt, timers); + var i = searchTimer(executeAt, this._timers); - timers.splice(i, 0, executeAt, fn); + this._timers.splice(i, 0, executeAt, fn); updateLaterTimer(this, executeAt, wait); @@ -316,13 +359,10 @@ define("backburner", }, throttle: function(target, method /* , args, wait, [immediate] */) { - var self = this, - args = arguments, - immediate = pop.call(args), - wait, - throttler, - index, - timer; + var backburner = this; + var args = arguments; + var immediate = pop.call(args); + var wait, throttler, index, timer; if (isNumber(immediate) || isString(immediate)) { wait = immediate; @@ -338,16 +378,16 @@ define("backburner", timer = global.setTimeout(function() { if (!immediate) { - self.run.apply(self, args); + backburner.run.apply(backburner, args); } - var index = findThrottler(target, method, self._throttlers); + var index = findThrottler(target, method, backburner._throttlers); if (index > -1) { - self._throttlers.splice(index, 1); + backburner._throttlers.splice(index, 1); } }, wait); if (immediate) { - self.run.apply(self, args); + this.run.apply(this, args); } throttler = [target, method, timer]; @@ -358,13 +398,10 @@ define("backburner", }, debounce: function(target, method /* , args, wait, [immediate] */) { - var self = this, - args = arguments, - immediate = pop.call(args), - wait, - index, - debouncee, - timer; + var backburner = this; + var args = arguments; + var immediate = pop.call(args); + var wait, index, debouncee, timer; if (isNumber(immediate) || isString(immediate)) { wait = immediate; @@ -385,21 +422,25 @@ define("backburner", timer = global.setTimeout(function() { if (!immediate) { - self.run.apply(self, args); + backburner.run.apply(backburner, args); } - var index = findDebouncee(target, method, self._debouncees); + var index = findDebouncee(target, method, backburner._debouncees); if (index > -1) { - self._debouncees.splice(index, 1); + backburner._debouncees.splice(index, 1); } }, wait); if (immediate && index === -1) { - self.run.apply(self, args); + backburner.run.apply(backburner, args); } - debouncee = [target, method, timer]; + debouncee = [ + target, + method, + timer + ]; - self._debouncees.push(debouncee); + backburner._debouncees.push(debouncee); return debouncee; }, @@ -419,7 +460,7 @@ define("backburner", clearTimeout(this._laterTimer); this._laterTimer = null; } - timers = []; + this._timers = []; if (this._autorun) { clearTimeout(this._autorun); @@ -428,7 +469,7 @@ define("backburner", }, hasTimers: function() { - return !!timers.length || !!this._debouncees.length || !!this._throttlers.length || this._autorun; + return !!this._timers.length || !!this._debouncees.length || !!this._throttlers.length || this._autorun; }, cancel: function(timer) { @@ -437,9 +478,18 @@ define("backburner", if (timer && timerType === 'object' && timer.queue && timer.method) { // we're cancelling a deferOnce return timer.queue.cancel(timer); } else if (timerType === 'function') { // we're cancelling a setTimeout - for (var i = 0, l = timers.length; i < l; i += 2) { - if (timers[i + 1] === timer) { - timers.splice(i, 2); // remove the two elements + for (var i = 0, l = this._timers.length; i < l; i += 2) { + if (this._timers[i + 1] === timer) { + this._timers.splice(i, 2); // remove the two elements + if (i === 0) { + if (this._laterTimer) { // Active timer? Then clear timer and reset for future timer + clearTimeout(this._laterTimer); + this._laterTimer = null; + } + if (this._timers.length > 0) { // Update to next available timer when available + updateLaterTimer(this, this._timers[0], this._timers[0] - now()); + } + } return true; } } @@ -452,18 +502,17 @@ define("backburner", }, _cancelItem: function(findMethod, array, timer){ - var item, - index; + var item, index; if (timer.length < 3) { return false; } index = findMethod(timer[0], timer[1], array); - if(index > -1) { + if (index > -1) { item = array[index]; - if(item[2] === timer[2]){ + if (item[2] === timer[2]) { array.splice(index, 1); clearTimeout(timer[2]); return true; @@ -486,21 +535,10 @@ define("backburner", Backburner.prototype.end = wrapInTryCatch(originalEnd); } - function wrapInTryCatch(func) { - return function () { - try { - return func.apply(this, arguments); - } catch (e) { - throw e; - } - }; - } - function getOnError(options) { return options.onError || (options.onErrorTarget && options.onErrorTarget[options.onErrorMethod]); } - function createAutorun(backburner) { backburner.begin(); backburner._autorun = global.setTimeout(function() { @@ -509,33 +547,48 @@ define("backburner", }); } - function updateLaterTimer(self, executeAt, wait) { - if (!self._laterTimer || executeAt < self._laterTimerExpiresAt) { - self._laterTimer = global.setTimeout(function() { - self._laterTimer = null; - self._laterTimerExpiresAt = null; - executeTimers(self); + function updateLaterTimer(backburner, executeAt, wait) { + var n = now(); + if (!backburner._laterTimer || executeAt < backburner._laterTimerExpiresAt || backburner._laterTimerExpiresAt < n) { + + if (backburner._laterTimer) { + // Clear when: + // - Already expired + // - New timer is earlier + clearTimeout(backburner._laterTimer); + + if (backburner._laterTimerExpiresAt < n) { // If timer was never triggered + // Calculate the left-over wait-time + wait = Math.max(0, executeAt - n); + } + } + + backburner._laterTimer = global.setTimeout(function() { + backburner._laterTimer = null; + backburner._laterTimerExpiresAt = null; + executeTimers(backburner); }, wait); - self._laterTimerExpiresAt = executeAt; + + backburner._laterTimerExpiresAt = n + wait; } } - function executeTimers(self) { - var now = +new Date(), - time, fns, i, l; + function executeTimers(backburner) { + var n = now(); + var fns, i, l; - self.run(function() { - i = searchTimer(now, timers); + backburner.run(function() { + i = searchTimer(n, backburner._timers); - fns = timers.splice(0, i); + fns = backburner._timers.splice(0, i); for (i = 1, l = fns.length; i < l; i += 2) { - self.schedule(self.options.defaultQueue, null, fns[i]); + backburner.schedule(backburner.options.defaultQueue, null, fns[i]); } }); - if (timers.length) { - updateLaterTimer(self, timers[0], timers[0] - now); + if (backburner._timers.length) { + updateLaterTimer(backburner, backburner._timers[0], backburner._timers[0] - n); } } @@ -548,8 +601,8 @@ define("backburner", } function findItem(target, method, collection) { - var item, - index = -1; + var item; + var index = -1; for (var i = 0, l = collection.length; i < l; i++) { item = collection[i]; @@ -562,10 +615,31 @@ define("backburner", return index; } - function searchTimer(time, timers) { - var start = 0, - end = timers.length - 2, - middle, l; + __exports__["default"] = Backburner; + }); +enifed("backburner.umd", + ["./backburner"], + function(__dependency1__) { + "use strict"; + var Backburner = __dependency1__["default"]; + + /* global define:true module:true window: true */ + if (typeof enifed === 'function' && enifed.amd) { + enifed(function() { return Backburner; }); + } else if (typeof module !== 'undefined' && module.exports) { + module.exports = Backburner; + } else if (typeof this !== 'undefined') { + this['Backburner'] = Backburner; + } + }); +enifed("backburner/binary-search", + ["exports"], + function(__exports__) { + "use strict"; + __exports__["default"] = function binarySearch(time, timers) { + var start = 0; + var end = timers.length - 2; + var middle, l; while (start < end) { // since timers is an array of pairs 'l' will always @@ -585,40 +659,37 @@ define("backburner", return (time >= timers[start]) ? start + 2 : start; } - - __exports__.Backburner = Backburner; }); -define("backburner/deferred_action_queues", - ["backburner/utils","backburner/queue","exports"], +enifed("backburner/deferred-action-queues", + ["./utils","./queue","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; - var Utils = __dependency1__["default"]; - var Queue = __dependency2__.Queue; - - var each = Utils.each, - isString = Utils.isString; + var each = __dependency1__.each; + var Queue = __dependency2__["default"]; function DeferredActionQueues(queueNames, options) { - var queues = this.queues = {}; + var queues = this.queues = Object.create(null); this.queueNames = queueNames = queueNames || []; this.options = options; each(queueNames, function(queueName) { - queues[queueName] = new Queue(this, queueName, options); + queues[queueName] = new Queue(queueName, options[queueName], options); }); } + function noSuchQueue(name) { + throw new Error("You attempted to schedule an action in a queue (" + name + ") that doesn't exist"); + } + DeferredActionQueues.prototype = { - queueNames: null, - queues: null, - options: null, + schedule: function(name, target, method, args, onceFlag, stack) { + var queues = this.queues; + var queue = queues[name]; - schedule: function(queueName, target, method, args, onceFlag, stack) { - var queues = this.queues, - queue = queues[queueName]; - - if (!queue) { throw new Error("You attempted to schedule an action in a queue (" + queueName + ") that doesn't exist"); } + if (!queue) { + noSuchQueue(name); + } if (onceFlag) { return queue.pushUnique(target, method, args, stack); @@ -627,7 +698,150 @@ define("backburner/deferred_action_queues", } }, - invoke: function(target, method, args, _) { + flush: function() { + var queues = this.queues; + var queueNames = this.queueNames; + var queueName, queue, queueItems, priorQueueNameIndex; + var queueNameIndex = 0; + var numberOfQueues = queueNames.length; + var options = this.options; + + while (queueNameIndex < numberOfQueues) { + queueName = queueNames[queueNameIndex]; + queue = queues[queueName]; + + var numberOfQueueItems = queue._queue.length; + + if (numberOfQueueItems === 0) { + queueNameIndex++; + } else { + queue.flush(false /* async */); + queueNameIndex = 0; + } + } + } + }; + + __exports__["default"] = DeferredActionQueues; + }); +enifed("backburner/platform", + ["exports"], + function(__exports__) { + "use strict"; + // In IE 6-8, try/finally doesn't work without a catch. + // Unfortunately, this is impossible to test for since wrapping it in a parent try/catch doesn't trigger the bug. + // This tests for another broken try/catch behavior that only exhibits in the same versions of IE. + var needsIETryCatchFix = (function(e,x){ + try{ x(); } + catch(e) { } // jshint ignore:line + return !!e; + })(); + __exports__.needsIETryCatchFix = needsIETryCatchFix; + }); +enifed("backburner/queue", + ["./utils","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var isString = __dependency1__.isString; + + function Queue(name, options, globalOptions) { + this.name = name; + this.globalOptions = globalOptions || {}; + this.options = options; + this._queue = []; + this.targetQueues = Object.create(null); + this._queueBeingFlushed = undefined; + } + + Queue.prototype = { + push: function(target, method, args, stack) { + var queue = this._queue; + queue.push(target, method, args, stack); + + return { + queue: this, + target: target, + method: method + }; + }, + + pushUniqueWithoutGuid: function(target, method, args, stack) { + var queue = this._queue; + + for (var i = 0, l = queue.length; i < l; i += 4) { + var currentTarget = queue[i]; + var currentMethod = queue[i+1]; + + if (currentTarget === target && currentMethod === method) { + queue[i+2] = args; // replace args + queue[i+3] = stack; // replace stack + return; + } + } + + queue.push(target, method, args, stack); + }, + + targetQueue: function(targetQueue, target, method, args, stack) { + var queue = this._queue; + + for (var i = 0, l = targetQueue.length; i < l; i += 4) { + var currentMethod = targetQueue[i]; + var currentIndex = targetQueue[i + 1]; + + if (currentMethod === method) { + queue[currentIndex + 2] = args; // replace args + queue[currentIndex + 3] = stack; // replace stack + return; + } + } + + targetQueue.push( + method, + queue.push(target, method, args, stack) - 4 + ); + }, + + pushUniqueWithGuid: function(guid, target, method, args, stack) { + var hasLocalQueue = this.targetQueues[guid]; + + if (hasLocalQueue) { + this.targetQueue(hasLocalQueue, target, method, args, stack); + } else { + this.targetQueues[guid] = [ + method, + this._queue.push(target, method, args, stack) - 4 + ]; + } + + return { + queue: this, + target: target, + method: method + }; + }, + + pushUnique: function(target, method, args, stack) { + var queue = this._queue, currentTarget, currentMethod, i, l; + var KEY = this.globalOptions.GUID_KEY; + + if (target && KEY) { + var guid = target[KEY]; + if (guid) { + return this.pushUniqueWithGuid(guid, target, method, args, stack); + } + } + + this.pushUniqueWithoutGuid(target, method, args, stack); + + return { + queue: this, + target: target, + method: method + }; + }, + + invoke: function(target, method, args, _, _errorRecordedForStack) { if (args && args.length > 0) { method.apply(target, args); } else { @@ -635,7 +849,7 @@ define("backburner/deferred_action_queues", } }, - invokeWithOnError: function(target, method, args, onError) { + invokeWithOnError: function(target, method, args, onError, errorRecordedForStack) { try { if (args && args.length > 0) { method.apply(target, args); @@ -643,179 +857,100 @@ define("backburner/deferred_action_queues", method.call(target); } } catch(error) { - onError(error); + onError(error, errorRecordedForStack); } }, - flush: function() { - var queues = this.queues, - queueNames = this.queueNames, - queueName, queue, queueItems, priorQueueNameIndex, - queueNameIndex = 0, numberOfQueues = queueNames.length, - options = this.options, - onError = options.onError || (options.onErrorTarget && options.onErrorTarget[options.onErrorMethod]), - invoke = onError ? this.invokeWithOnError : this.invoke; - - outerloop: - while (queueNameIndex < numberOfQueues) { - queueName = queueNames[queueNameIndex]; - queue = queues[queueName]; - queueItems = queue._queueBeingFlushed = queue._queue.slice(); - queue._queue = []; - - var queueOptions = queue.options, // TODO: write a test for this - before = queueOptions && queueOptions.before, - after = queueOptions && queueOptions.after, - target, method, args, stack, - queueIndex = 0, numberOfQueueItems = queueItems.length; - - if (numberOfQueueItems && before) { before(); } - - while (queueIndex < numberOfQueueItems) { - target = queueItems[queueIndex]; - method = queueItems[queueIndex+1]; - args = queueItems[queueIndex+2]; - stack = queueItems[queueIndex+3]; // Debugging assistance - - if (isString(method)) { method = target[method]; } - - // method could have been nullified / canceled during flush - if (method) { - invoke(target, method, args, onError); - } - - queueIndex += 4; - } - - queue._queueBeingFlushed = null; - if (numberOfQueueItems && after) { after(); } - - if ((priorQueueNameIndex = indexOfPriorQueueWithActions(this, queueNameIndex)) !== -1) { - queueNameIndex = priorQueueNameIndex; - continue outerloop; - } - - queueNameIndex++; - } - } - }; - - function indexOfPriorQueueWithActions(daq, currentQueueIndex) { - var queueName, queue; - - for (var i = 0, l = currentQueueIndex; i <= l; i++) { - queueName = daq.queueNames[i]; - queue = daq.queues[queueName]; - if (queue._queue.length) { return i; } - } - - return -1; - } - - __exports__.DeferredActionQueues = DeferredActionQueues; - }); -define("backburner/queue", - ["exports"], - function(__exports__) { - "use strict"; - function Queue(daq, name, options) { - this.daq = daq; - this.name = name; - this.globalOptions = options; - this.options = options[name]; - this._queue = []; - } - - Queue.prototype = { - daq: null, - name: null, - options: null, - onError: null, - _queue: null, - - push: function(target, method, args, stack) { + flush: function(sync) { var queue = this._queue; - queue.push(target, method, args, stack); - return {queue: this, target: target, method: method}; - }, + var length = queue.length; - pushUnique: function(target, method, args, stack) { - var queue = this._queue, currentTarget, currentMethod, i, l; + if (length === 0) { + return; + } - for (i = 0, l = queue.length; i < l; i += 4) { - currentTarget = queue[i]; - currentMethod = queue[i+1]; + var globalOptions = this.globalOptions; + var options = this.options; + var before = options && options.before; + var after = options && options.after; + var onError = globalOptions.onError || (globalOptions.onErrorTarget && + globalOptions.onErrorTarget[globalOptions.onErrorMethod]); + var target, method, args, errorRecordedForStack; + var invoke = onError ? this.invokeWithOnError : this.invoke; - if (currentTarget === target && currentMethod === method) { - queue[i+2] = args; // replace args - queue[i+3] = stack; // replace stack - return {queue: this, target: target, method: method}; + this.targetQueues = Object.create(null); + var queueItems = this._queueBeingFlushed = this._queue.slice(); + this._queue = []; + + if (before) { + before(); + } + + for (var i = 0; i < length; i += 4) { + target = queueItems[i]; + method = queueItems[i+1]; + args = queueItems[i+2]; + errorRecordedForStack = queueItems[i+3]; // Debugging assistance + + if (isString(method)) { + method = target[method]; + } + + // method could have been nullified / canceled during flush + if (method) { + // + // ** Attention intrepid developer ** + // + // To find out the stack of this task when it was scheduled onto + // the run loop, add the following to your app.js: + // + // Ember.run.backburner.DEBUG = true; // NOTE: This slows your app, don't leave it on in production. + // + // Once that is in place, when you are at a breakpoint and navigate + // here in the stack explorer, you can look at `errorRecordedForStack.stack`, + // which will be the captured stack when this job was scheduled. + // + invoke(target, method, args, onError, errorRecordedForStack); } } - queue.push(target, method, args, stack); - return {queue: this, target: target, method: method}; - }, - - // TODO: remove me, only being used for Ember.run.sync - flush: function() { - var queue = this._queue, - globalOptions = this.globalOptions, - options = this.options, - before = options && options.before, - after = options && options.after, - onError = globalOptions.onError || (globalOptions.onErrorTarget && globalOptions.onErrorTarget[globalOptions.onErrorMethod]), - target, method, args, stack, i, l = queue.length; - - if (l && before) { before(); } - for (i = 0; i < l; i += 4) { - target = queue[i]; - method = queue[i+1]; - args = queue[i+2]; - stack = queue[i+3]; // Debugging assistance - - // TODO: error handling - if (args && args.length > 0) { - if (onError) { - try { - method.apply(target, args); - } catch (e) { - onError(e); - } - } else { - method.apply(target, args); - } - } else { - if (onError) { - try { - method.call(target); - } catch(e) { - onError(e); - } - } else { - method.call(target); - } - } + if (after) { + after(); } - if (l && after) { after(); } - // check if new items have been added - if (queue.length > l) { - this._queue = queue.slice(l); - this.flush(); - } else { - this._queue.length = 0; + this._queueBeingFlushed = undefined; + + if (sync !== false && + this._queue.length > 0) { + // check if new items have been added + this.flush(true); } }, cancel: function(actionToCancel) { var queue = this._queue, currentTarget, currentMethod, i, l; + var target = actionToCancel.target; + var method = actionToCancel.method; + var GUID_KEY = this.globalOptions.GUID_KEY; + + if (GUID_KEY && this.targetQueues && target) { + var targetQueue = this.targetQueues[target[GUID_KEY]]; + + if (targetQueue) { + for (i = 0, l = targetQueue.length; i < l; i++) { + if (targetQueue[i] === method) { + targetQueue.splice(i, 1); + } + } + } + } for (i = 0, l = queue.length; i < l; i += 4) { currentTarget = queue[i]; currentMethod = queue[i+1]; - if (currentTarget === actionToCancel.target && currentMethod === actionToCancel.method) { + if (currentTarget === target && + currentMethod === method) { queue.splice(i, 4); return true; } @@ -824,14 +959,17 @@ define("backburner/queue", // if not found in current queue // could be in the queue that is being flushed queue = this._queueBeingFlushed; + if (!queue) { return; } + for (i = 0, l = queue.length; i < l; i += 4) { currentTarget = queue[i]; currentMethod = queue[i+1]; - if (currentTarget === actionToCancel.target && currentMethod === actionToCancel.method) { + if (currentTarget === target && + currentMethod === method) { // don't mess with array during flush // just nullify the method queue[i+1] = null; @@ -841,44 +979,63 @@ define("backburner/queue", } }; - __exports__.Queue = Queue; + __exports__["default"] = Queue; }); -define("backburner/utils", +enifed("backburner/utils", ["exports"], function(__exports__) { "use strict"; - __exports__["default"] = { - each: function(collection, callback) { - for (var i = 0; i < collection.length; i++) { - callback(collection[i]); - } - }, + var NUMBER = /\d+/; - isString: function(suspect) { - return typeof suspect === 'string'; - }, - - isFunction: function(suspect) { - return typeof suspect === 'function'; - }, - - isNumber: function(suspect) { - return typeof suspect === 'number'; + function each(collection, callback) { + for (var i = 0; i < collection.length; i++) { + callback(collection[i]); } - }; - }); + } -define("calculateVersion", + __exports__.each = each;// Date.now is not available in browsers < IE9 + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now#Compatibility + var now = Date.now || function() { return new Date().getTime(); }; + __exports__.now = now; + function isString(suspect) { + return typeof suspect === 'string'; + } + + __exports__.isString = isString;function isFunction(suspect) { + return typeof suspect === 'function'; + } + + __exports__.isFunction = isFunction;function isNumber(suspect) { + return typeof suspect === 'number'; + } + + __exports__.isNumber = isNumber;function isCoercableNumber(number) { + return isNumber(number) || NUMBER.test(number); + } + + __exports__.isCoercableNumber = isCoercableNumber;function wrapInTryCatch(func) { + return function () { + try { + return func.apply(this, arguments); + } catch (e) { + throw e; + } + }; + } + + __exports__.wrapInTryCatch = wrapInTryCatch; + }); +enifed("calculateVersion", [], function() { "use strict"; 'use strict'; - var fs = require('fs'); - var path = require('path'); + var fs = eriuqer('fs'); + var path = eriuqer('path'); module.exports = function () { - var packageVersion = require('../package.json').version; + var packageVersion = eriuqer('../package.json').version; var output = [packageVersion]; var gitPath = path.join(__dirname,'..','.git'); var headFilePath = path.join(gitPath, 'HEAD'); @@ -909,7 +1066,7 @@ define("calculateVersion", } }; }); -define("container", +enifed("container", ["container/container","exports"], function(__dependency1__, __exports__) { "use strict"; @@ -936,13 +1093,14 @@ define("container", __exports__["default"] = Container; }); -define("container/container", - ["container/inheriting_dict","ember-metal/core","exports"], - function(__dependency1__, __dependency2__, __exports__) { +enifed("container/container", + ["ember-metal/core","ember-metal/keys","ember-metal/dictionary","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { "use strict"; - var InheritingDict = __dependency1__["default"]; - var Ember = __dependency2__["default"]; + var Ember = __dependency1__["default"]; // Ember.assert + var emberKeys = __dependency2__["default"]; + var dictionary = __dependency3__["default"]; // A lightweight container that helps to assemble and decouple components. // Public api for the container is still in flux. @@ -953,18 +1111,19 @@ define("container/container", this.resolver = parent && parent.resolver || function() {}; - this.registry = new InheritingDict(parent && parent.registry); - this.cache = new InheritingDict(parent && parent.cache); - this.factoryCache = new InheritingDict(parent && parent.factoryCache); - this.resolveCache = new InheritingDict(parent && parent.resolveCache); - this.typeInjections = new InheritingDict(parent && parent.typeInjections); - this.injections = {}; + this.registry = dictionary(parent ? parent.registry : null); + this.cache = dictionary(parent ? parent.cache : null); + this.factoryCache = dictionary(parent ? parent.factoryCache : null); + this.resolveCache = dictionary(parent ? parent.resolveCache : null); + this.typeInjections = dictionary(parent ? parent.typeInjections : null); + this.injections = dictionary(null); + this.normalizeCache = dictionary(null); - this.factoryTypeInjections = new InheritingDict(parent && parent.factoryTypeInjections); - this.factoryInjections = {}; + this.factoryTypeInjections = dictionary(parent ? parent.factoryTypeInjections : null); + this.factoryInjections = dictionary(null); - this._options = new InheritingDict(parent && parent._options); - this._typeOptions = new InheritingDict(parent && parent._typeOptions); + this._options = dictionary(parent ? parent._options : null); + this._typeOptions = dictionary(parent ? parent._typeOptions : null); } Container.prototype = { @@ -1044,20 +1203,6 @@ define("container/container", return container; }, - /** - Sets a key-value pair on the current container. If a parent container, - has the same key, once set on a child, the parent and child will diverge - as expected. - - @method set - @param {Object} object - @param {String} key - @param {any} value - */ - set: function(object, key, value) { - object[key] = value; - }, - /** Registers a factory for later injection. @@ -1085,12 +1230,12 @@ define("container/container", var normalizedName = this.normalize(fullName); - if (this.cache.has(normalizedName)) { + if (normalizedName in this.cache) { throw new Error('Cannot re-register: `' + fullName +'`, as it has already been looked up.'); } - this.registry.set(normalizedName, factory); - this._options.set(normalizedName, options || {}); + this.registry[normalizedName] = factory; + this._options[normalizedName] = (options || {}); }, /** @@ -1114,11 +1259,11 @@ define("container/container", var normalizedName = this.normalize(fullName); - this.registry.remove(normalizedName); - this.cache.remove(normalizedName); - this.factoryCache.remove(normalizedName); - this.resolveCache.remove(normalizedName); - this._options.remove(normalizedName); + delete this.registry[normalizedName]; + delete this.cache[normalizedName]; + delete this.factoryCache[normalizedName]; + delete this.resolveCache[normalizedName]; + delete this._options[normalizedName]; }, /** @@ -1177,12 +1322,25 @@ define("container/container", /** A hook to enable custom fullName normalization behaviour + @method normalizeFullName + @param {String} fullName + @return {string} normalized fullName + */ + normalizeFullName: function(fullName) { + return fullName; + }, + + /** + normalize a fullName based on the applications conventions + @method normalize @param {String} fullName @return {string} normalized fullName */ normalize: function(fullName) { - return fullName; + return this.normalizeCache[fullName] || ( + this.normalizeCache[fullName] = this.normalizeFullName(fullName) + ); }, /** @@ -1295,16 +1453,18 @@ define("container/container", optionsForType: function(type, options) { if (this.parent) { illegalChildOperation('optionsForType'); } - this._typeOptions.set(type, options); + this._typeOptions[type] = options; }, /** @method options - @param {String} type + @param {String} fullName @param {Object} options */ - options: function(type, options) { - this.optionsForType(type, options); + options: function(fullName, options) { + options = options || {}; + var normalizedName = this.normalize(fullName); + this._options[normalizedName] = options; }, /** @@ -1344,12 +1504,17 @@ define("container/container", */ typeInjection: function(type, property, fullName) { Ember.assert('fullName must be a proper full name', validateFullName(fullName)); + if (this.parent) { illegalChildOperation('typeInjection'); } var fullNameType = fullName.split(':')[0]; - if(fullNameType === type) { - throw new Error('Cannot inject a `' + fullName + '` on other ' + type + '(s). Register the `' + fullName + '` as a different type and perform the typeInjection.'); + if (fullNameType === type) { + throw new Error('Cannot inject a `' + fullName + + '` on other ' + type + + '(s). Register the `' + fullName + + '` as a different type and perform the typeInjection.'); } + addTypeInjection(this.typeInjections, type, property, fullName); }, @@ -1410,10 +1575,14 @@ define("container/container", Ember.assert('fullName must be a proper full name', validateFullName(fullName)); var normalizedName = this.normalize(fullName); - if (this.cache.has(normalizedName)) { - throw new Error("Attempted to register an injection for a type that has already been looked up. ('" + normalizedName + "', '" + property + "', '" + injectionName + "')"); + if (this.cache[normalizedName]) { + throw new Error("Attempted to register an injection for a type that has already been looked up. ('" + + normalizedName + "', '" + + property + "', '" + + injectionName + "')"); } - addInjection(this.injections, normalizedName, property, normalizedInjectionName); + + addInjection(initRules(this.injections, normalizedName), property, normalizedInjectionName); }, @@ -1515,12 +1684,12 @@ define("container/container", Ember.assert('fullName must be a proper full name', validateFullName(fullName)); - if (this.factoryCache.has(normalizedName)) { + if (this.factoryCache[normalizedName]) { throw new Error('Attempted to register a factoryInjection for a type that has already ' + 'been looked up. (\'' + normalizedName + '\', \'' + property + '\', \'' + injectionName + '\')'); } - addInjection(this.factoryInjections, normalizedName, property, normalizedInjectionName); + addInjection(initRules(this.factoryInjections, normalizedName), property, normalizedInjectionName); }, /** @@ -1557,28 +1726,28 @@ define("container/container", }; function resolve(container, normalizedName) { - var cached = container.resolveCache.get(normalizedName); + var cached = container.resolveCache[normalizedName]; if (cached) { return cached; } - var resolved = container.resolver(normalizedName) || container.registry.get(normalizedName); - container.resolveCache.set(normalizedName, resolved); + var resolved = container.resolver(normalizedName) || container.registry[normalizedName]; + container.resolveCache[normalizedName] = resolved; return resolved; } function has(container, fullName){ - if (container.cache.has(fullName)) { + if (container.cache[fullName]) { return true; } - return !!container.resolve(fullName); + return container.resolve(fullName) !== undefined; } function lookup(container, fullName, options) { options = options || {}; - if (container.cache.has(fullName) && options.singleton !== false) { - return container.cache.get(fullName); + if (container.cache[fullName] && options.singleton !== false) { + return container.cache[fullName]; } var value = instantiate(container, fullName); @@ -1586,7 +1755,7 @@ define("container/container", if (value === undefined) { return; } if (isSingleton(container, fullName) && options.singleton !== false) { - container.cache.set(fullName, value); + container.cache[fullName] = value; } return value; @@ -1607,31 +1776,41 @@ define("container/container", if (!injections) { return hash; } - var injection, injectable; + validateInjections(container, injections); + + var injection; for (var i = 0, length = injections.length; i < length; i++) { injection = injections[i]; - injectable = lookup(container, injection.fullName); - - if (injectable !== undefined) { - hash[injection.property] = injectable; - } else { - throw new Error('Attempting to inject an unknown injection: `' + injection.fullName + '`'); - } + hash[injection.property] = lookup(container, injection.fullName); } return hash; } + function validateInjections(container, injections) { + if (!injections) { return; } + + var fullName; + + for (var i = 0, length = injections.length; i < length; i++) { + fullName = injections[i].fullName; + + if (!container.has(fullName)) { + throw new Error('Attempting to inject an unknown injection: `' + fullName + '`'); + } + } + } + function option(container, fullName, optionName) { - var options = container._options.get(fullName); + var options = container._options[fullName]; if (options && options[optionName] !== undefined) { return options[optionName]; } var type = fullName.split(':')[0]; - options = container._typeOptions.get(type); + options = container._typeOptions[type]; if (options) { return options[optionName]; @@ -1640,8 +1819,8 @@ define("container/container", function factoryFor(container, fullName) { var cache = container.factoryCache; - if (cache.has(fullName)) { - return cache.get(fullName); + if (cache[fullName]) { + return cache[fullName]; } var factory = container.resolve(fullName); if (factory === undefined) { return; } @@ -1650,6 +1829,7 @@ define("container/container", if (!factory || typeof factory.extend !== 'function' || (!Ember.MODEL_FACTORY_INJECTIONS && type === 'model')) { // TODO: think about a 'safe' merge style extension // for now just fallback to create time injection + cache[fullName] = factory; return factory; } else { var injections = injectionsFor(container, fullName); @@ -1660,18 +1840,18 @@ define("container/container", var injectedFactory = factory.extend(injections); injectedFactory.reopenClass(factoryInjections); - cache.set(fullName, injectedFactory); + cache[fullName] = injectedFactory; return injectedFactory; } } function injectionsFor(container, fullName) { - var splitName = fullName.split(':'), - type = splitName[0], - injections = []; + var splitName = fullName.split(':'); + var type = splitName[0]; + var injections = []; - injections = injections.concat(container.typeInjections.get(type) || []); + injections = injections.concat(container.typeInjections[type] || []); injections = injections.concat(container.injections[fullName] || []); injections = buildInjections(container, injections); @@ -1682,11 +1862,11 @@ define("container/container", } function factoryInjectionsFor(container, fullName) { - var splitName = fullName.split(':'), - type = splitName[0], - factoryInjections = []; + var splitName = fullName.split(':'); + var type = splitName[0]; + var factoryInjections = []; - factoryInjections = factoryInjections.concat(container.factoryTypeInjections.get(type) || []); + factoryInjections = factoryInjections.concat(container.factoryTypeInjections[type] || []); factoryInjections = factoryInjections.concat(container.factoryInjections[fullName] || []); factoryInjections = buildInjections(container, factoryInjections); @@ -1695,8 +1875,23 @@ define("container/container", return factoryInjections; } + function normalizeInjectionsHash(hash) { + var injections = []; + + for (var key in hash) { + if (hash.hasOwnProperty(key)) { + Ember.assert("Expected a proper full name, given '" + hash[key] + "'", validateFullName(hash[key])); + + addInjection(injections, key, hash[key]); + } + } + + return injections; + } + function instantiate(container, fullName) { var factory = factoryFor(container, fullName); + var lazyInjections; if (option(container, fullName, 'instantiate') === false) { return factory; @@ -1708,6 +1903,7 @@ define("container/container", 'Most likely an improperly defined class or an invalid module export.'); } + if (typeof factory.extend === 'function') { // assume the factory was extendable and is already injected return factory.create(); @@ -1721,26 +1917,34 @@ define("container/container", } function eachDestroyable(container, callback) { - container.cache.eachLocal(function(key, value) { - if (option(container, key, 'instantiate') === false) { return; } - callback(value); - }); + var cache = container.cache; + var keys = emberKeys(cache); + var key, value; + + for (var i = 0, l = keys.length; i < l; i++) { + key = keys[i]; + value = cache[key]; + + if (option(container, key, 'instantiate') !== false) { + callback(value); + } + } } function resetCache(container) { - container.cache.eachLocal(function(key, value) { - if (option(container, key, 'instantiate') === false) { return; } + eachDestroyable(container, function(value) { value.destroy(); }); - container.cache.dict = {}; + + container.cache.dict = dictionary(null); } function addTypeInjection(rules, type, property, fullName) { - var injections = rules.get(type); + var injections = rules[type]; if (!injections) { injections = []; - rules.set(type, injections); + rules[type] = injections; } injections.push({ @@ -1757,130 +1961,221 @@ define("container/container", return true; } - function addInjection(rules, factoryName, property, injectionName) { - var injections = rules[factoryName] = rules[factoryName] || []; - injections.push({ property: property, fullName: injectionName }); + function initRules(rules, factoryName) { + return rules[factoryName] || (rules[factoryName] = []); + } + + function addInjection(injections, property, injectionName) { + injections.push({ + property: property, + fullName: injectionName + }); } __exports__["default"] = Container; }); -define("container/inheriting_dict", +enifed("dag-map", ["exports"], function(__exports__) { "use strict"; - // A safe and simple inheriting object. - function InheritingDict(parent) { - this.parent = parent; - this.dict = {}; + function visit(vertex, fn, visited, path) { + var name = vertex.name; + var vertices = vertex.incoming; + var names = vertex.incomingNames; + var len = names.length; + var i; + + if (!visited) { + visited = {}; + } + if (!path) { + path = []; + } + if (visited.hasOwnProperty(name)) { + return; + } + path.push(name); + visited[name] = true; + for (i = 0; i < len; i++) { + visit(vertices[names[i]], fn, visited, path); + } + fn(vertex, path); + path.pop(); } - InheritingDict.prototype = { - /** - @property parent - @type InheritingDict - @default null - */ + /** + * DAG stands for Directed acyclic graph. + * + * It is used to build a graph of dependencies checking that there isn't circular + * dependencies. p.e Registering initializers with a certain precedence order. + * + * @class DAG + * @constructor + */ + function DAG() { + this.names = []; + this.vertices = Object.create(null); + } - parent: null, + /** + * DAG Vertex + * + * @class Vertex + * @constructor + */ - /** - Object used to store the current nodes data. + function Vertex(name) { + this.name = name; + this.incoming = {}; + this.incomingNames = []; + this.hasOutgoing = false; + this.value = null; + } - @property dict - @type Object - @default Object - */ - dict: null, + /** + * Adds a vertex entry to the graph unless it is already added. + * + * @private + * @method add + * @param {String} name The name of the vertex to add + */ + DAG.prototype.add = function(name) { + if (!name) { + throw new Error("Can't add Vertex without name"); + } + if (this.vertices[name] !== undefined) { + return this.vertices[name]; + } + var vertex = new Vertex(name); + this.vertices[name] = vertex; + this.names.push(name); + return vertex; + }; - /** - Retrieve the value given a key, if the value is present at the current - level use it, otherwise walk up the parent hierarchy and try again. If - no matching key is found, return undefined. + /** + * Adds a vertex to the graph and sets its value. + * + * @private + * @method map + * @param {String} name The name of the vertex. + * @param value The value to put in the vertex. + */ + DAG.prototype.map = function(name, value) { + this.add(name).value = value; + }; - @method get - @param {String} key - @return {any} - */ - get: function(key) { - var dict = this.dict; - - if (dict.hasOwnProperty(key)) { - return dict[key]; + /** + * Connects the vertices with the given names, adding them to the graph if + * necessary, only if this does not produce is any circular dependency. + * + * @private + * @method addEdge + * @param {String} fromName The name the vertex where the edge starts. + * @param {String} toName The name the vertex where the edge ends. + */ + DAG.prototype.addEdge = function(fromName, toName) { + if (!fromName || !toName || fromName === toName) { + return; + } + var from = this.add(fromName); + var to = this.add(toName); + if (to.incoming.hasOwnProperty(fromName)) { + return; + } + function checkCycle(vertex, path) { + if (vertex.name === toName) { + throw new Error("cycle detected: " + toName + " <- " + path.join(" <- ")); } + } + visit(from, checkCycle); + from.hasOutgoing = true; + to.incoming[fromName] = from; + to.incomingNames.push(fromName); + }; - if (this.parent) { - return this.parent.get(key); + /** + * Visits all the vertex of the graph calling the given function with each one, + * ensuring that the vertices are visited respecting their precedence. + * + * @method topsort + * @param {Function} fn The function to be invoked on each vertex. + */ + DAG.prototype.topsort = function(fn) { + var visited = {}; + var vertices = this.vertices; + var names = this.names; + var len = names.length; + var i, vertex; + + for (i = 0; i < len; i++) { + vertex = vertices[names[i]]; + if (!vertex.hasOutgoing) { + visit(vertex, fn, visited); } - }, + } + }; - /** - Set the given value for the given key, at the current level. - - @method set - @param {String} key - @param {Any} value - */ - set: function(key, value) { - this.dict[key] = value; - }, - - /** - Delete the given key - - @method remove - @param {String} key - */ - remove: function(key) { - delete this.dict[key]; - }, - - /** - Check for the existence of given a key, if the key is present at the current - level return true, otherwise walk up the parent hierarchy and try again. If - no matching key is found, return false. - - @method has - @param {String} key - @return {Boolean} - */ - has: function(key) { - var dict = this.dict; - - if (dict.hasOwnProperty(key)) { - return true; + /** + * Adds a vertex with the given name and value to the graph and joins it with the + * vertices referenced in _before_ and _after_. If there isn't vertices with those + * names, they are added too. + * + * If either _before_ or _after_ are falsy/empty, the added vertex will not have + * an incoming/outgoing edge. + * + * @method addEdges + * @param {String} name The name of the vertex to be added. + * @param value The value of that vertex. + * @param before An string or array of strings with the names of the vertices before + * which this vertex must be visited. + * @param after An string or array of strings with the names of the vertex after + * which this vertex must be visited. + * + */ + DAG.prototype.addEdges = function(name, value, before, after) { + var i; + this.map(name, value); + if (before) { + if (typeof before === 'string') { + this.addEdge(name, before); + } else { + for (i = 0; i < before.length; i++) { + this.addEdge(name, before[i]); + } } - - if (this.parent) { - return this.parent.has(key); - } - - return false; - }, - - /** - Iterate and invoke a callback for each local key-value pair. - - @method eachLocal - @param {Function} callback - @param {Object} binding - */ - eachLocal: function(callback, binding) { - var dict = this.dict; - - for (var prop in dict) { - if (dict.hasOwnProperty(prop)) { - callback.call(binding, prop, dict[prop]); + } + if (after) { + if (typeof after === 'string') { + this.addEdge(after, name); + } else { + for (i = 0; i < after.length; i++) { + this.addEdge(after[i], name); } } } }; - __exports__["default"] = InheritingDict; + __exports__["default"] = DAG; }); -define("ember-application", - ["ember-metal/core","ember-runtime/system/lazy_load","ember-application/system/dag","ember-application/system/resolver","ember-application/system/application","ember-application/ext/controller"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__) { +enifed("dag-map.umd", + ["./dag-map"], + function(__dependency1__) { + "use strict"; + var DAG = __dependency1__["default"]; + + /* global define:true module:true window: true */ + if (typeof enifed === 'function' && enifed.amd) { + enifed(function() { return DAG; }); + } else if (typeof module !== 'undefined' && module.exports) { + module.exports = DAG; + } else if (typeof this !== 'undefined') { + this['DAG'] = DAG; + } + }); +enifed("ember-application", + ["ember-metal/core","ember-runtime/system/lazy_load","ember-application/system/resolver","ember-application/system/application","ember-application/ext/controller"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) { "use strict"; var Ember = __dependency1__["default"]; var runLoadHooks = __dependency2__.runLoadHooks; @@ -1893,22 +2188,20 @@ define("ember-application", @requires ember-views, ember-routing */ - var DAG = __dependency3__["default"]; - var Resolver = __dependency4__.Resolver; - var DefaultResolver = __dependency4__["default"]; - var Application = __dependency5__["default"]; + var Resolver = __dependency3__.Resolver; + var DefaultResolver = __dependency3__["default"]; + var Application = __dependency4__["default"]; // side effect of extending ControllerMixin Ember.Application = Application; - Ember.DAG = DAG; Ember.Resolver = Resolver; Ember.DefaultResolver = DefaultResolver; runLoadHooks('Ember.Application', Application); }); -define("ember-application/ext/controller", - ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/error","ember-metal/utils","ember-metal/computed","ember-runtime/mixins/controller","ember-routing/system/controller_for","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) { +enifed("ember-application/ext/controller", + ["ember-metal/core","ember-metal/property_get","ember-metal/error","ember-metal/utils","ember-metal/computed","ember-runtime/mixins/controller","ember-routing/system/controller_for","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) { "use strict"; /** @module ember @@ -1918,21 +2211,22 @@ define("ember-application/ext/controller", var Ember = __dependency1__["default"]; // Ember.assert var get = __dependency2__.get; - var set = __dependency3__.set; - var EmberError = __dependency4__["default"]; - var inspect = __dependency5__.inspect; - var computed = __dependency6__.computed; - var ControllerMixin = __dependency7__["default"]; - var meta = __dependency5__.meta; - var controllerFor = __dependency8__["default"]; + var EmberError = __dependency3__["default"]; + var inspect = __dependency4__.inspect; + var computed = __dependency5__.computed; + var ControllerMixin = __dependency6__["default"]; + var meta = __dependency4__.meta; + var controllerFor = __dependency7__["default"]; function verifyNeedsDependencies(controller, container, needs) { - var dependency, i, l, missing = []; + var dependency, i, l; + var missing = []; for (i=0, l=needs.length; i 1 ? 'they' : 'it') + " could not be found"); + throw new EmberError(inspect(controller) + " needs [ " + missing.join(', ') + + " ] but " + (missing.length > 1 ? 'they' : 'it') + " could not be found"); } } @@ -1955,8 +2250,9 @@ define("ember-application/ext/controller", needs: get(controller, 'needs'), container: get(controller, 'container'), unknownProperty: function(controllerName) { - var needs = this.needs, - dependency, i, l; + var needs = this.needs; + var dependency, i, l; + for (i=0, l=needs.length; i Ember.TEMPLATES['post'] - 'template:posts/byline' //=> Ember.TEMPLATES['posts/byline'] - 'template:posts.byline' //=> Ember.TEMPLATES['posts/byline'] - 'template:blogPost' //=> Ember.TEMPLATES['blogPost'] - // OR - // Ember.TEMPLATES['blog_post'] - 'controller:post' //=> App.PostController - 'controller:posts.index' //=> App.PostsIndexController - 'controller:blog/post' //=> Blog.PostController - 'controller:basic' //=> Ember.Controller - 'route:post' //=> App.PostRoute - 'route:posts.index' //=> App.PostsIndexRoute - 'route:blog/post' //=> Blog.PostRoute - 'route:basic' //=> Ember.Route - 'view:post' //=> App.PostView - 'view:posts.index' //=> App.PostsIndexView - 'view:blog/post' //=> Blog.PostView - 'view:basic' //=> Ember.View - 'foo:post' //=> App.PostFoo - 'model:post' //=> App.Post + 'template:post' //=> Ember.TEMPLATES['post'] + 'template:posts/byline' //=> Ember.TEMPLATES['posts/byline'] + 'template:posts.byline' //=> Ember.TEMPLATES['posts/byline'] + 'template:blogPost' //=> Ember.TEMPLATES['blogPost'] + // OR + // Ember.TEMPLATES['blog_post'] + 'controller:post' //=> App.PostController + 'controller:posts.index' //=> App.PostsIndexController + 'controller:blog/post' //=> Blog.PostController + 'controller:basic' //=> Ember.Controller + 'route:post' //=> App.PostRoute + 'route:posts.index' //=> App.PostsIndexRoute + 'route:blog/post' //=> Blog.PostRoute + 'route:basic' //=> Ember.Route + 'view:post' //=> App.PostView + 'view:posts.index' //=> App.PostsIndexView + 'view:blog/post' //=> Blog.PostView + 'view:basic' //=> Ember.View + 'foo:post' //=> App.PostFoo + 'model:post' //=> App.Post ``` @class DefaultResolver @namespace Ember @extends Ember.Object */ + var dictionary = __dependency8__["default"]; __exports__["default"] = EmberObject.extend({ /** @@ -3424,22 +3581,31 @@ define("ember-application/system/resolver", */ namespace: null, + init: function() { + this._parseNameCache = dictionary(null); + }, normalize: function(fullName) { - var split = fullName.split(':', 2), - type = split[0], - name = split[1]; + var split = fullName.split(':', 2); + var type = split[0]; + var name = split[1]; - Ember.assert("Tried to normalize a container name without a colon (:) in it. You probably tried to lookup a name that did not contain a type, a colon, and a name. A proper lookup name would be `view:post`.", split.length === 2); + Ember.assert("Tried to normalize a container name without a colon (:) in it." + + " You probably tried to lookup a name that did not contain a type," + + " a colon, and a name. A proper lookup name would be `view:post`.", split.length === 2); if (type !== 'template') { var result = name; if (result.indexOf('.') > -1) { - result = result.replace(/\.(.)/g, function(m) { return m.charAt(1).toUpperCase(); }); + result = result.replace(/\.(.)/g, function(m) { + return m.charAt(1).toUpperCase(); + }); } if (name.indexOf('_') > -1) { - result = result.replace(/_(.)/g, function(m) { return m.charAt(1).toUpperCase(); }); + result = result.replace(/_(.)/g, function(m) { + return m.charAt(1).toUpperCase(); + }); } return type + ':' + result; @@ -3459,9 +3625,9 @@ define("ember-application/system/resolver", @return {Object} the resolved factory */ resolve: function(fullName) { - var parsedName = this.parseName(fullName), - resolveMethodName = parsedName.resolveMethodName, - resolved; + var parsedName = this.parseName(fullName); + var resolveMethodName = parsedName.resolveMethodName; + var resolved; if (!(parsedName.name && parsedName.type)) { throw new TypeError('Invalid fullName: `' + fullName + '`, must be of the form `type:name` '); @@ -3490,12 +3656,19 @@ define("ember-application/system/resolver", @param {String} fullName the lookup string @method parseName */ + parseName: function(fullName) { - var nameParts = fullName.split(':'), - type = nameParts[0], fullNameWithoutType = nameParts[1], - name = fullNameWithoutType, - namespace = get(this, 'namespace'), - root = namespace; + return this._parseNameCache[fullName] || ( + this._parseNameCache[fullName] = this._parseName(fullName) + ); + }, + + _parseName: function(fullName) { + var nameParts = fullName.split(':'); + var type = nameParts[0], fullNameWithoutType = nameParts[1]; + var name = fullNameWithoutType; + var namespace = get(this, 'namespace'); + var root = namespace; if (type !== 'template' && name.indexOf('/') !== -1) { var parts = name.split('/'); @@ -3503,7 +3676,9 @@ define("ember-application/system/resolver", var namespaceName = capitalize(parts.slice(0, -1).join('.')); root = Namespace.byName(namespaceName); - Ember.assert('You are looking for a ' + name + ' ' + type + ' in the ' + namespaceName + ' namespace, but the namespace could not be found', root); + Ember.assert('You are looking for a ' + name + ' ' + type + + ' in the ' + namespaceName + + ' namespace, but the namespace could not be found', root); } return { @@ -3534,7 +3709,10 @@ define("ember-application/system/resolver", } var description = parsedName.root + '.' + classify(parsedName.name); - if (parsedName.type !== 'model') { description += classify(parsedName.type); } + + if (parsedName.type !== 'model') { + description += classify(parsedName.type); + } return description; }, @@ -3577,6 +3755,7 @@ define("ember-application/system/resolver", return Ember.TEMPLATES[templateName]; } }, + /** Lookup the view using `resolveOther` @@ -3589,6 +3768,7 @@ define("ember-application/system/resolver", this.useRouterNaming(parsedName); return this.resolveOther(parsedName); }, + /** Lookup the controller using `resolveOther` @@ -3677,9 +3857,9 @@ define("ember-application/system/resolver", } }); }); -define("ember-debug", - ["ember-metal/core","ember-metal/error","ember-metal/logger"], - function(__dependency1__, __dependency2__, __dependency3__) { +enifed("ember-debug", + ["ember-metal/core","ember-metal/error","ember-metal/logger","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { "use strict"; /*global __fail__*/ @@ -3776,7 +3956,9 @@ define("ember-debug", try { __fail__.fail(); } catch (e) { error = e; } if (Ember.LOG_STACKTRACE_ON_DEPRECATION && error.stack) { - var stack, stackStr = ''; + var stack; + var stackStr = ''; + if (error['arguments']) { // Chrome stack = error.stack.replace(/^\s+at\s+/gm, ''). @@ -3846,8 +4028,39 @@ define("ember-debug", func(); }; - // Inform the developer about the Ember Inspector if not installed. - if (!Ember.testing) { + /** + Will call `Ember.warn()` if ENABLE_ALL_FEATURES, ENABLE_OPTIONAL_FEATURES, or + any specific FEATURES flag is truthy. + + This method is called automatically in debug canary builds. + + @private + @method _warnIfUsingStrippedFeatureFlags + @return {void} + */ + function _warnIfUsingStrippedFeatureFlags(FEATURES, featuresWereStripped) { + if (featuresWereStripped) { + Ember.warn('Ember.ENV.ENABLE_ALL_FEATURES is only available in canary builds.', !Ember.ENV.ENABLE_ALL_FEATURES); + Ember.warn('Ember.ENV.ENABLE_OPTIONAL_FEATURES is only available in canary builds.', !Ember.ENV.ENABLE_OPTIONAL_FEATURES); + + for (var key in FEATURES) { + if (FEATURES.hasOwnProperty(key) && key !== 'isEnabled') { + Ember.warn('FEATURE["' + key + '"] is set as enabled, but FEATURE flags are only available in canary builds.', !FEATURES[key]); + } + } + } + } + + __exports__._warnIfUsingStrippedFeatureFlags = _warnIfUsingStrippedFeatureFlags;if (!Ember.testing) { + // Complain if they're using FEATURE flags in builds other than canary + Ember.FEATURES['features-stripped-test'] = true; + var featuresWereStripped = true; + + + delete Ember.FEATURES['features-stripped-test']; + _warnIfUsingStrippedFeatureFlags(Ember.ENV.FEATURES, featuresWereStripped); + + // Inform the developer about the Ember Inspector if not installed. var isFirefox = typeof InstallTrigger !== 'undefined'; var isChrome = !!window.chrome && !window.opera; @@ -3868,7 +4081,7 @@ define("ember-debug", } } }); -define("ember-extension-support", +enifed("ember-extension-support", ["ember-metal/core","ember-extension-support/data_adapter","ember-extension-support/container_debug_adapter"], function(__dependency1__, __dependency2__, __dependency3__) { "use strict"; @@ -3887,7 +4100,7 @@ define("ember-extension-support", Ember.DataAdapter = DataAdapter; Ember.ContainerDebugAdapter = ContainerDebugAdapter; }); -define("ember-extension-support/container_debug_adapter", +enifed("ember-extension-support/container_debug_adapter", ["ember-metal/core","ember-runtime/system/native_array","ember-metal/utils","ember-runtime/system/string","ember-runtime/system/namespace","ember-runtime/system/object","exports"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) { "use strict"; @@ -3964,7 +4177,7 @@ define("ember-extension-support/container_debug_adapter", classes in the resolver for a given type. @method canCatalogEntriesByType - @param {string} type The type. e.g. "model", "controller", "route" + @param {String} type The type. e.g. "model", "controller", "route" @return {boolean} whether a list is available for this type. */ canCatalogEntriesByType: function(type) { @@ -3976,11 +4189,11 @@ define("ember-extension-support/container_debug_adapter", Returns the available classes a given type. @method catalogEntriesByType - @param {string} type The type. e.g. "model", "controller", "route" + @param {String} type The type. e.g. "model", "controller", "route" @return {Array} An array of strings. */ catalogEntriesByType: function(type) { - var namespaces = emberA(Namespace.NAMESPACES), types = emberA(), self = this; + var namespaces = emberA(Namespace.NAMESPACES), types = emberA(); var typeSuffixRegex = new RegExp(classify(type) + "$"); namespaces.forEach(function(namespace) { @@ -4000,7 +4213,7 @@ define("ember-extension-support/container_debug_adapter", } }); }); -define("ember-extension-support/data_adapter", +enifed("ember-extension-support/data_adapter", ["ember-metal/core","ember-metal/property_get","ember-metal/run_loop","ember-runtime/system/string","ember-runtime/system/namespace","ember-runtime/system/object","ember-runtime/system/native_array","ember-application/system/application","exports"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) { "use strict"; @@ -4138,8 +4351,10 @@ define("ember-extension-support/data_adapter", @return {Function} Method to call to remove all observers */ watchModelTypes: function(typesAdded, typesUpdated) { - var modelTypes = this.getModelTypes(), - self = this, typesToSend, releaseMethods = emberA(); + var modelTypes = this.getModelTypes(); + var self = this; + var releaseMethods = emberA(); + var typesToSend; typesToSend = modelTypes.map(function(type) { var klass = type.klass; @@ -4280,7 +4495,8 @@ define("ember-extension-support/data_adapter", */ observeModelType: function(type, typesUpdated) { - var self = this, records = this.getRecords(type); + var self = this; + var records = this.getRecords(type); var onChange = function() { typesUpdated([self.wrapModelType(type)]); @@ -4320,8 +4536,8 @@ define("ember-extension-support/data_adapter", release: {Function} The function to remove observers */ wrapModelType: function(type, name) { - var release, records = this.getRecords(type), - typeToSend, self = this; + var records = this.getRecords(type); + var typeToSend; typeToSend = { name: name || type.toString(), @@ -4343,8 +4559,9 @@ define("ember-extension-support/data_adapter", @return {Array} Array of model types */ getModelTypes: function() { - var types, self = this, - containerDebugAdapter = this.get('containerDebugAdapter'); + var self = this; + var containerDebugAdapter = this.get('containerDebugAdapter'); + var types; if (containerDebugAdapter.canCatalogEntriesByType('model')) { types = containerDebugAdapter.catalogEntriesByType('model'); @@ -4375,9 +4592,9 @@ define("ember-extension-support/data_adapter", @return {Array} Array of model type strings */ _getObjectsOnNamespaces: function() { - var namespaces = emberA(Namespace.NAMESPACES), - types = emberA(), - self = this; + var namespaces = emberA(Namespace.NAMESPACES); + var types = emberA(); + var self = this; namespaces.forEach(function(namespace) { for (var key in namespace) { @@ -4420,7 +4637,7 @@ define("ember-extension-support/data_adapter", searchKeywords: {Array} */ wrapRecord: function(record) { - var recordToSend = { object: record }, columnValues = {}, self = this; + var recordToSend = { object: record }; recordToSend.columnValues = this.getRecordColumnValues(record); recordToSend.searchKeywords = this.getRecordKeywords(record); @@ -4493,17 +4710,21 @@ define("ember-extension-support/data_adapter", } }); }); -define("ember-extension-support/initializers", +enifed("ember-extension-support/initializers", [], function() { "use strict"; }); -define("ember-handlebars-compiler", +enifed("ember-handlebars-compiler", ["ember-metal/core","exports"], function(__dependency1__, __exports__) { - "use strict"; - /* global Handlebars:true */ + /* global Handlebars:true */ + + // Remove "use strict"; from transpiled module (in browser builds only) until + // https://bugs.webkit.org/show_bug.cgi?id=138038 is fixed + // + // REMOVE_USE_STRICT: true /** @module ember @@ -4528,18 +4749,17 @@ define("ember-handlebars-compiler", // ES6Todo: when ember-debug is es6'ed import this. // var emberAssert = Ember.assert; var Handlebars = (Ember.imports && Ember.imports.Handlebars) || (this && this.Handlebars); - if (!Handlebars && typeof require === 'function') { - Handlebars = require('handlebars'); + if (!Handlebars && typeof eriuqer === 'function') { + Handlebars = eriuqer('handlebars'); } - Ember.assert("Ember Handlebars requires Handlebars version 1.0 or 1.1. Include " + + Ember.assert("Ember Handlebars requires Handlebars version 2.0. Include " + "a SCRIPT tag in the HTML HEAD linking to the Handlebars file " + "before you link to Ember.", Handlebars); - Ember.assert("Ember Handlebars requires Handlebars version 1.0 or 1.1, " + - "COMPILER_REVISION expected: 4, got: " + Handlebars.COMPILER_REVISION + - " - Please note: Builds of master may have other COMPILER_REVISION values.", - Handlebars.COMPILER_REVISION === 4); + Ember.assert("Ember Handlebars requires Handlebars version 2.0. " + + "Please see more details at http://emberjs.com/blog/2014/10/16/handlebars-update.html.", + Handlebars.COMPILER_REVISION === 6); /** Prepares the Handlebars templating library for use inside Ember's view @@ -4555,7 +4775,7 @@ define("ember-handlebars-compiler", @class Handlebars @namespace Ember */ - var EmberHandlebars = Ember.Handlebars = objectCreate(Handlebars); + var EmberHandlebars = Ember.Handlebars = Handlebars.create(); /** Register a bound helper or custom view helper. @@ -4598,7 +4818,7 @@ define("ember-handlebars-compiler", Which is functionally equivalent to: ```handlebars - {{view App.CalendarView}} + {{view 'calendar'}} ``` Options in the helper will be passed to the view in exactly the same @@ -4614,7 +4834,8 @@ define("ember-handlebars-compiler", if (!View) { View = requireModule('ember-views/views/view')['default']; } // ES6TODO: stupid circular dep if (!Component) { Component = requireModule('ember-views/views/component')['default']; } // ES6TODO: stupid circular dep - Ember.assert("You tried to register a component named '" + name + "', but component names must include a '-'", !Component.detect(value) || name.match(/-/)); + Ember.assert("You tried to register a component named '" + name + + "', but component names must include a '-'", !Component.detect(value) || name.match(/-/)); if (View.detect(value)) { EmberHandlebars.registerHelper(name, EmberHandlebars.makeViewHelper(value)); @@ -4637,7 +4858,8 @@ define("ember-handlebars-compiler", */ EmberHandlebars.makeViewHelper = function(ViewClass) { return function(options) { - Ember.assert("You can only pass attributes (such as name=value) not bare values to a helper for a View found in '" + ViewClass.toString() + "'", arguments.length < 2); + Ember.assert("You can only pass attributes (such as name=value) not bare " + + "values to a helper for a View found in '" + ViewClass.toString() + "'", arguments.length < 2); return EmberHandlebars.helpers.view.call(this, ViewClass, options); }; }; @@ -4699,44 +4921,6 @@ define("ember-handlebars-compiler", return "data.buffer.push("+string+");"; }; - // Hacks ahead: - // Handlebars presently has a bug where the `blockHelperMissing` hook - // doesn't get passed the name of the missing helper name, but rather - // gets passed the value of that missing helper evaluated on the current - // context, which is most likely `undefined` and totally useless. - // - // So we alter the compiled template function to pass the name of the helper - // instead, as expected. - // - // This can go away once the following is closed: - // https://github.com/wycats/handlebars.js/issues/634 - - var DOT_LOOKUP_REGEX = /helpers\.(.*?)\)/, - BRACKET_STRING_LOOKUP_REGEX = /helpers\['(.*?)'/, - INVOCATION_SPLITTING_REGEX = /(.*blockHelperMissing\.call\(.*)(stack[0-9]+)(,.*)/; - - EmberHandlebars.JavaScriptCompiler.stringifyLastBlockHelperMissingInvocation = function(source) { - var helperInvocation = source[source.length - 1], - helperName = (DOT_LOOKUP_REGEX.exec(helperInvocation) || BRACKET_STRING_LOOKUP_REGEX.exec(helperInvocation))[1], - matches = INVOCATION_SPLITTING_REGEX.exec(helperInvocation); - - source[source.length - 1] = matches[1] + "'" + helperName + "'" + matches[3]; - }; - - var stringifyBlockHelperMissing = EmberHandlebars.JavaScriptCompiler.stringifyLastBlockHelperMissingInvocation; - - var originalBlockValue = EmberHandlebars.JavaScriptCompiler.prototype.blockValue; - EmberHandlebars.JavaScriptCompiler.prototype.blockValue = function() { - originalBlockValue.apply(this, arguments); - stringifyBlockHelperMissing(this.source); - }; - - var originalAmbiguousBlockValue = EmberHandlebars.JavaScriptCompiler.prototype.ambiguousBlockValue; - EmberHandlebars.JavaScriptCompiler.prototype.ambiguousBlockValue = function() { - originalAmbiguousBlockValue.apply(this, arguments); - stringifyBlockHelperMissing(this.source); - }; - /** Rewrite simple mustaches from `{{foo}}` to `{{bind "foo"}}`. This means that all simple mustaches in Ember's Handlebars will also set up an observer to @@ -4771,12 +4955,12 @@ define("ember-handlebars-compiler", @method precompile @for Ember.Handlebars @static - @param {String} string The template to precompile + @param {String|Object} value The template to precompile or an Handlebars AST @param {Boolean} asObject optional parameter, defaulting to true, of whether or not the compiled template should be returned as an Object or a String */ - EmberHandlebars.precompile = function(string, asObject) { - var ast = Handlebars.parse(string); + EmberHandlebars.precompile = function(value, asObject) { + var ast = Handlebars.parse(value); var options = { knownHelpers: { @@ -4825,9 +5009,9 @@ define("ember-handlebars-compiler", __exports__["default"] = EmberHandlebars; }); -define("ember-handlebars", - ["ember-handlebars-compiler","ember-metal/core","ember-runtime/system/lazy_load","ember-handlebars/loader","ember-handlebars/ext","ember-handlebars/string","ember-handlebars/helpers/shared","ember-handlebars/helpers/binding","ember-handlebars/helpers/collection","ember-handlebars/helpers/view","ember-handlebars/helpers/unbound","ember-handlebars/helpers/debug","ember-handlebars/helpers/each","ember-handlebars/helpers/template","ember-handlebars/helpers/partial","ember-handlebars/helpers/yield","ember-handlebars/helpers/loc","ember-handlebars/controls/checkbox","ember-handlebars/controls/select","ember-handlebars/controls/text_area","ember-handlebars/controls/text_field","ember-handlebars/controls/text_support","ember-handlebars/controls","ember-handlebars/component_lookup","ember-handlebars/views/handlebars_bound_view","ember-handlebars/views/metamorph_view","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __dependency18__, __dependency19__, __dependency20__, __dependency21__, __dependency22__, __dependency23__, __dependency24__, __dependency25__, __dependency26__, __exports__) { +enifed("ember-handlebars", + ["ember-handlebars-compiler","ember-metal/core","ember-runtime/system/lazy_load","ember-handlebars/loader","ember-handlebars/ext","ember-handlebars/string","ember-handlebars/helpers/binding","ember-handlebars/helpers/if_unless","ember-handlebars/helpers/with","ember-handlebars/helpers/bind_attr","ember-handlebars/helpers/collection","ember-handlebars/helpers/view","ember-handlebars/helpers/unbound","ember-handlebars/helpers/debug","ember-handlebars/helpers/each","ember-handlebars/helpers/template","ember-handlebars/helpers/partial","ember-handlebars/helpers/yield","ember-handlebars/helpers/loc","ember-handlebars/controls/checkbox","ember-handlebars/controls/select","ember-handlebars/controls/text_area","ember-handlebars/controls/text_field","ember-handlebars/controls/text_support","ember-handlebars/controls","ember-handlebars/component_lookup","ember-handlebars/views/handlebars_bound_view","ember-handlebars/views/metamorph_view","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __dependency18__, __dependency19__, __dependency20__, __dependency21__, __dependency22__, __dependency23__, __dependency24__, __dependency25__, __dependency26__, __dependency27__, __dependency28__, __exports__) { "use strict"; var EmberHandlebars = __dependency1__["default"]; var Ember = __dependency2__["default"]; @@ -4836,68 +5020,61 @@ define("ember-handlebars", var runLoadHooks = __dependency3__.runLoadHooks; var bootstrap = __dependency4__["default"]; - var normalizePath = __dependency5__.normalizePath; - var template = __dependency5__.template; var makeBoundHelper = __dependency5__.makeBoundHelper; var registerBoundHelper = __dependency5__.registerBoundHelper; - var resolveHash = __dependency5__.resolveHash; - var resolveParams = __dependency5__.resolveParams; - var getEscaped = __dependency5__.getEscaped; - var handlebarsGet = __dependency5__.handlebarsGet; - var evaluateUnboundHelper = __dependency5__.evaluateUnboundHelper; var helperMissingHelper = __dependency5__.helperMissingHelper; var blockHelperMissingHelper = __dependency5__.blockHelperMissingHelper; + var handlebarsGet = __dependency5__.handlebarsGet; // side effect of extending StringUtils of htmlSafe - var resolvePaths = __dependency7__["default"]; - var bind = __dependency8__.bind; - var _triageMustacheHelper = __dependency8__._triageMustacheHelper; - var resolveHelper = __dependency8__.resolveHelper; - var bindHelper = __dependency8__.bindHelper; + var bind = __dependency7__.bind; + var _triageMustacheHelper = __dependency7__._triageMustacheHelper; + var resolveHelper = __dependency7__.resolveHelper; + var bindHelper = __dependency7__.bindHelper; + + var ifHelper = __dependency8__.ifHelper; var boundIfHelper = __dependency8__.boundIfHelper; var unboundIfHelper = __dependency8__.unboundIfHelper; - var withHelper = __dependency8__.withHelper; - var ifHelper = __dependency8__.ifHelper; var unlessHelper = __dependency8__.unlessHelper; - var bindAttrHelper = __dependency8__.bindAttrHelper; - var bindAttrHelperDeprecated = __dependency8__.bindAttrHelperDeprecated; - var bindClasses = __dependency8__.bindClasses; - var collectionHelper = __dependency9__["default"]; - var ViewHelper = __dependency10__.ViewHelper; - var viewHelper = __dependency10__.viewHelper; - var unboundHelper = __dependency11__["default"]; - var logHelper = __dependency12__.logHelper; - var debuggerHelper = __dependency12__.debuggerHelper; - var EachView = __dependency13__.EachView; - var GroupedEach = __dependency13__.GroupedEach; - var eachHelper = __dependency13__.eachHelper; - var templateHelper = __dependency14__["default"]; - var partialHelper = __dependency15__["default"]; - var yieldHelper = __dependency16__["default"]; - var locHelper = __dependency17__["default"]; + var withHelper = __dependency9__["default"]; + + var bindAttrHelper = __dependency10__.bindAttrHelper; + var bindAttrHelperDeprecated = __dependency10__.bindAttrHelperDeprecated; + var bindClasses = __dependency10__.bindClasses; + + var collectionHelper = __dependency11__["default"]; + var ViewHelper = __dependency12__.ViewHelper; + var viewHelper = __dependency12__.viewHelper; + var unboundHelper = __dependency13__["default"]; + var logHelper = __dependency14__.logHelper; + var debuggerHelper = __dependency14__.debuggerHelper; + var EachView = __dependency15__.EachView; + var eachHelper = __dependency15__.eachHelper; + var templateHelper = __dependency16__["default"]; + var partialHelper = __dependency17__["default"]; + var yieldHelper = __dependency18__["default"]; + var locHelper = __dependency19__["default"]; - var Checkbox = __dependency18__["default"]; - var Select = __dependency19__.Select; - var SelectOption = __dependency19__.SelectOption; - var SelectOptgroup = __dependency19__.SelectOptgroup; - var TextArea = __dependency20__["default"]; - var TextField = __dependency21__["default"]; - var TextSupport = __dependency22__["default"]; - var inputHelper = __dependency23__.inputHelper; - var textareaHelper = __dependency23__.textareaHelper; + var Checkbox = __dependency20__["default"]; + var Select = __dependency21__.Select; + var SelectOption = __dependency21__.SelectOption; + var SelectOptgroup = __dependency21__.SelectOptgroup; + var TextArea = __dependency22__["default"]; + var TextField = __dependency23__["default"]; + var TextSupport = __dependency24__["default"]; + var inputHelper = __dependency25__.inputHelper; + var textareaHelper = __dependency25__.textareaHelper; - - var ComponentLookup = __dependency24__["default"]; - var _HandlebarsBoundView = __dependency25__._HandlebarsBoundView; - var SimpleHandlebarsView = __dependency25__.SimpleHandlebarsView; - var _wrapMap = __dependency26__._wrapMap; - var _SimpleMetamorphView = __dependency26__._SimpleMetamorphView; - var _MetamorphView = __dependency26__._MetamorphView; - var _Metamorph = __dependency26__._Metamorph; + var ComponentLookup = __dependency26__["default"]; + var _HandlebarsBoundView = __dependency27__._HandlebarsBoundView; + var SimpleHandlebarsView = __dependency27__.SimpleHandlebarsView; + var _MetamorphView = __dependency28__["default"]; + var _SimpleMetamorphView = __dependency28__._SimpleMetamorphView; + var _Metamorph = __dependency28__._Metamorph; /** @@ -4910,33 +5087,24 @@ define("ember-handlebars", // Ember.Handlebars.Globals EmberHandlebars.bootstrap = bootstrap; - EmberHandlebars.template = template; EmberHandlebars.makeBoundHelper = makeBoundHelper; EmberHandlebars.registerBoundHelper = registerBoundHelper; - EmberHandlebars.resolveHash = resolveHash; - EmberHandlebars.resolveParams = resolveParams; EmberHandlebars.resolveHelper = resolveHelper; - EmberHandlebars.get = handlebarsGet; - EmberHandlebars.getEscaped = getEscaped; - EmberHandlebars.evaluateUnboundHelper = evaluateUnboundHelper; EmberHandlebars.bind = bind; EmberHandlebars.bindClasses = bindClasses; EmberHandlebars.EachView = EachView; - EmberHandlebars.GroupedEach = GroupedEach; - EmberHandlebars.resolvePaths = resolvePaths; EmberHandlebars.ViewHelper = ViewHelper; - EmberHandlebars.normalizePath = normalizePath; // Ember Globals Ember.Handlebars = EmberHandlebars; + EmberHandlebars.get = handlebarsGet; Ember.ComponentLookup = ComponentLookup; Ember._SimpleHandlebarsView = SimpleHandlebarsView; Ember._HandlebarsBoundView = _HandlebarsBoundView; Ember._SimpleMetamorphView = _SimpleMetamorphView; Ember._MetamorphView = _MetamorphView; Ember._Metamorph = _Metamorph; - Ember._metamorphWrapMap = _wrapMap; Ember.TextSupport = TextSupport; Ember.Checkbox = Checkbox; Ember.Select = Select; @@ -4976,20 +5144,20 @@ define("ember-handlebars", __exports__["default"] = EmberHandlebars; }); -define("ember-handlebars/component_lookup", +enifed("ember-handlebars/component_lookup", ["ember-runtime/system/object","exports"], function(__dependency1__, __exports__) { "use strict"; var EmberObject = __dependency1__["default"]; - var ComponentLookup = EmberObject.extend({ + __exports__["default"] = EmberObject.extend({ lookupFactory: function(name, container) { container = container || this.container; - var fullName = 'component:' + name, - templateFullName = 'template:components/' + name, - templateRegistered = container && container.has(templateFullName); + var fullName = 'component:' + name; + var templateFullName = 'template:components/' + name; + var templateRegistered = container && container.has(templateFullName); if (templateRegistered) { container.injection(fullName, 'layout', templateFullName); @@ -5008,12 +5176,10 @@ define("ember-handlebars/component_lookup", } } }); - - __exports__["default"] = ComponentLookup; }); -define("ember-handlebars/controls", - ["ember-handlebars/controls/checkbox","ember-handlebars/controls/text_field","ember-handlebars/controls/text_area","ember-metal/core","ember-handlebars-compiler","ember-handlebars/ext","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) { +enifed("ember-handlebars/controls", + ["ember-handlebars/controls/checkbox","ember-handlebars/controls/text_field","ember-handlebars/controls/text_area","ember-metal/core","ember-handlebars-compiler","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { "use strict"; var Checkbox = __dependency1__["default"]; var TextField = __dependency2__["default"]; @@ -5024,21 +5190,12 @@ define("ember-handlebars/controls", // var emberAssert = Ember.assert; var EmberHandlebars = __dependency5__["default"]; - var handlebarsGet = __dependency6__.handlebarsGet; - var helpers = EmberHandlebars.helpers; + /** @module ember @submodule ember-handlebars-compiler */ - function _resolveOption(context, options, key) { - if (options.hashTypes[key] === "ID") { - return handlebarsGet(context, options.hash[key], options); - } else { - return options.hash[key]; - } - } - /** The `{{input}}` helper inserts an HTML `` tag into the template, @@ -5102,13 +5259,15 @@ define("ember-handlebars/controls", The helper can send multiple actions based on user events. - The action property defines the action which is send when + The action property defines the action which is sent when the user presses the return key. + ```handlebars {{input action="submit"}} ``` + The helper allows some user events to send actions. * `enter` @@ -5118,13 +5277,16 @@ define("ember-handlebars/controls", * `focus-out` * `key-press` + For example, if you desire an action to be sent when the input is blurred, you only need to setup the action name to the event name property. + ```handlebars {{input focus-in="alertMessage"}} ``` + See more about [Text Support Actions](/api/classes/Ember.TextField.html) ## Extension @@ -5217,23 +5379,31 @@ define("ember-handlebars/controls", function inputHelper(options) { Ember.assert('You can only pass attributes to the `input` helper, not arguments', arguments.length < 2); - var hash = options.hash, - types = options.hashTypes, - inputType = _resolveOption(this, options, 'type'), - onEvent = hash.on; + var view = options.data.view; + var hash = options.hash; + var types = options.hashTypes; + var onEvent = hash.on; + var inputType; + + if (types.type === 'ID') { + inputType = view.getStream(hash.type).value(); + } else { + inputType = hash.type; + } if (inputType === 'checkbox') { delete hash.type; delete types.type; - Ember.assert("{{input type='checkbox'}} does not support setting `value=someBooleanValue`; you must use `checked=someBooleanValue` instead.", options.hashTypes.value !== 'ID'); + Ember.assert("{{input type='checkbox'}} does not support setting `value=someBooleanValue`;" + + " you must use `checked=someBooleanValue` instead.", options.hashTypes.value !== 'ID'); - return helpers.view.call(this, Checkbox, options); + return EmberHandlebars.helpers.view.call(this, Checkbox, options); } else { delete hash.on; hash.onEvent = onEvent || 'enter'; - return helpers.view.call(this, TextField, options); + return EmberHandlebars.helpers.view.call(this, TextField, options); } } @@ -5400,7 +5570,7 @@ define("ember-handlebars/controls", Internally, `{{textarea}}` creates an instance of `Ember.TextArea`, passing arguments from the helper to `Ember.TextArea`'s `create` method. You can extend the capabilities of text areas in your application by reopening this - class. For example, if you are building a Bootstrap project where `data-*` + class. For example, if you are building a Bootstrap project where `data-*` attributes are used, you can globally add support for a `data-*` attribute on all `{{textarea}}`s' in your app by reopening `Ember.TextArea` or `Ember.TextSupport` and adding it to the `attributeBindings` concatenated @@ -5425,15 +5595,12 @@ define("ember-handlebars/controls", function textareaHelper(options) { Ember.assert('You can only pass attributes to the `textarea` helper, not arguments', arguments.length < 2); - var hash = options.hash, - types = options.hashTypes; - - return helpers.view.call(this, TextArea, options); + return EmberHandlebars.helpers.view.call(this, TextArea, options); } __exports__.textareaHelper = textareaHelper; }); -define("ember-handlebars/controls/checkbox", +enifed("ember-handlebars/controls/checkbox", ["ember-metal/property_get","ember-metal/property_set","ember-views/views/view","exports"], function(__dependency1__, __dependency2__, __dependency3__, __exports__) { "use strict"; @@ -5509,7 +5676,7 @@ define("ember-handlebars/controls/checkbox", } }); }); -define("ember-handlebars/controls/select", +enifed("ember-handlebars/controls/select", ["ember-handlebars-compiler","ember-metal/enumerable_utils","ember-metal/property_get","ember-metal/property_set","ember-views/views/view","ember-views/views/collection_view","ember-metal/utils","ember-metal/is_none","ember-metal/computed","ember-runtime/system/native_array","ember-metal/mixin","ember-metal/properties","exports"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __exports__) { "use strict"; @@ -5536,7 +5703,6 @@ define("ember-handlebars/controls/select", var observer = __dependency11__.observer; var defineProperty = __dependency12__.defineProperty; - var precompileTemplate = EmberHandlebars.compile; var SelectOption = View.extend({ instrumentDisplay: 'Ember.SelectOption', @@ -5557,8 +5723,8 @@ define("ember-handlebars/controls/select", }, selected: computed(function() { - var content = get(this, 'content'), - selection = get(this, 'parentView.selection'); + var content = get(this, 'content'); + var selection = get(this, 'parentView.selection'); if (get(this, 'parentView.multiple')) { return selection && indexOf(selection, content.valueOf()) > -1; } else { @@ -5628,7 +5794,7 @@ define("ember-handlebars/controls/select", ``` ```handlebars - {{view Ember.Select content=names}} + {{view "select" content=names}} ``` Would result in the following HTML: @@ -5651,10 +5817,7 @@ define("ember-handlebars/controls/select", ``` ```handlebars - {{view Ember.Select - content=names - value=selectedName - }} + {{view "select" content=names value=selectedName}} ``` Would result in the following HTML with the `"); return buffer; - } - - function program3(depth0,data) { - + },"3":function(depth0,helpers,partials,data) { var stack1; - stack1 = helpers.each.call(depth0, "view.groupedContent", {hash:{},hashTypes:{},hashContexts:{},inverse:self.noop,fn:self.program(4, program4, data),contexts:[depth0],types:["ID"],data:data}); - if(stack1 || stack1 === 0) { data.buffer.push(stack1); } + stack1 = helpers.each.call(depth0, "group", "in", "view.groupedContent", {"name":"each","hash":{},"hashTypes":{},"hashContexts":{},"fn":this.program(4, data),"inverse":this.noop,"types":["ID","ID","ID"],"contexts":[depth0,depth0,depth0],"data":data}); + if (stack1 != null) { data.buffer.push(stack1); } else { data.buffer.push(''); } - } - function program4(depth0,data) { - - - data.buffer.push(escapeExpression(helpers.view.call(depth0, "view.groupView", {hash:{ - 'content': ("content"), - 'label': ("label") - },hashTypes:{'content': "ID",'label': "ID"},hashContexts:{'content': depth0,'label': depth0},contexts:[depth0],types:["ID"],data:data}))); - } - - function program6(depth0,data) { - + },"4":function(depth0,helpers,partials,data) { + var escapeExpression=this.escapeExpression; + data.buffer.push(escapeExpression(helpers.view.call(depth0, "view.groupView", {"name":"view","hash":{ + 'label': ("group.label"), + 'content': ("group.content") + },"hashTypes":{'label': "ID",'content': "ID"},"hashContexts":{'label': depth0,'content': depth0},"types":["ID"],"contexts":[depth0],"data":data}))); + },"6":function(depth0,helpers,partials,data) { var stack1; - stack1 = helpers.each.call(depth0, "view.content", {hash:{},hashTypes:{},hashContexts:{},inverse:self.noop,fn:self.program(7, program7, data),contexts:[depth0],types:["ID"],data:data}); - if(stack1 || stack1 === 0) { data.buffer.push(stack1); } + stack1 = helpers.each.call(depth0, "item", "in", "view.content", {"name":"each","hash":{},"hashTypes":{},"hashContexts":{},"fn":this.program(7, data),"inverse":this.noop,"types":["ID","ID","ID"],"contexts":[depth0,depth0,depth0],"data":data}); + if (stack1 != null) { data.buffer.push(stack1); } else { data.buffer.push(''); } - } - function program7(depth0,data) { - - - data.buffer.push(escapeExpression(helpers.view.call(depth0, "view.optionView", {hash:{ - 'content': ("") - },hashTypes:{'content': "ID"},hashContexts:{'content': depth0},contexts:[depth0],types:["ID"],data:data}))); - } - - stack1 = helpers['if'].call(depth0, "view.prompt", {hash:{},hashTypes:{},hashContexts:{},inverse:self.noop,fn:self.program(1, program1, data),contexts:[depth0],types:["ID"],data:data}); - if(stack1 || stack1 === 0) { data.buffer.push(stack1); } - stack1 = helpers['if'].call(depth0, "view.optionGroupPath", {hash:{},hashTypes:{},hashContexts:{},inverse:self.program(6, program6, data),fn:self.program(3, program3, data),contexts:[depth0],types:["ID"],data:data}); - if(stack1 || stack1 === 0) { data.buffer.push(stack1); } + },"7":function(depth0,helpers,partials,data) { + var escapeExpression=this.escapeExpression; + data.buffer.push(escapeExpression(helpers.view.call(depth0, "view.optionView", {"name":"view","hash":{ + 'content': ("item") + },"hashTypes":{'content': "ID"},"hashContexts":{'content': depth0},"types":["ID"],"contexts":[depth0],"data":data}))); + },"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) { + var stack1, buffer = ''; + stack1 = helpers['if'].call(depth0, "view.prompt", {"name":"if","hash":{},"hashTypes":{},"hashContexts":{},"fn":this.program(1, data),"inverse":this.noop,"types":["ID"],"contexts":[depth0],"data":data}); + if (stack1 != null) { data.buffer.push(stack1); } + stack1 = helpers['if'].call(depth0, "view.optionGroupPath", {"name":"if","hash":{},"hashTypes":{},"hashContexts":{},"fn":this.program(3, data),"inverse":this.program(6, data),"types":["ID"],"contexts":[depth0],"data":data}); + if (stack1 != null) { data.buffer.push(stack1); } return buffer; - - }), + },"useData":true}), attributeBindings: ['multiple', 'disabled', 'tabindex', 'name', 'required', 'autofocus', 'form', 'size'], @@ -5956,7 +6093,8 @@ define("ember-handlebars/controls/select", Otherwise, this should be a list of objects. For instance: ```javascript - Ember.Select.create({ + var App = Ember.Application.create(); + var App.MySelect = Ember.Select.extend({ content: Ember.A([ { id: 1, firstName: 'Yehuda' }, { id: 2, firstName: 'Tom' } @@ -6099,11 +6237,11 @@ define("ember-handlebars/controls/select", }), valueDidChange: observer('value', function() { - var content = get(this, 'content'), - value = get(this, 'value'), - valuePath = get(this, 'optionValuePath').replace(/^content\.?/, ''), - selectedValue = (valuePath ? get(this, 'selection.' + valuePath) : get(this, 'selection')), - selection; + var content = get(this, 'content'); + var value = get(this, 'value'); + var valuePath = get(this, 'optionValuePath').replace(/^content\.?/, ''); + var selectedValue = (valuePath ? get(this, 'selection.' + valuePath) : get(this, 'selection')); + var selection; if (value !== selectedValue) { selection = content ? content.find(function(obj) { @@ -6126,9 +6264,9 @@ define("ember-handlebars/controls/select", }, _changeSingle: function() { - var selectedIndex = this.$()[0].selectedIndex, - content = get(this, 'content'), - prompt = get(this, 'prompt'); + var selectedIndex = this.$()[0].selectedIndex; + var content = get(this, 'content'); + var prompt = get(this, 'prompt'); if (!content || !get(content, 'length')) { return; } if (prompt && selectedIndex === 0) { set(this, 'selection', null); return; } @@ -6139,11 +6277,11 @@ define("ember-handlebars/controls/select", _changeMultiple: function() { - var options = this.$('option:selected'), - prompt = get(this, 'prompt'), - offset = prompt ? 1 : 0, - content = get(this, 'content'), - selection = get(this, 'selection'); + var options = this.$('option:selected'); + var prompt = get(this, 'prompt'); + var offset = prompt ? 1 : 0; + var content = get(this, 'content'); + var selection = get(this, 'selection'); if (!content) { return; } if (options) { @@ -6164,23 +6302,23 @@ define("ember-handlebars/controls/select", var el = this.get('element'); if (!el) { return; } - var content = get(this, 'content'), - selection = get(this, 'selection'), - selectionIndex = content ? indexOf(content, selection) : -1, - prompt = get(this, 'prompt'); + var content = get(this, 'content'); + var selection = get(this, 'selection'); + var selectionIndex = content ? indexOf(content, selection) : -1; + var prompt = get(this, 'prompt'); if (prompt) { selectionIndex += 1; } if (el) { el.selectedIndex = selectionIndex; } }, _selectionDidChangeMultiple: function() { - var content = get(this, 'content'), - selection = get(this, 'selection'), - selectedIndexes = content ? indexesOf(content, selection) : [-1], - prompt = get(this, 'prompt'), - offset = prompt ? 1 : 0, - options = this.$('option'), - adjusted; + var content = get(this, 'content'); + var selection = get(this, 'selection'); + var selectedIndexes = content ? indexesOf(content, selection) : [-1]; + var prompt = get(this, 'prompt'); + var offset = prompt ? 1 : 0; + var options = this.$('option'); + var adjusted; if (options) { options.each(function() { @@ -6202,7 +6340,7 @@ define("ember-handlebars/controls/select", __exports__.SelectOption = SelectOption; __exports__.SelectOptgroup = SelectOptgroup; }); -define("ember-handlebars/controls/text_area", +enifed("ember-handlebars/controls/text_area", ["ember-metal/property_get","ember-views/views/component","ember-handlebars/controls/text_support","ember-metal/mixin","exports"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { "use strict"; @@ -6239,14 +6377,23 @@ define("ember-handlebars/controls/text_area", classNames: ['ember-text-area'], tagName: "textarea", - attributeBindings: ['rows', 'cols', 'name', 'selectionEnd', 'selectionStart', 'wrap'], + attributeBindings: [ + 'rows', + 'cols', + 'name', + 'selectionEnd', + 'selectionStart', + 'wrap', + 'lang', + 'dir' + ], rows: null, cols: null, _updateElementValue: observer('value', function() { // We do this check so cursor position doesn't get affected in IE - var value = get(this, 'value'), - $el = this.$(); + var value = get(this, 'value'); + var $el = this.$(); if ($el && value !== $el.val()) { $el.val(value); } @@ -6258,19 +6405,16 @@ define("ember-handlebars/controls/text_area", } }); }); -define("ember-handlebars/controls/text_field", - ["ember-metal/property_get","ember-metal/property_set","ember-views/views/component","ember-handlebars/controls/text_support","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { +enifed("ember-handlebars/controls/text_field", + ["ember-views/views/component","ember-handlebars/controls/text_support","exports"], + function(__dependency1__, __dependency2__, __exports__) { "use strict"; /** @module ember @submodule ember-handlebars */ - - var get = __dependency1__.get; - var set = __dependency2__.set; - var Component = __dependency3__["default"]; - var TextSupport = __dependency4__["default"]; + var Component = __dependency1__["default"]; + var TextSupport = __dependency2__["default"]; /** @@ -6295,11 +6439,31 @@ define("ember-handlebars/controls/text_field", classNames: ['ember-text-field'], tagName: "input", - attributeBindings: ['type', 'value', 'size', 'pattern', 'name', 'min', 'max', - 'accept', 'autocomplete', 'autosave', 'formaction', - 'formenctype', 'formmethod', 'formnovalidate', 'formtarget', - 'height', 'inputmode', 'list', 'multiple', 'step', - 'width'], + attributeBindings: [ + 'accept', + 'autocomplete', + 'autosave', + 'dir', + 'formaction', + 'formenctype', + 'formmethod', + 'formnovalidate', + 'formtarget', + 'height', + 'inputmode', + 'lang', + 'list', + 'max', + 'min', + 'multiple', + 'name', + 'pattern', + 'size', + 'step', + 'type', + 'value', + 'width' + ], /** The `value` attribute of the input element. As the user inputs text, this @@ -6359,7 +6523,7 @@ define("ember-handlebars/controls/text_field", max: null }); }); -define("ember-handlebars/controls/text_support", +enifed("ember-handlebars/controls/text_support", ["ember-metal/property_get","ember-metal/property_set","ember-metal/mixin","ember-runtime/mixins/target_action_support","exports"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { "use strict"; @@ -6385,21 +6549,30 @@ define("ember-handlebars/controls/text_support", var TextSupport = Mixin.create(TargetActionSupport, { value: "", - attributeBindings: ['placeholder', 'disabled', 'maxlength', 'tabindex', 'readonly', - 'autofocus', 'form', 'selectionDirection', 'spellcheck', 'required', - 'title', 'autocapitalize', 'autocorrect'], + attributeBindings: [ + 'autocapitalize', + 'autocorrect', + 'autofocus', + 'disabled', + 'form', + 'maxlength', + 'placeholder', + 'readonly', + 'required', + 'selectionDirection', + 'spellcheck', + 'tabindex', + 'title' + ], placeholder: null, disabled: false, maxlength: null, init: function() { this._super(); - this.on("focusOut", this, this._elementValueDidChange); - this.on("change", this, this._elementValueDidChange); this.on("paste", this, this._elementValueDidChange); this.on("cut", this, this._elementValueDidChange); this.on("input", this, this._elementValueDidChange); - this.on("keyUp", this, this.interpretKeyEvents); }, /** @@ -6430,7 +6603,7 @@ define("ember-handlebars/controls/text_support", onEvent: 'enter', /** - Whether they `keyUp` event that triggers an `action` to be sent continues + Whether the `keyUp` event that triggers an `action` to be sent continues propagating to other views. By default, when the user presses the return key on their keyboard and @@ -6485,6 +6658,10 @@ define("ember-handlebars/controls/text_support", sendAction('escape-press', this, event); }, + change: function(event) { + this._elementValueDidChange(event); + }, + /** Called when the text area is focused. @@ -6498,7 +6675,7 @@ define("ember-handlebars/controls/text_support", }, /** - Called when the text area is blurred. + Called when the text area is blurred. Uses sendAction to send the `focus-out` action. @@ -6506,6 +6683,7 @@ define("ember-handlebars/controls/text_support", @param {Event} event */ focusOut: function(event) { + this._elementValueDidChange(event); sendAction('focus-out', this, event); }, @@ -6520,8 +6698,36 @@ define("ember-handlebars/controls/text_support", */ keyPress: function(event) { sendAction('key-press', this, event); - } + }, + /** + Called when the browser triggers a `keyup` event on the element. + + Uses sendAction to send the `key-up` action passing the current value + and event as parameters. + + @method keyUp + @param {Event} event + */ + keyUp: function(event) { + this.interpretKeyEvents(event); + + this.sendAction('key-up', get(this, 'value'), event); + }, + + /** + Called when the browser triggers a `keydown` event on the element. + + Uses sendAction to send the `key-down` action passing the current value + and event as parameters. Note that generally in key-down the value is unchanged + (as the key pressing has not completed yet). + + @method keyDown + @param {Event} event + */ + keyDown: function(event) { + this.sendAction('key-down', get(this, 'value'), event); + } }); TextSupport.KEY_EVENTS = { @@ -6533,9 +6739,9 @@ define("ember-handlebars/controls/text_support", // sendAction semantics for TextField are different from // the component semantics so this method normalizes them. function sendAction(eventName, view, event) { - var action = get(view, eventName), - on = get(view, 'onEvent'), - value = get(view, 'value'); + var action = get(view, eventName); + var on = get(view, 'onEvent'); + var value = get(view, 'value'); // back-compat support for keyPress as an event name even though // it's also a method name that consumes the event (and therefore @@ -6555,9 +6761,9 @@ define("ember-handlebars/controls/text_support", __exports__["default"] = TextSupport; }); -define("ember-handlebars/ext", - ["ember-metal/core","ember-runtime/system/string","ember-handlebars-compiler","ember-metal/property_get","ember-metal/binding","ember-metal/error","ember-metal/mixin","ember-metal/is_empty","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) { +enifed("ember-handlebars/ext", + ["ember-metal/core","ember-runtime/system/string","ember-handlebars-compiler","ember-metal/property_get","ember-metal/error","ember-metal/mixin","ember-views/views/view","ember-metal/path_cache","ember-metal/streams/stream","ember-metal/streams/read","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __exports__) { "use strict"; var Ember = __dependency1__["default"]; // Ember.FEATURES, Ember.assert, Ember.Handlebars, Ember.lookup @@ -6566,62 +6772,22 @@ define("ember-handlebars/ext", var fmt = __dependency2__.fmt; var EmberHandlebars = __dependency3__["default"]; - var helpers = EmberHandlebars.helpers; var get = __dependency4__.get; - var isGlobalPath = __dependency5__.isGlobalPath; - var EmberError = __dependency6__["default"]; - var IS_BINDING = __dependency7__.IS_BINDING; + var EmberError = __dependency5__["default"]; + var IS_BINDING = __dependency6__.IS_BINDING; + + var View = __dependency7__["default"]; + var detectIsGlobal = __dependency8__.isGlobal; // late bound via requireModule because of circular dependencies. - var resolveHelper, - SimpleHandlebarsView; + var resolveHelper, SimpleHandlebarsView; - var isEmpty = __dependency8__["default"]; - - var slice = [].slice, originalTemplate = EmberHandlebars.template; - - /** - If a path starts with a reserved keyword, returns the root - that should be used. - - @private - @method normalizePath - @for Ember - @param root {Object} - @param path {String} - @param data {Hash} - */ - function normalizePath(root, path, data) { - var keywords = (data && data.keywords) || {}, - keyword, isKeyword; - - // Get the first segment of the path. For example, if the - // path is "foo.bar.baz", returns "foo". - keyword = path.split('.', 1)[0]; - - // Test to see if the first path is a keyword that has been - // passed along in the view's data hash. If so, we will treat - // that object as the new root. - if (keywords.hasOwnProperty(keyword)) { - // Look up the value in the template's data hash. - root = keywords[keyword]; - isKeyword = true; - - // Handle cases where the entire path is the reserved - // word. In that case, return the object itself. - if (path === keyword) { - path = ''; - } else { - // Strip the keyword from the path and look up - // the remainder from the newly found root. - path = path.substr(keyword.length+1); - } - } - - return { root: root, path: path, isKeyword: isKeyword }; - } + var Stream = __dependency9__["default"]; + var readArray = __dependency10__.readArray; + var readHash = __dependency10__.readHash; + var slice = [].slice; /** Lookup both on root and on window. If the path starts with @@ -6633,90 +6799,98 @@ define("ember-handlebars/ext", @param {Object} root The object to look up the property on @param {String} path The path to be lookedup @param {Object} options The template's option hash + @deprecated */ function handlebarsGet(root, path, options) { - var data = options && options.data, - normalizedPath = normalizePath(root, path, data), - value; + Ember.deprecate('Usage of Ember.Handlebars.get is deprecated, use a Component or Ember.Handlebars.makeBoundHelper instead.'); - - root = normalizedPath.root; - path = normalizedPath.path; + return options.data.view.getStream(path).value(); + } - value = get(root, path); + /** + handlebarsGetView resolves a view based on strings passed into a template. + For example: - if (value === undefined && root !== Ember.lookup && isGlobalPath(path)) { - value = get(Ember.lookup, path); + ```handlebars + {{view "some-view"}} + {{view view.someView}} + {{view App.SomeView}} {{! deprecated }} + ``` + + A value is first checked to be a string- non-strings are presumed to be + an object and returned. This handles the "access a view on a context" + case (line 2 in the above examples). + + Next a string is normalized, then called on the context with `get`. If + there is still no value, a GlobalPath will be fetched from the global + context (raising a deprecation) and a localPath will be passed to the + container to be looked up. + + @private + @for Ember.Handlebars + @param {Object} context The context of the template being rendered + @param {String} path The path to be lookedup + @param {Object} container The container + @param {Object} data The template's data hash + */ + function handlebarsGetView(context, path, container, data) { + var viewClass; + if ('string' === typeof path) { + if (!data) { + throw new Error("handlebarsGetView: must pass data"); } - + + // Only lookup view class on context if there is a context. If not, + // the global lookup path on get may kick in. + var lazyValue = data.view.getStream(path); + viewClass = lazyValue.value(); + var isGlobal = detectIsGlobal(path); + + if (!viewClass && !isGlobal) { + Ember.assert("View requires a container to resolve views not passed in through the context", !!container); + viewClass = container.lookupFactory('view:'+path); + } + if (!viewClass && isGlobal) { + var globalViewClass = get(path); + Ember.deprecate('Resolved the view "'+path+'" on the global context. Pass a view name to be looked' + + ' up on the container instead, such as {{view "select"}}.' + + ' http://emberjs.com/guides/deprecations#toc_global-lookup-of-views', !globalViewClass); + if (globalViewClass) { + viewClass = globalViewClass; + } + } + } else { + viewClass = path; + } + + // Sometimes a view's value is yet another path + if ('string' === typeof viewClass && data && data.view) { + viewClass = handlebarsGetView(data.view, viewClass, container, data); + } + + Ember.assert( + fmt(path+" must be a subclass of Ember.View, not %@", [viewClass]), + View.detect(viewClass) + ); + + return viewClass; + } + + function stringifyValue(value, shouldEscape) { + if (value === null || value === undefined) { + value = ""; + } else if (!(value instanceof Handlebars.SafeString)) { + value = String(value); + } + + if (shouldEscape) { + value = Handlebars.Utils.escapeExpression(value); + } return value; } - /** - This method uses `Ember.Handlebars.get` to lookup a value, then ensures - that the value is escaped properly. - - If `unescaped` is a truthy value then the escaping will not be performed. - - @method getEscaped - @for Ember.Handlebars - @param {Object} root The object to look up the property on - @param {String} path The path to be lookedup - @param {Object} options The template's option hash - @since 1.4.0 - */ - function getEscaped(root, path, options) { - var result = handlebarsGet(root, path, options); - - if (result === null || result === undefined) { - result = ""; - } else if (!(result instanceof Handlebars.SafeString)) { - result = String(result); - } - if (!options.hash.unescaped){ - result = Handlebars.Utils.escapeExpression(result); - } - - return result; - } - - __exports__.getEscaped = getEscaped;function resolveParams(context, params, options) { - var resolvedParams = [], types = options.types, param, type; - - for (var i=0, l=params.length; i 0) { + var firstParam = params[0]; + // Only bother with subscriptions if the first argument + // is a stream itself, and not a primitive. + if (firstParam && firstParam.isStream) { + var onDependentKeyNotify = function onDependentKeyNotify(stream) { + stream.value(); + lazyValue.notify(); + }; + for (i = 0; i < dependentKeys.length; i++) { + var childParam = firstParam.get(dependentKeys[i]); + childParam.value(); + childParam.subscribe(onDependentKeyNotify); + } + } + } } } - helper._rawFunction = fn; return helper; } - /** - Renders the unbound form of an otherwise bound helper function. - - @private - @method evaluateUnboundHelper - @param {Function} fn - @param {Object} context - @param {Array} normalizedProperties - @param {String} options - */ - function evaluateUnboundHelper(context, fn, normalizedProperties, options) { - var args = [], - hash = options.hash, - boundOptions = hash.boundOptions, - types = slice.call(options.types, 1), - loc, - len, - property, - propertyType, - boundOption; - - for (boundOption in boundOptions) { - if (!boundOptions.hasOwnProperty(boundOption)) { continue; } - hash[boundOption] = handlebarsGet(context, boundOptions[boundOption], options); - } - - for(loc = 0, len = normalizedProperties.length; loc < len; ++loc) { - property = normalizedProperties[loc]; - propertyType = types[loc]; - if(propertyType === "ID") { - args.push(handlebarsGet(property.root, property.path, options)); - } else { - args.push(property.path); - } - } - args.push(options); - return fn.apply(context, args); - } - - /** - Overrides Handlebars.template so that we can distinguish - user-created, top-level templates from inner contexts. - - @private - @method template - @for Ember.Handlebars - @param {String} spec - */ - function template(spec) { - var t = originalTemplate(spec); - t.isTop = true; - return t; - } - - __exports__.template = template;__exports__.normalizePath = normalizePath; __exports__.makeBoundHelper = makeBoundHelper; + __exports__.handlebarsGetView = handlebarsGetView; __exports__.handlebarsGet = handlebarsGet; - __exports__.evaluateUnboundHelper = evaluateUnboundHelper; }); -define("ember-handlebars/helpers/binding", - ["ember-metal/core","ember-handlebars-compiler","ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-runtime/system/string","ember-metal/platform","ember-metal/is_none","ember-metal/enumerable_utils","ember-metal/array","ember-views/views/view","ember-metal/run_loop","ember-metal/observer","ember-metal/binding","ember-views/system/jquery","ember-handlebars/ext","ember-runtime/keys","ember-handlebars/views/handlebars_bound_view","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __dependency18__, __exports__) { +enifed("ember-handlebars/helpers/bind_attr", + ["ember-metal/core","ember-handlebars-compiler","ember-metal/utils","ember-runtime/system/string","ember-metal/array","ember-views/views/view","ember-metal/keys","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) { "use strict"; /** @module ember @@ -7119,596 +7197,19 @@ define("ember-handlebars/helpers/binding", */ var Ember = __dependency1__["default"]; - // Ember.assert, Ember.warn, uuid - // var emberAssert = Ember.assert, Ember.warn = Ember.warn; - + // Ember.assert var EmberHandlebars = __dependency2__["default"]; - var get = __dependency3__.get; - var set = __dependency4__.set; - var apply = __dependency5__.apply; - var uuid = __dependency5__.uuid; - var fmt = __dependency6__.fmt; - var o_create = __dependency7__.create; - var isNone = __dependency8__["default"]; - var EnumerableUtils = __dependency9__["default"]; - var forEach = __dependency10__.forEach; - var View = __dependency11__["default"]; - var run = __dependency12__["default"]; - var removeObserver = __dependency13__.removeObserver; - var isGlobalPath = __dependency14__.isGlobalPath; - var emberBind = __dependency14__.bind; - var jQuery = __dependency15__["default"]; - var isArray = __dependency5__.isArray; - var handlebarsGetEscaped = __dependency16__.getEscaped; - var keys = __dependency17__["default"]; - var _HandlebarsBoundView = __dependency18__._HandlebarsBoundView; - var SimpleHandlebarsView = __dependency18__.SimpleHandlebarsView; - - var normalizePath = __dependency16__.normalizePath; - var handlebarsGet = __dependency16__.handlebarsGet; - var getEscaped = __dependency16__.getEscaped; - - var guidFor = __dependency5__.guidFor; - var typeOf = __dependency5__.typeOf; + var uuid = __dependency3__.uuid; + var fmt = __dependency4__.fmt; + var typeOf = __dependency3__.typeOf; + var forEach = __dependency5__.forEach; + var View = __dependency6__["default"]; + var keys = __dependency7__["default"]; var helpers = EmberHandlebars.helpers; var SafeString = EmberHandlebars.SafeString; - function exists(value) { - return !isNone(value); - } - - var WithView = _HandlebarsBoundView.extend({ - init: function() { - var controller; - - apply(this, this._super, arguments); - - var keywords = this.templateData.keywords; - var keywordName = this.templateHash.keywordName; - var keywordPath = this.templateHash.keywordPath; - var controllerName = this.templateHash.controller; - var preserveContext = this.preserveContext; - - if (controllerName) { - var previousContext = this.previousContext; - controller = this.container.lookupFactory('controller:'+controllerName).create({ - parentController: previousContext, - target: previousContext - }); - - this._generatedController = controller; - - if (!preserveContext) { - this.set('controller', controller); - - this.valueNormalizerFunc = function(result) { - controller.set('model', result); - return controller; - }; - } else { - var controllerPath = jQuery.expando + guidFor(controller); - keywords[controllerPath] = controller; - emberBind(keywords, controllerPath + '.model', keywordPath); - keywordPath = controllerPath; - } - } - - if (preserveContext) { - emberBind(keywords, keywordName, keywordPath); - } - - }, - willDestroy: function() { - this._super(); - - if (this._generatedController) { - this._generatedController.destroy(); - } - } - }); - - // Binds a property into the DOM. This will create a hook in DOM that the - // KVO system will look for and update if the property changes. - function bind(property, options, preserveContext, shouldDisplay, valueNormalizer, childProperties) { - var data = options.data, - fn = options.fn, - inverse = options.inverse, - view = data.view, - normalized, observer, i; - - // we relied on the behavior of calling without - // context to mean this === window, but when running - // "use strict", it's possible for this to === undefined; - var currentContext = this || window; - - normalized = normalizePath(currentContext, property, data); - - // Set up observers for observable objects - if ('object' === typeof this) { - if (data.insideGroup) { - observer = function() { - while (view._contextView) { - view = view._contextView; - } - run.once(view, 'rerender'); - }; - - var template, context, result = handlebarsGet(currentContext, property, options); - - result = valueNormalizer ? valueNormalizer(result) : result; - - context = preserveContext ? currentContext : result; - if (shouldDisplay(result)) { - template = fn; - } else if (inverse) { - template = inverse; - } - - template(context, { data: options.data }); - } else { - var viewClass = _HandlebarsBoundView; - var viewOptions = { - preserveContext: preserveContext, - shouldDisplayFunc: shouldDisplay, - valueNormalizerFunc: valueNormalizer, - displayTemplate: fn, - inverseTemplate: inverse, - path: property, - pathRoot: currentContext, - previousContext: currentContext, - isEscaped: !options.hash.unescaped, - templateData: options.data, - templateHash: options.hash, - helperName: options.helperName - }; - - if (options.isWithHelper) { - viewClass = WithView; - } - - // Create the view that will wrap the output of this template/property - // and add it to the nearest view's childViews array. - // See the documentation of Ember._HandlebarsBoundView for more. - var bindView = view.createChildView(viewClass, viewOptions); - - view.appendChild(bindView); - - observer = function() { - run.scheduleOnce('render', bindView, 'rerenderIfNeeded'); - }; - } - - // Observes the given property on the context and - // tells the Ember._HandlebarsBoundView to re-render. If property - // is an empty string, we are printing the current context - // object ({{this}}) so updating it is not our responsibility. - if (normalized.path !== '') { - view.registerObserver(normalized.root, normalized.path, observer); - if (childProperties) { - for (i=0; i{{user.name}} - -
-
{{user.role.label}}
- {{user.role.id}} - -

{{user.role.description}}

-
- ``` - - `{{with}}` can be our best friend in these cases, - instead of writing `user.role.*` over and over, we use `{{#with user.role}}`. - Now the context within the `{{#with}} .. {{/with}}` block is `user.role` so you can do the following: - - ```handlebars -
{{user.name}}
- -
- {{#with user.role}} -
{{label}}
- {{id}} - -

{{description}}

- {{/with}} -
- ``` - - ### `as` operator - - This operator aliases the scope to a new name. It's helpful for semantic clarity and to retain - default scope or to reference from another `{{with}}` block. - - ```handlebars - // posts might not be - {{#with user.posts as blogPosts}} -
- There are {{blogPosts.length}} blog posts written by {{user.name}}. -
- - {{#each post in blogPosts}} -
  • {{post.title}}
  • - {{/each}} - {{/with}} - ``` - - Without the `as` operator, it would be impossible to reference `user.name` in the example above. - - NOTE: The alias should not reuse a name from the bound property path. - For example: `{{#with foo.bar as foo}}` is not supported because it attempts to alias using - the first part of the property path, `foo`. Instead, use `{{#with foo.bar as baz}}`. - - ### `controller` option - - Adding `controller='something'` instructs the `{{with}}` helper to create and use an instance of - the specified controller with the new context as its content. - - This is very similar to using an `itemController` option with the `{{each}}` helper. - - ```handlebars - {{#with users.posts controller='userBlogPosts'}} - {{!- The current context is wrapped in our controller instance }} - {{/with}} - ``` - - In the above example, the template provided to the `{{with}}` block is now wrapped in the - `userBlogPost` controller, which provides a very elegant way to decorate the context with custom - functions/properties. - - @method with - @for Ember.Handlebars.helpers - @param {Function} context - @param {Hash} options - @return {String} HTML string - */ - function withHelper(context, options) { - var bindContext, preserveContext, controller, helperName = 'with'; - - if (arguments.length === 4) { - var keywordName, path, rootPath, normalized, contextPath; - - Ember.assert("If you pass more than one argument to the with helper, it must be in the form #with foo as bar", arguments[1] === "as"); - options = arguments[3]; - keywordName = arguments[2]; - path = arguments[0]; - - if (path) { - helperName += ' ' + path + ' as ' + keywordName; - } - - Ember.assert("You must pass a block to the with helper", options.fn && options.fn !== Handlebars.VM.noop); - - var localizedOptions = o_create(options); - localizedOptions.data = o_create(options.data); - localizedOptions.data.keywords = o_create(options.data.keywords || {}); - - if (isGlobalPath(path)) { - contextPath = path; - } else { - normalized = normalizePath(this, path, options.data); - path = normalized.path; - rootPath = normalized.root; - - // This is a workaround for the fact that you cannot bind separate objects - // together. When we implement that functionality, we should use it here. - var contextKey = jQuery.expando + guidFor(rootPath); - localizedOptions.data.keywords[contextKey] = rootPath; - // if the path is '' ("this"), just bind directly to the current context - contextPath = path ? contextKey + '.' + path : contextKey; - } - - localizedOptions.hash.keywordName = keywordName; - localizedOptions.hash.keywordPath = contextPath; - - bindContext = this; - context = contextPath; - options = localizedOptions; - preserveContext = true; - } else { - Ember.assert("You must pass exactly one argument to the with helper", arguments.length === 2); - Ember.assert("You must pass a block to the with helper", options.fn && options.fn !== Handlebars.VM.noop); - - helperName += ' ' + context; - bindContext = options.contexts[0]; - preserveContext = false; - } - - options.helperName = helperName; - options.isWithHelper = true; - - return bind.call(bindContext, context, options, preserveContext, exists); - } - /** - See [boundIf](/api/classes/Ember.Handlebars.helpers.html#method_boundIf) - and [unboundIf](/api/classes/Ember.Handlebars.helpers.html#method_unboundIf) - - @method if - @for Ember.Handlebars.helpers - @param {Function} context - @param {Hash} options - @return {String} HTML string - */ - function ifHelper(context, options) { - Ember.assert("You must pass exactly one argument to the if helper", arguments.length === 2); - Ember.assert("You must pass a block to the if helper", options.fn && options.fn !== Handlebars.VM.noop); - - options.helperName = options.helperName || ('if ' + context); - - if (options.data.isUnbound) { - return helpers.unboundIf.call(options.contexts[0], context, options); - } else { - return helpers.boundIf.call(options.contexts[0], context, options); - } - } - - /** - @method unless - @for Ember.Handlebars.helpers - @param {Function} context - @param {Hash} options - @return {String} HTML string - */ - function unlessHelper(context, options) { - Ember.assert("You must pass exactly one argument to the unless helper", arguments.length === 2); - Ember.assert("You must pass a block to the unless helper", options.fn && options.fn !== Handlebars.VM.noop); - - var fn = options.fn, inverse = options.inverse, helperName = 'unless'; - - if (context) { - helperName += ' ' + context; - } - - options.fn = inverse; - options.inverse = fn; - - options.helperName = options.helperName || helperName; - - if (options.data.isUnbound) { - return helpers.unboundIf.call(options.contexts[0], context, options); - } else { - return helpers.boundIf.call(options.contexts[0], context, options); - } - } - /** `bind-attr` allows you to create a binding between DOM element attributes and Ember objects. For example: @@ -7864,22 +7365,20 @@ define("ember-handlebars/helpers/binding", // For each attribute passed, create an observer and emit the // current value of the property as an attribute. forEach.call(attrKeys, function(attr) { - var path = attrs[attr], - normalized; + var path = attrs[attr]; - Ember.assert(fmt("You must provide an expression as the value of bound attribute. You specified: %@=%@", [attr, path]), typeof path === 'string'); + Ember.assert(fmt("You must provide an expression as the value of bound attribute." + + " You specified: %@=%@", [attr, path]), typeof path === 'string'); - normalized = normalizePath(ctx, path, options.data); + var lazyValue = view.getStream(path); + var value = lazyValue.value(); + var type = typeOf(value); - var value = (path === 'this') ? normalized.root : handlebarsGet(ctx, path, options), - type = typeOf(value); + Ember.assert(fmt("Attributes must be numbers, strings or booleans, not %@", [value]), + value === null || value === undefined || type === 'number' || type === 'string' || type === 'boolean'); - Ember.assert(fmt("Attributes must be numbers, strings or booleans, not %@", [value]), value === null || value === undefined || type === 'number' || type === 'string' || type === 'boolean'); - - var observer; - - observer = function observer() { - var result = handlebarsGet(ctx, path, options); + lazyValue.subscribe(view._wrapAsScheduled(function applyAttributeBindings() { + var result = lazyValue.value(); Ember.assert(fmt("Attributes must be numbers, strings or booleans, not %@", [result]), result === null || result === undefined || typeof result === 'number' || @@ -7887,26 +7386,10 @@ define("ember-handlebars/helpers/binding", var elem = view.$("[data-bindattr-" + dataId + "='" + dataId + "']"); - // If we aren't able to find the element, it means the element - // to which we were bound has been removed from the view. - // In that case, we can assume the template has been re-rendered - // and we need to clean up the observer. - if (!elem || elem.length === 0) { - removeObserver(normalized.root, normalized.path, observer); - return; - } + Ember.assert("An attribute binding was triggered when the element was not in the DOM", elem && elem.length !== 0); View.applyAttributeBindings(elem, attr, result); - }; - - // Add an observer to the view for when the property changes. - // When the observer fires, find the element using the - // unique data id and update the attribute to the new value. - // Note: don't add observer when path is 'this' or path - // is whole keyword e.g. {{#each x in list}} ... {{bind-attr attr="x"}} - if (path !== 'this' && !(normalized.isKeyword && normalized.path === '' )) { - view.registerObserver(normalized.root, normalized.path, observer); - } + })); // if this changes, also change the logic in ember-views/lib/views/view.js if ((type === 'string' || (type === 'number' && !isNaN(value)))) { @@ -7934,7 +7417,8 @@ define("ember-handlebars/helpers/binding", @return {String} HTML string */ function bindAttrHelperDeprecated() { - Ember.warn("The 'bindAttr' view helper is deprecated in favor of 'bind-attr'"); + Ember.deprecate("The 'bindAttr' view helper is deprecated in favor of 'bind-attr'"); + return helpers['bind-attr'].apply(this, arguments); } @@ -7962,25 +7446,8 @@ define("ember-handlebars/helpers/binding", @return {Array} An array of class names to add */ function bindClasses(context, classBindings, view, bindAttrId, options) { - var ret = [], newClass, value, elem; - - // Helper method to retrieve the property from the context and - // determine which class string to return, based on whether it is - // a Boolean or not. - var classStringForPath = function(root, parsedPath, options) { - var val, - path = parsedPath.path; - - if (path === 'this') { - val = root; - } else if (path === '') { - val = true; - } else { - val = handlebarsGet(root, path, options); - } - - return View._classStringForValue(path, val, parsedPath.className, parsedPath.falsyClassName); - }; + var ret = []; + var newClass, value, elem; // For each property passed, loop through and setup // an observer. @@ -7990,33 +7457,26 @@ define("ember-handlebars/helpers/binding", // closes over this variable, so it knows which string to remove when // the property changes. var oldClass; + var parsedPath = View._parsePropertyPath(binding); + var path = parsedPath.path; + var initialValue; - var observer; + if (path === '') { + initialValue = true; + } else { + var lazyValue = view.getStream(path); + initialValue = lazyValue.value(); - var parsedPath = View._parsePropertyPath(binding), - path = parsedPath.path, - pathRoot = context, - normalized; + // Set up an observer on the context. If the property changes, toggle the + // class name. + lazyValue.subscribe(view._wrapAsScheduled(function applyClassNameBindings() { + // Get the current value of the property + var value = lazyValue.value(); + newClass = classStringForParsedPath(parsedPath, value); + elem = bindAttrId ? view.$("[data-bindattr-" + bindAttrId + "='" + bindAttrId + "']") : view.$(); - if (path !== '' && path !== 'this') { - normalized = normalizePath(context, path, options.data); + Ember.assert("A class name binding was triggered when the element was not in the DOM", elem && elem.length !== 0); - pathRoot = normalized.root; - path = normalized.path; - } - - // Set up an observer on the context. If the property changes, toggle the - // class name. - observer = function() { - // Get the current value of the property - newClass = classStringForPath(context, parsedPath, options); - elem = bindAttrId ? view.$("[data-bindattr-" + bindAttrId + "='" + bindAttrId + "']") : view.$(); - - // If we can't find the element anymore, a parent template has been - // re-rendered and we've been nuked. Remove the observer. - if (!elem || elem.length === 0) { - removeObserver(pathRoot, path, observer); - } else { // If we had previously added a class to the element, remove it. if (oldClass) { elem.removeClass(oldClass); @@ -8030,16 +7490,12 @@ define("ember-handlebars/helpers/binding", } else { oldClass = null; } - } - }; - - if (path !== '' && path !== 'this') { - view.registerObserver(pathRoot, path, observer); + })); } // We've already setup the observer; now we just need to figure out the // correct behavior right now on the first pass through. - value = classStringForPath(context, parsedPath, options); + value = classStringForParsedPath(parsedPath, initialValue); if (value) { ret.push(value); @@ -8053,22 +7509,238 @@ define("ember-handlebars/helpers/binding", return ret; } - __exports__.bind = bind; - __exports__._triageMustacheHelper = _triageMustacheHelper; - __exports__.resolveHelper = resolveHelper; - __exports__.bindHelper = bindHelper; - __exports__.boundIfHelper = boundIfHelper; - __exports__.unboundIfHelper = unboundIfHelper; - __exports__.withHelper = withHelper; - __exports__.ifHelper = ifHelper; - __exports__.unlessHelper = unlessHelper; + function classStringForParsedPath(parsedPath, value) { + return View._classStringForValue(parsedPath.path, value, parsedPath.className, parsedPath.falsyClassName); + } + + __exports__["default"] = bindAttrHelper; + __exports__.bindAttrHelper = bindAttrHelper; __exports__.bindAttrHelperDeprecated = bindAttrHelperDeprecated; __exports__.bindClasses = bindClasses; }); -define("ember-handlebars/helpers/collection", - ["ember-metal/core","ember-metal/utils","ember-handlebars-compiler","ember-runtime/system/string","ember-metal/property_get","ember-handlebars/ext","ember-handlebars/helpers/view","ember-metal/computed","ember-views/views/collection_view","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __exports__) { +enifed("ember-handlebars/helpers/binding", + ["ember-metal/core","ember-handlebars-compiler","ember-metal/is_none","ember-metal/run_loop","ember-metal/cache","ember-metal/streams/simple","ember-handlebars/views/handlebars_bound_view","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-handlebars + */ + + var Ember = __dependency1__["default"]; + // Ember.assert + var EmberHandlebars = __dependency2__["default"]; + + var isNone = __dependency3__["default"]; + var run = __dependency4__["default"]; + var Cache = __dependency5__["default"]; + var SimpleStream = __dependency6__["default"]; + + var _HandlebarsBoundView = __dependency7__._HandlebarsBoundView; + var SimpleHandlebarsView = __dependency7__.SimpleHandlebarsView; + + var helpers = EmberHandlebars.helpers; + + function exists(value) { + return !isNone(value); + } + + // Binds a property into the DOM. This will create a hook in DOM that the + // KVO system will look for and update if the property changes. + function bind(property, options, preserveContext, shouldDisplay, valueNormalizer, childProperties, _viewClass) { + var data = options.data; + var view = data.view; + + // we relied on the behavior of calling without + // context to mean this === window, but when running + // "use strict", it's possible for this to === undefined; + var currentContext = this || window; + + var valueStream = view.getStream(property); + var lazyValue; + + if (childProperties) { + lazyValue = new SimpleStream(valueStream); + + var subscriber = function(childStream) { + childStream.value(); + lazyValue.notify(); + }; + + for (var i = 0; i < childProperties.length; i++) { + var childStream = valueStream.get(childProperties[i]); + childStream.value(); + childStream.subscribe(subscriber); + } + } else { + lazyValue = valueStream; + } + + // Set up observers for observable objects + var viewClass = _viewClass || _HandlebarsBoundView; + var viewOptions = { + preserveContext: preserveContext, + shouldDisplayFunc: shouldDisplay, + valueNormalizerFunc: valueNormalizer, + displayTemplate: options.fn, + inverseTemplate: options.inverse, + lazyValue: lazyValue, + previousContext: currentContext, + isEscaped: !options.hash.unescaped, + templateData: options.data, + templateHash: options.hash, + helperName: options.helperName + }; + + if (options.keywords) { + viewOptions._keywords = options.keywords; + } + + // Create the view that will wrap the output of this template/property + // and add it to the nearest view's childViews array. + // See the documentation of Ember._HandlebarsBoundView for more. + var bindView = view.createChildView(viewClass, viewOptions); + + view.appendChild(bindView); + + lazyValue.subscribe(view._wrapAsScheduled(function() { + run.scheduleOnce('render', bindView, 'rerenderIfNeeded'); + })); + } + + function simpleBind(currentContext, lazyValue, options) { + var data = options.data; + var view = data.view; + + var bindView = new SimpleHandlebarsView( + lazyValue, !options.hash.unescaped + ); + + bindView._parentView = view; + view.appendChild(bindView); + + lazyValue.subscribe(view._wrapAsScheduled(function() { + run.scheduleOnce('render', bindView, 'rerender'); + })); + } + + /** + '_triageMustache' is used internally select between a binding, helper, or component for + the given context. Until this point, it would be hard to determine if the + mustache is a property reference or a regular helper reference. This triage + helper resolves that. + + This would not be typically invoked by directly. + + @private + @method _triageMustache + @for Ember.Handlebars.helpers + @param {String} property Property/helperID to triage + @param {Object} options hash of template/rendering options + @return {String} HTML string + */ + function _triageMustacheHelper(property, options) { + Ember.assert("You cannot pass more than one argument to the _triageMustache helper", arguments.length <= 2); + + var helper = EmberHandlebars.resolveHelper(options.data.view.container, property); + if (helper) { + return helper.call(this, options); + } + + return helpers.bind.call(this, property, options); + } + + var ISNT_HELPER_CACHE = new Cache(1000, function(key) { + return key.indexOf('-') === -1; + }); + __exports__.ISNT_HELPER_CACHE = ISNT_HELPER_CACHE; + /** + Used to lookup/resolve handlebars helpers. The lookup order is: + + * Look for a registered helper + * If a dash exists in the name: + * Look for a helper registed in the container + * Use Ember.ComponentLookup to find an Ember.Component that resolves + to the given name + + @private + @method resolveHelper + @param {Container} container + @param {String} name the name of the helper to lookup + @return {Handlebars Helper} + */ + function resolveHelper(container, name) { + if (helpers[name]) { + return helpers[name]; + } + + if (!container || ISNT_HELPER_CACHE.get(name)) { + return; + } + + var helper = container.lookup('helper:' + name); + if (!helper) { + var componentLookup = container.lookup('component-lookup:main'); + Ember.assert("Could not find 'component-lookup:main' on the provided container," + + " which is necessary for performing component lookups", componentLookup); + + var Component = componentLookup.lookupFactory(name, container); + if (Component) { + helper = EmberHandlebars.makeViewHelper(Component); + container.register('helper:' + name, helper); + } + } + return helper; + } + + + /** + `bind` can be used to display a value, then update that value if it + changes. For example, if you wanted to print the `title` property of + `content`: + + ```handlebars + {{bind "content.title"}} + ``` + + This will return the `title` property as a string, then create a new observer + at the specified path. If it changes, it will update the value in DOM. Note + that if you need to support IE7 and IE8 you must modify the model objects + properties using `Ember.get()` and `Ember.set()` for this to work as it + relies on Ember's KVO system. For all other browsers this will be handled for + you automatically. + + @private + @method bind + @for Ember.Handlebars.helpers + @param {String} property Property to bind + @param {Function} fn Context to provide for rendering + @return {String} HTML string + */ + function bindHelper(property, options) { + Ember.assert("You cannot pass more than one argument to the bind helper", arguments.length <= 2); + + var context = (options.contexts && options.contexts.length) ? options.contexts[0] : this; + + if (!options.fn) { + var lazyValue = options.data.view.getStream(property); + return simpleBind(context, lazyValue, options); + } + + options.helperName = 'bind'; + + return bind.call(context, property, options, false, exists); + } + + __exports__.bind = bind; + __exports__._triageMustacheHelper = _triageMustacheHelper; + __exports__.resolveHelper = resolveHelper; + __exports__.bindHelper = bindHelper; + }); +enifed("ember-handlebars/helpers/collection", + ["ember-metal/core","ember-handlebars-compiler","ember-metal/mixin","ember-runtime/system/string","ember-metal/property_get","ember-metal/streams/simple","ember-handlebars/ext","ember-handlebars/helpers/view","ember-views/views/view","ember-views/views/collection_view","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __exports__) { "use strict"; /** @module ember @@ -8077,22 +7749,21 @@ define("ember-handlebars/helpers/collection", var Ember = __dependency1__["default"]; // Ember.assert, Ember.deprecate - var inspect = __dependency2__.inspect; // var emberAssert = Ember.assert; // emberDeprecate = Ember.deprecate; - var EmberHandlebars = __dependency3__["default"]; - var helpers = EmberHandlebars.helpers; + var EmberHandlebars = __dependency2__["default"]; + var IS_BINDING = __dependency3__.IS_BINDING; var fmt = __dependency4__.fmt; var get = __dependency5__.get; - var handlebarsGet = __dependency6__.handlebarsGet; - var ViewHelper = __dependency7__.ViewHelper; - var computed = __dependency8__.computed; - var CollectionView = __dependency9__["default"]; + var SimpleStream = __dependency6__["default"]; + var handlebarsGetView = __dependency7__.handlebarsGetView; + var ViewHelper = __dependency8__.ViewHelper; + var View = __dependency9__["default"]; + var CollectionView = __dependency10__["default"]; - var alias = computed.alias; /** `{{collection}}` is a `Ember.Handlebars` helper for adding instances of `Ember.CollectionView` to a template. See [Ember.CollectionView](/api/classes/Ember.CollectionView.html) @@ -8109,7 +7780,8 @@ define("ember-handlebars/helpers/collection", Given an empty `` the following template: ```handlebars - {{#collection contentBinding="App.items"}} + {{! application.hbs }} + {{#collection content=model}} Hi {{view.content.name}} {{/collection}} ``` @@ -8117,44 +7789,45 @@ define("ember-handlebars/helpers/collection", And the following application code ```javascript - App = Ember.Application.create() - App.items = [ - Ember.Object.create({name: 'Dave'}), - Ember.Object.create({name: 'Mary'}), - Ember.Object.create({name: 'Sara'}) - ] + App = Ember.Application.create(); + App.ApplicationRoute = Ember.Route.extend({ + model: function(){ + return [{name: 'Yehuda'},{name: 'Tom'},{name: 'Peter'}]; + } + }); ``` - Will result in the HTML structure below + The following HTML will result: ```html
    -
    Hi Dave
    -
    Hi Mary
    -
    Hi Sara
    +
    Hi Yehuda
    +
    Hi Tom
    +
    Hi Peter
    ``` - ### Blockless use in a collection + ### Non-block version of collection - If you provide an `itemViewClass` option that has its own `template` you can + If you provide an `itemViewClass` option that has its own `template` you may omit the block. The following template: ```handlebars - {{collection contentBinding="App.items" itemViewClass="App.AnItemView"}} + {{! application.hbs }} + {{collection content=model itemViewClass="an-item"}} ``` And application code ```javascript App = Ember.Application.create(); - App.items = [ - Ember.Object.create({name: 'Dave'}), - Ember.Object.create({name: 'Mary'}), - Ember.Object.create({name: 'Sara'}) - ]; + App.ApplicationRoute = Ember.Route.extend({ + model: function(){ + return [{name: 'Yehuda'},{name: 'Tom'},{name: 'Peter'}]; + } + }); App.AnItemView = Ember.View.extend({ template: Ember.Handlebars.compile("Greetings {{view.content.name}}") @@ -8165,9 +7838,9 @@ define("ember-handlebars/helpers/collection", ```html
    -
    Greetings Dave
    -
    Greetings Mary
    -
    Greetings Sara
    +
    Greetings Yehuda
    +
    Greetings Tom
    +
    Greetings Peter
    ``` @@ -8178,11 +7851,13 @@ define("ember-handlebars/helpers/collection", the helper by passing it as the first argument: ```handlebars - {{#collection App.MyCustomCollectionClass contentBinding="App.items"}} + {{#collection "my-custom-collection" content=model}} Hi {{view.content.name}} {{/collection}} ``` + This example would look for the class `App.MyCustomCollection`. + ### Forwarded `item.*`-named Options As with the `{{view}}`, helper options passed to the `{{collection}}` will be @@ -8191,7 +7866,7 @@ define("ember-handlebars/helpers/collection", item (note the camelcasing): ```handlebars - {{#collection contentBinding="App.items" + {{#collection content=model itemTagName="p" itemClassNames="greeting"}} Howdy {{view.content.name}} @@ -8202,9 +7877,9 @@ define("ember-handlebars/helpers/collection", ```html
    -

    Howdy Dave

    -

    Howdy Mary

    -

    Howdy Sara

    +

    Howdy Yehuda

    +

    Howdy Tom

    +

    Howdy Peter

    ``` @@ -8216,7 +7891,8 @@ define("ember-handlebars/helpers/collection", @deprecated Use `{{each}}` helper instead. */ function collectionHelper(path, options) { - Ember.deprecate("Using the {{collection}} helper without specifying a class has been deprecated as the {{each}} helper now supports the same functionality.", path !== 'collection'); + Ember.deprecate("Using the {{collection}} helper without specifying a class has been" + + " deprecated as the {{each}} helper now supports the same functionality.", path !== 'collection'); // If no path is provided, treat path param as options. if (path && path.data && path.data.isRenderData) { @@ -8227,66 +7903,75 @@ define("ember-handlebars/helpers/collection", Ember.assert("You cannot pass more than one argument to the collection helper", arguments.length === 2); } - var fn = options.fn; - var data = options.data; - var inverse = options.inverse; - var view = options.data.view; + var fn = options.fn, + data = options.data, + inverse = options.inverse, + view = options.data.view, + // This should be deterministic, and should probably come from a + // parent view and not the controller. + container = (view.controller && view.controller.container ? view.controller.container : view.container); - - var controller, container; // If passed a path string, convert that into an object. // Otherwise, just default to the standard class. var collectionClass; if (path) { - controller = data.keywords.controller; - container = controller && controller.container; - collectionClass = handlebarsGet(this, path, options) || container.lookupFactory('view:' + path); + collectionClass = handlebarsGetView(this, path, container, options.data); Ember.assert(fmt("%@ #collection: Could not find collection class %@", [data.view, path]), !!collectionClass); } else { collectionClass = CollectionView; } - var hash = options.hash, itemHash = {}, match; + var hash = options.hash; + var hashTypes = options.hashTypes; + var itemHash = {}; + var match; // Extract item view class if provided else default to the standard class - var collectionPrototype = collectionClass.proto(), itemViewClass; + var collectionPrototype = collectionClass.proto(); + var itemViewClass; if (hash.itemView) { - controller = data.keywords.controller; - Ember.assert('You specified an itemView, but the current context has no ' + - 'container to look the itemView up in. This probably means ' + - 'that you created a view manually, instead of through the ' + - 'container. Instead, use container.lookup("view:viewName"), ' + - 'which will properly instantiate your view.', - controller && controller.container); - container = controller.container; - itemViewClass = container.lookupFactory('view:' + hash.itemView); - Ember.assert('You specified the itemView ' + hash.itemView + ", but it was " + - "not found at " + container.describe("view:" + hash.itemView) + - " (and it was not registered in the container)", !!itemViewClass); + itemViewClass = hash.itemView; } else if (hash.itemViewClass) { - itemViewClass = handlebarsGet(collectionPrototype, hash.itemViewClass, options); + if (hashTypes.itemViewClass === 'ID') { + var itemViewClassStream = view.getStream(hash.itemViewClass); + Ember.deprecate('Resolved the view "'+hash.itemViewClass+'" on the global context. Pass a view name to be looked up on the container instead, such as {{view "select"}}. http://emberjs.com/guides/deprecations#toc_global-lookup-of-views', !itemViewClassStream.isGlobal()); + itemViewClass = itemViewClassStream.value(); + } else { + itemViewClass = hash.itemViewClass; + } } else { itemViewClass = collectionPrototype.itemViewClass; } + if (typeof itemViewClass === 'string') { + itemViewClass = container.lookupFactory('view:'+itemViewClass); + } + Ember.assert(fmt("%@ #collection: Could not find itemViewClass %@", [data.view, itemViewClass]), !!itemViewClass); delete hash.itemViewClass; delete hash.itemView; + delete hashTypes.itemViewClass; + delete hashTypes.itemView; // Go through options passed to the {{collection}} helper and extract options // that configure item views instead of the collection itself. for (var prop in hash) { + if (prop === 'itemController' || prop === 'itemClassBinding') { + continue; + } if (hash.hasOwnProperty(prop)) { match = prop.match(/^item(.)(.*)$/); + if (match) { + var childProp = match[1].toLowerCase() + match[2]; - if (match && prop !== 'itemController') { - // Convert itemShouldFoo -> shouldFoo - itemHash[match[1].toLowerCase() + match[2]] = hash[prop]; - // Delete from hash as this will end up getting passed to the - // {{view}} helper method. + if (hashTypes[prop] === 'ID' || IS_BINDING.test(prop)) { + itemHash[childProp] = view._getBindingForStream(hash[prop]); + } else { + itemHash[childProp] = hash[prop]; + } delete hash[prop]; } } @@ -8305,29 +7990,47 @@ define("ember-handlebars/helpers/collection", tagName: itemHash.tagName }); } else if (hash.emptyViewClass) { - emptyViewClass = handlebarsGet(this, hash.emptyViewClass, options); + emptyViewClass = handlebarsGetView(this, hash.emptyViewClass, container, options.data); } if (emptyViewClass) { hash.emptyView = emptyViewClass; } if (hash.keyword) { - itemHash._context = this; + itemHash._contextBinding = '_parentView.context'; } else { - itemHash._context = alias('content'); + itemHash._contextBinding = 'content'; } var viewOptions = ViewHelper.propertiesFromHTMLOptions({ data: data, hash: itemHash }, this); - hash.itemViewClass = itemViewClass.extend(viewOptions); + + if (hash.itemClassBinding) { + var itemClassBindings = hash.itemClassBinding.split(' '); + + for (var i = 0; i < itemClassBindings.length; i++) { + var parsedPath = View._parsePropertyPath(itemClassBindings[i]); + if (parsedPath.path === '') { + parsedPath.stream = new SimpleStream(true); + } else { + parsedPath.stream = view.getStream(parsedPath.path); + } + itemClassBindings[i] = parsedPath; + } + + viewOptions.classNameBindings = itemClassBindings; + } + + hash.itemViewClass = itemViewClass; + hash._itemViewProps = viewOptions; options.helperName = options.helperName || 'collection'; - return helpers.view.call(this, collectionClass, options); + return EmberHandlebars.helpers.view.call(this, collectionClass, options); } __exports__["default"] = collectionHelper; }); -define("ember-handlebars/helpers/debug", - ["ember-metal/core","ember-metal/utils","ember-metal/logger","ember-metal/property_get","ember-handlebars/ext","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { +enifed("ember-handlebars/helpers/debug", + ["ember-metal/core","ember-metal/utils","ember-metal/logger","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { "use strict"; /*jshint debug:true*/ @@ -8340,10 +8043,6 @@ define("ember-handlebars/helpers/debug", var inspect = __dependency2__.inspect; var Logger = __dependency3__["default"]; - var get = __dependency4__.get; - var normalizePath = __dependency5__.normalizePath; - var handlebarsGet = __dependency5__.handlebarsGet; - var a_slice = [].slice; /** @@ -8359,24 +8058,16 @@ define("ember-handlebars/helpers/debug", @param {String} property */ function logHelper() { - var params = a_slice.call(arguments, 0, -1), - options = arguments[arguments.length - 1], - logger = Logger.log, - values = [], - allowPrimitives = true; + var params = a_slice.call(arguments, 0, -1); + var options = arguments[arguments.length - 1]; + var view = options.data.view; + var logger = Logger.log; + var values = []; for (var i = 0; i < params.length; i++) { - var type = options.types[i]; - - if (type === 'ID' || !allowPrimitives) { - var context = (options.contexts && options.contexts[i]) || this, - normalized = normalizePath(context, params[i], options.data); - - if (normalized.path === 'this') { - values.push(normalized.root); - } else { - values.push(handlebarsGet(normalized.root, normalized.path, options)); - } + if (options.types[i] === 'ID') { + var stream = view.getStream(params[i]); + values.push(stream.value()); } else { values.push(params[i]); } @@ -8422,6 +8113,7 @@ define("ember-handlebars/helpers/debug", function debuggerHelper(options) { // These are helpful values you can inspect while debugging. + /* jshint unused: false */ var templateContext = this; var typeOfTemplateContext = inspect(templateContext); Ember.Logger.info('Use `this` to access the context of the calling template.'); @@ -8432,9 +8124,9 @@ define("ember-handlebars/helpers/debug", __exports__.logHelper = logHelper; __exports__.debuggerHelper = debuggerHelper; }); -define("ember-handlebars/helpers/each", - ["ember-metal/core","ember-handlebars-compiler","ember-runtime/system/string","ember-metal/property_get","ember-metal/property_set","ember-views/views/collection_view","ember-metal/binding","ember-runtime/mixins/controller","ember-runtime/controllers/array_controller","ember-runtime/mixins/array","ember-runtime/copy","ember-metal/run_loop","ember-metal/events","ember-handlebars/ext","ember-metal/computed","ember-metal/observer","ember-handlebars/views/metamorph_view","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __exports__) { +enifed("ember-handlebars/helpers/each", + ["ember-metal/core","ember-handlebars-compiler","ember-runtime/system/string","ember-metal/property_get","ember-metal/property_set","ember-views/views/collection_view","ember-metal/binding","ember-runtime/mixins/controller","ember-runtime/controllers/array_controller","ember-runtime/mixins/array","ember-metal/observer","ember-handlebars/views/metamorph_view","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __exports__) { "use strict"; /** @@ -8443,11 +8135,8 @@ define("ember-handlebars/helpers/each", */ var Ember = __dependency1__["default"]; // Ember.assert;, Ember.K - // var emberAssert = Ember.assert, - var K = Ember.K; var EmberHandlebars = __dependency2__["default"]; - var helpers = EmberHandlebars.helpers; var fmt = __dependency3__.fmt; var get = __dependency4__.get; @@ -8457,19 +8146,14 @@ define("ember-handlebars/helpers/each", var ControllerMixin = __dependency8__["default"]; var ArrayController = __dependency9__["default"]; var EmberArray = __dependency10__["default"]; - var copy = __dependency11__["default"]; - var run = __dependency12__["default"]; - var on = __dependency13__.on; - var handlebarsGet = __dependency14__.handlebarsGet; - var computed = __dependency15__.computed; - var addObserver = __dependency16__.addObserver; - var removeObserver = __dependency16__.removeObserver; - var addBeforeObserver = __dependency16__.addBeforeObserver; - var removeBeforeObserver = __dependency16__.removeBeforeObserver; + var addObserver = __dependency11__.addObserver; + var removeObserver = __dependency11__.removeObserver; + var addBeforeObserver = __dependency11__.addBeforeObserver; + var removeBeforeObserver = __dependency11__.removeBeforeObserver; - var _Metamorph = __dependency17__._Metamorph; - var _MetamorphView = __dependency17__._MetamorphView; + var _MetamorphView = __dependency12__["default"]; + var _Metamorph = __dependency12__._Metamorph; var EachView = CollectionView.extend(_Metamorph, { @@ -8510,7 +8194,11 @@ define("ember-handlebars/helpers/each", !ControllerMixin.detect(content) || (content && content.isGenerated) || content instanceof ArrayController); - Ember.assert(fmt("The value that #each loops over must be an Array. You passed %@", [(ControllerMixin.detect(content) && content.get('model') !== undefined) ? fmt("'%@' (wrapped in %@)", [content.get('model'), content]) : content]), EmberArray.detect(content)); + Ember.assert(fmt("The value that #each loops over must be an Array. You passed %@", + [(ControllerMixin.detect(content) && + content.get('model') !== undefined) ? + fmt("'%@' (wrapped in %@)", [content.get('model'), content]) : content]), + EmberArray.detect(content)); }, disableContentObservers: function(callback) { @@ -8529,22 +8217,11 @@ define("ember-handlebars/helpers/each", createChildView: function(view, attrs) { view = this._super(view, attrs); - // At the moment, if a container view subclass wants - // to insert keywords, it is responsible for cloning - // the keywords hash. This will be fixed momentarily. - var keyword = get(this, 'keyword'); var content = get(view, 'content'); + var keyword = get(this, 'keyword'); if (keyword) { - var data = get(view, 'templateData'); - - data = copy(data); - data.keywords = view.cloneKeywords(); - set(view, 'templateData', data); - - // In this case, we do not bind, because the `content` of - // a #each item cannot change. - data.keywords[keyword] = content; + view._keywords[keyword] = content; } // If {{#each}} is looping over an array of controllers, @@ -8569,351 +8246,343 @@ define("ember-handlebars/helpers/each", } }); - // Defeatureify doesn't seem to like nested functions that need to be removed - function _addMetamorphCheck() { - EachView.reopen({ - _checkMetamorph: on('didInsertElement', function() { - Ember.assert("The metamorph tags, " + - this.morph.start + " and " + this.morph.end + - ", have different parents.\nThe browser has fixed your template to output valid HTML (for example, check that you have properly closed all tags and have used a TBODY tag when creating a table with '{{#each}}')", - document.getElementById( this.morph.start ).parentNode === - document.getElementById( this.morph.end ).parentNode - ); - }) - }); - } - - // until ember-debug is es6ed - var runInDebug = function(f){ f(); }; - runInDebug( function() { - _addMetamorphCheck(); - }); - - var GroupedEach = EmberHandlebars.GroupedEach = function(context, path, options) { - var self = this, - normalized = EmberHandlebars.normalizePath(context, path, options.data); - - this.context = context; - this.path = path; - this.options = options; - this.template = options.fn; - this.containingView = options.data.view; - this.normalizedRoot = normalized.root; - this.normalizedPath = normalized.path; - this.content = this.lookupContent(); - - this.addContentObservers(); - this.addArrayObservers(); - - this.containingView.on('willClearRender', function() { - self.destroy(); - }); - }; - - GroupedEach.prototype = { - contentWillChange: function() { - this.removeArrayObservers(); - }, - - contentDidChange: function() { - this.content = this.lookupContent(); - this.addArrayObservers(); - this.rerenderContainingView(); - }, - - contentArrayWillChange: K, - - contentArrayDidChange: function() { - this.rerenderContainingView(); - }, - - lookupContent: function() { - return handlebarsGet(this.normalizedRoot, this.normalizedPath, this.options); - }, - - addArrayObservers: function() { - if (!this.content) { return; } - - this.content.addArrayObserver(this, { - willChange: 'contentArrayWillChange', - didChange: 'contentArrayDidChange' - }); - }, - - removeArrayObservers: function() { - if (!this.content) { return; } - - this.content.removeArrayObserver(this, { - willChange: 'contentArrayWillChange', - didChange: 'contentArrayDidChange' - }); - }, - - addContentObservers: function() { - addBeforeObserver(this.normalizedRoot, this.normalizedPath, this, this.contentWillChange); - addObserver(this.normalizedRoot, this.normalizedPath, this, this.contentDidChange); - }, - - removeContentObservers: function() { - removeBeforeObserver(this.normalizedRoot, this.normalizedPath, this.contentWillChange); - removeObserver(this.normalizedRoot, this.normalizedPath, this.contentDidChange); - }, - - render: function() { - if (!this.content) { return; } - - var content = this.content, - contentLength = get(content, 'length'), - options = this.options, - data = options.data, - template = this.template; - - data.insideEach = true; - for (var i = 0; i < contentLength; i++) { - var context = content.objectAt(i); - options.data.keywords[options.hash.keyword] = context; - template(context, { data: data }); - } - }, - - rerenderContainingView: function() { - var self = this; - run.scheduleOnce('render', this, function() { - // It's possible it's been destroyed after we enqueued a re-render call. - if (!self.destroyed) { - self.containingView.rerender(); - } - }); - }, - - destroy: function() { - this.removeContentObservers(); - if (this.content) { - this.removeArrayObservers(); - } - this.destroyed = true; - } - }; - /** - The `{{#each}}` helper loops over elements in a collection, rendering its - block once for each item. It is an extension of the base Handlebars `{{#each}}` - helper: + The `{{#each}}` helper loops over elements in a collection. It is an extension + of the base Handlebars `{{#each}}` helper. + + The default behavior of `{{#each}}` is to yield its inner block once for every + item in an array. ```javascript - Developers = [{name: 'Yehuda'},{name: 'Tom'}, {name: 'Paul'}]; + var developers = [{name: 'Yehuda'},{name: 'Tom'}, {name: 'Paul'}]; ``` ```handlebars - {{#each Developers}} + {{#each person in developers}} + {{person.name}} + {{! `this` is whatever it was outside the #each }} + {{/each}} + ``` + + The same rules apply to arrays of primitives, but the items may need to be + references with `{{this}}`. + + ```javascript + var developerNames = ['Yehuda', 'Tom', 'Paul'] + ``` + + ```handlebars + {{#each name in developerNames}} {{name}} {{/each}} ``` - `{{each}}` supports an alternative syntax with element naming: - - ```handlebars - {{#each person in Developers}} - {{person.name}} - {{/each}} - ``` - - When looping over objects that do not have properties, `{{this}}` can be used - to render the object: - - ```javascript - DeveloperNames = ['Yehuda', 'Tom', 'Paul'] - ``` - - ```handlebars - {{#each DeveloperNames}} - {{this}} - {{/each}} - ``` ### {{else}} condition + `{{#each}}` can have a matching `{{else}}`. The contents of this block will render if the collection is empty. ``` - {{#each person in Developers}} + {{#each person in developers}} {{person.name}} {{else}}

    Sorry, nobody is available for this task.

    {{/each}} ``` - ### Specifying a View class for items - If you provide an `itemViewClass` option that references a view class - with its own `template` you can omit the block. + + ### Specifying an alternative view for each item + + `itemViewClass` can control which view will be used during the render of each + item's template. The following template: ```handlebars - {{#view App.MyView }} - {{each view.items itemViewClass="App.AnItemView"}} - {{/view}} +
      + {{#each developer in developers itemViewClass="person"}} + {{developer.name}} + {{/each}} +
    ``` - And application code + Will use the following view for each item ```javascript - App = Ember.Application.create({ - MyView: Ember.View.extend({ - items: [ - Ember.Object.create({name: 'Dave'}), - Ember.Object.create({name: 'Mary'}), - Ember.Object.create({name: 'Sara'}) - ] - }) - }); - - App.AnItemView = Ember.View.extend({ - template: Ember.Handlebars.compile("Greetings {{name}}") + App.PersonView = Ember.View.extend({ + tagName: 'li' }); ``` - Will result in the HTML structure below + Resulting in HTML output that looks like the following: ```html -
    -
    Greetings Dave
    -
    Greetings Mary
    -
    Greetings Sara
    -
    +
      +
    • Yehuda
    • +
    • Tom
    • +
    • Paul
    • +
    ``` - If an `itemViewClass` is defined on the helper, and therefore the helper is not - being used as a block, an `emptyViewClass` can also be provided optionally. - The `emptyViewClass` will match the behavior of the `{{else}}` condition - described above. That is, the `emptyViewClass` will render if the collection - is empty. - - ### Representing each item with a Controller. - By default the controller lookup within an `{{#each}}` block will be - the controller of the template where the `{{#each}}` was used. If each - item needs to be presented by a custom controller you can provide a - `itemController` option which references a controller by lookup name. - Each item in the loop will be wrapped in an instance of this controller - and the item itself will be set to the `model` property of that controller. - - This is useful in cases where properties of model objects need transformation - or synthesis for display: + `itemViewClass` also enables a non-block form of `{{each}}`. The view + must {{#crossLink "Ember.View/toc_templates"}}provide its own template{{/crossLink}}, + and then the block should be dropped. An example that outputs the same HTML + as the previous one: ```javascript - App.DeveloperController = Ember.ObjectController.extend({ + App.PersonView = Ember.View.extend({ + tagName: 'li', + template: '{{developer.name}}' + }); + ``` + + ```handlebars +
      + {{each developer in developers itemViewClass="person"}} +
    + ``` + + ### Specifying an alternative view for no items (else) + + The `emptyViewClass` option provides the same flexibility to the `{{else}}` + case of the each helper. + + ```javascript + App.NoPeopleView = Ember.View.extend({ + tagName: 'li', + template: 'No person is available, sorry' + }); + ``` + + ```handlebars +
      + {{#each developer in developers emptyViewClass="no-people"}} +
    • {{developer.name}}
    • + {{/each}} +
    + ``` + + ### Wrapping each item in a controller + + Controllers in Ember manage state and decorate data. In many cases, + providing a controller for each item in a list can be useful. + Specifically, an {{#crossLink "Ember.ObjectController"}}Ember.ObjectController{{/crossLink}} + should probably be used. Item controllers are passed the item they + will present as a `model` property, and an object controller will + proxy property lookups to `model` for us. + + This allows state and decoration to be added to the controller + while any other property lookups are delegated to the model. An example: + + ```javascript + App.RecruitController = Ember.ObjectController.extend({ isAvailableForHire: function() { - return !this.get('model.isEmployed') && this.get('model.isSeekingWork'); + return !this.get('isEmployed') && this.get('isSeekingWork'); }.property('isEmployed', 'isSeekingWork') }) ``` ```handlebars - {{#each person in developers itemController="developer"}} + {{#each person in developers itemController="recruit"}} {{person.name}} {{#if person.isAvailableForHire}}Hire me!{{/if}} {{/each}} ``` - Each itemController will receive a reference to the current controller as - a `parentController` property. - - ### (Experimental) Grouped Each - - When used in conjunction with the experimental [group helper](https://github.com/emberjs/group-helper), - you can inform Handlebars to re-render an entire group of items instead of - re-rendering them one at a time (in the event that they are changed en masse - or an item is added/removed). - - ```handlebars - {{#group}} - {{#each people}} - {{firstName}} {{lastName}} - {{/each}} - {{/group}} - ``` - - This can be faster than the normal way that Handlebars re-renders items - in some cases. - - If for some reason you have a group with more than one `#each`, you can make - one of the collections be updated in normal (non-grouped) fashion by setting - the option `groupedRows=true` (counter-intuitive, I know). - - For example, - - ```handlebars - {{dealershipName}} - - {{#group}} - {{#each dealers}} - {{firstName}} {{lastName}} - {{/each}} - - {{#each car in cars groupedRows=true}} - {{car.make}} {{car.model}} {{car.color}} - {{/each}} - {{/group}} - ``` - Any change to `dealershipName` or the `dealers` collection will cause the - entire group to be re-rendered. However, changes to the `cars` collection - will be re-rendered individually (as normal). - - Note that `group` behavior is also disabled by specifying an `itemViewClass`. - @method each @for Ember.Handlebars.helpers @param [name] {String} name for item (used with `in`) @param [path] {String} path @param [options] {Object} Handlebars key/value pairs of options @param [options.itemViewClass] {String} a path to a view class used for each item + @param [options.emptyViewClass] {String} a path to a view class used for each item @param [options.itemController] {String} name of a controller to be created for each item - @param [options.groupedRows] {boolean} enable normal item-by-item rendering when inside a `#group` helper */ - function eachHelper(path, options) { - var ctx, helperName = 'each'; + function eachHelper(path) { + var options = arguments[arguments.length - 1]; + var helperName = 'each'; + var keywordName; if (arguments.length === 4) { - Ember.assert("If you pass more than one argument to the each helper, it must be in the form #each foo in bar", arguments[1] === "in"); + Ember.assert("If you pass more than one argument to the each helper," + + " it must be in the form #each foo in bar", arguments[1] === "in"); - var keywordName = arguments[0]; - - - options = arguments[3]; + keywordName = arguments[0]; path = arguments[2]; helperName += ' ' + keywordName + ' in ' + path; - if (path === '') { path = "this"; } - options.hash.keyword = keywordName; - } else if (arguments.length === 1) { - options = path; - path = 'this'; + path = ''; } else { helperName += ' ' + path; } + Ember.deprecate('Using the context switching form of {{each}} is deprecated. Please use the keyword form (`{{#each foo in bar}}`) instead. See http://emberjs.com/guides/deprecations/#toc_more-consistent-handlebars-scope for more details.', keywordName); + + options.hash.emptyViewClass = Ember._MetamorphView; options.hash.dataSourceBinding = path; - // Set up emptyView as a metamorph with no tag - //options.hash.emptyViewClass = Ember._MetamorphView; - - // can't rely on this default behavior when use strict - ctx = this || window; - + options.hashTypes.dataSourceBinding = 'STRING'; options.helperName = options.helperName || helperName; - if (options.data.insideGroup && !options.hash.groupedRows && !options.hash.itemViewClass) { - new GroupedEach(ctx, path, options).render(); - } else { - // ES6TODO: figure out how to do this without global lookup. - return helpers.collection.call(ctx, 'Ember.Handlebars.EachView', options); - } + return EmberHandlebars.helpers.collection.call(this, EmberHandlebars.EachView, options); } __exports__.EachView = EachView; - __exports__.GroupedEach = GroupedEach; __exports__.eachHelper = eachHelper; }); -define("ember-handlebars/helpers/loc", +enifed("ember-handlebars/helpers/if_unless", + ["ember-metal/core","ember-handlebars-compiler","ember-handlebars/helpers/binding","ember-metal/property_get","ember-metal/utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-handlebars + */ + + var Ember = __dependency1__["default"]; + // Ember.assert + var EmberHandlebars = __dependency2__["default"]; + + var bind = __dependency3__.bind; + + var get = __dependency4__.get; + var isArray = __dependency5__.isArray; + + var helpers = EmberHandlebars.helpers; + + function shouldDisplayIfHelperContent(result) { + var truthy = result && get(result, 'isTruthy'); + if (typeof truthy === 'boolean') { return truthy; } + + if (isArray(result)) { + return get(result, 'length') !== 0; + } else { + return !!result; + } + } + + /** + Use the `boundIf` helper to create a conditional that re-evaluates + whenever the truthiness of the bound value changes. + + ```handlebars + {{#boundIf "content.shouldDisplayTitle"}} + {{content.title}} + {{/boundIf}} + ``` + + @private + @method boundIf + @for Ember.Handlebars.helpers + @param {String} property Property to bind + @param {Function} fn Context to provide for rendering + @return {String} HTML string + */ + function boundIfHelper(property, fn) { + var context = (fn.contexts && fn.contexts.length) ? fn.contexts[0] : this; + + fn.helperName = fn.helperName || 'boundIf'; + + return bind.call(context, property, fn, true, shouldDisplayIfHelperContent, shouldDisplayIfHelperContent, [ + 'isTruthy', + 'length' + ]); + } + + /** + @private + + Use the `unboundIf` helper to create a conditional that evaluates once. + + ```handlebars + {{#unboundIf "content.shouldDisplayTitle"}} + {{content.title}} + {{/unboundIf}} + ``` + + @method unboundIf + @for Ember.Handlebars.helpers + @param {String} property Property to bind + @param {Function} fn Context to provide for rendering + @return {String} HTML string + @since 1.4.0 + */ + function unboundIfHelper(property, fn) { + var context = (fn.contexts && fn.contexts.length) ? fn.contexts[0] : this; + var data = fn.data; + var view = data.view; + var template = fn.fn; + var inverse = fn.inverse; + + var propertyValue = view.getStream(property).value(); + + if (!shouldDisplayIfHelperContent(propertyValue)) { + template = inverse; + } + + template(context, { data: data }); + } + + /** + See [boundIf](/api/classes/Ember.Handlebars.helpers.html#method_boundIf) + and [unboundIf](/api/classes/Ember.Handlebars.helpers.html#method_unboundIf) + + @method if + @for Ember.Handlebars.helpers + @param {Function} context + @param {Hash} options + @return {String} HTML string + */ + function ifHelper(context, options) { + Ember.assert("You must pass exactly one argument to the if helper", arguments.length === 2); + Ember.assert("You must pass a block to the if helper", options.fn && options.fn !== Handlebars.VM.noop); + + options.helperName = options.helperName || ('if ' + context); + + if (options.data.isUnbound) { + return helpers.unboundIf.call(options.contexts[0], context, options); + } else { + return helpers.boundIf.call(options.contexts[0], context, options); + } + } + + /** + @method unless + @for Ember.Handlebars.helpers + @param {Function} context + @param {Hash} options + @return {String} HTML string + */ + function unlessHelper(context, options) { + Ember.assert("You must pass exactly one argument to the unless helper", arguments.length === 2); + Ember.assert("You must pass a block to the unless helper", options.fn && options.fn !== Handlebars.VM.noop); + + var fn = options.fn; + var inverse = options.inverse; + var helperName = 'unless'; + + if (context) { + helperName += ' ' + context; + } + + options.fn = inverse; + options.inverse = fn; + + options.helperName = options.helperName || helperName; + + if (options.data.isUnbound) { + return helpers.unboundIf.call(options.contexts[0], context, options); + } else { + return helpers.boundIf.call(options.contexts[0], context, options); + } + } + + __exports__.ifHelper = ifHelper; + __exports__.boundIfHelper = boundIfHelper; + __exports__.unboundIfHelper = unboundIfHelper; + __exports__.unlessHelper = unlessHelper; + }); +enifed("ember-handlebars/helpers/loc", ["ember-runtime/system/string","exports"], function(__dependency1__, __exports__) { "use strict"; @@ -8924,26 +8593,31 @@ define("ember-handlebars/helpers/loc", @submodule ember-handlebars */ - // ES6TODO: - // Pretty sure this can be expressed as - // var locHelper EmberStringUtils.loc ? - /** Calls [Ember.String.loc](/api/classes/Ember.String.html#method_loc) with the provided string. - This is a convenient way to localize text. For example: + This is a convenient way to localize text within a template: - ```html - + ```javascript + Ember.STRINGS = { + '_welcome_': 'Bonjour' + }; ``` - Take note that `"welcome"` is a string and not an object - reference. + ```handlebars +
    + {{loc '_welcome_'}} +
    + ``` - See [Ember.String.loc](/api/classes/Ember.String.html#method_loc) for how to + ```html +
    + Bonjour +
    + ``` + + See [Ember.String.loc](/api/classes/Ember.String.html#method_loc) for how to set up localized string references. @method loc @@ -8951,21 +8625,18 @@ define("ember-handlebars/helpers/loc", @param {String} str The string to format @see {Ember.String#loc} */ - __exports__["default"] = function locHelper(str) { - return loc(str); - } + __exports__["default"] = loc; }); -define("ember-handlebars/helpers/partial", - ["ember-metal/core","ember-metal/is_none","ember-handlebars/ext","ember-handlebars/helpers/binding","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { +enifed("ember-handlebars/helpers/partial", + ["ember-metal/core","ember-metal/is_none","ember-handlebars/helpers/binding","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { "use strict"; var Ember = __dependency1__["default"]; // Ember.assert // var emberAssert = Ember.assert; - var isNone = __dependency2__.isNone; - var handlebarsGet = __dependency3__.handlebarsGet; - var bind = __dependency4__.bind; + var isNone = __dependency2__["default"]; + var bind = __dependency3__.bind; /** @module ember @@ -9006,16 +8677,6 @@ define("ember-handlebars/helpers/partial", changes, the partial will be re-rendered using the new template name. - ## Setting the partial's context with `with` - - The `partial` helper can be used in conjunction with the `with` - helper to set a context that will be used by the partial: - - ```handlebars - {{#with currentUser}} - {{partial "user_info"}} - {{/with}} - ``` @method partial @for Ember.Handlebars.helpers @@ -9023,18 +8684,19 @@ define("ember-handlebars/helpers/partial", */ __exports__["default"] = function partialHelper(name, options) { + var view = options.data.view; var context = (options.contexts && options.contexts.length) ? options.contexts[0] : this; options.helperName = options.helperName || 'partial'; if (options.types[0] === "ID") { + var partialNameStream = view.getStream(name); // Helper was passed a property path; we need to // create a binding that will re-render whenever // this property changes. options.fn = function(context, fnOptions) { - var partialName = handlebarsGet(context, name, fnOptions); - renderPartial(context, partialName, fnOptions); + renderPartial(context, partialNameStream.value(), fnOptions); }; return bind.call(context, name, options, true, exists); @@ -9063,111 +8725,51 @@ define("ember-handlebars/helpers/partial", template = template || deprecatedTemplate; - template(context, { data: options.data }); + template(context, { + data: options.data + }); } }); -define("ember-handlebars/helpers/shared", - ["ember-handlebars/ext","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var handlebarsGet = __dependency1__.handlebarsGet; - - __exports__["default"] = function resolvePaths(options) { - var ret = [], - contexts = options.contexts, - roots = options.roots, - data = options.data; - - for (var i=0, l=contexts.length; i - {{#with loggedInUser}} - Last Login: {{lastLogin}} - User Info: {{template "user_info"}} - {{/with}} - - ``` - - ```html - - ``` - - ```handlebars - {{#if isUser}} - {{template "user_info"}} - {{else}} - {{template "unlogged_user_info"}} - {{/if}} - ``` - - This helper looks for templates in the global `Ember.TEMPLATES` hash. If you - add `"; - return testEl.firstChild.innerHTML === ''; - })(); - - // IE 8 (and likely earlier) likes to move whitespace preceeding - // a script tag to appear after it. This means that we can - // accidentally remove whitespace when updating a morph. - var movesWhitespace = typeof document !== 'undefined' && (function() { - var testEl = document.createElement('div'); - testEl.innerHTML = "Test: Value"; - return testEl.childNodes[0].nodeValue === 'Test:' && - testEl.childNodes[2].nodeValue === ' Value'; - })(); - - // Use this to find children by ID instead of using jQuery - var findChildById = function(element, id) { - if (element.getAttribute('id') === id) { return element; } - - var len = element.childNodes.length, idx, node, found; - for (idx=0; idx 0) { - var len = matches.length, idx; - for (idx=0; idxTest'); - canSet = el.options.length === 1; - } - - innerHTMLTags[tagName] = canSet; - - return canSet; - }; - - function setInnerHTML(element, html) { - var tagName = element.tagName; - - if (canSetInnerHTML(tagName)) { - setInnerHTMLWithoutFix(element, html); - } else { - // Firefox versions < 11 do not have support for element.outerHTML. - var outerHTML = element.outerHTML || new XMLSerializer().serializeToString(element); - Ember.assert("Can't set innerHTML on "+element.tagName+" in this browser", outerHTML); - - var startTag = outerHTML.match(new RegExp("<"+tagName+"([^>]*)>", 'i'))[0], - endTag = ''; - - var wrapper = document.createElement('div'); - setInnerHTMLWithoutFix(wrapper, startTag + html + endTag); - element = wrapper.firstChild; - while (element.tagName !== tagName) { - element = element.nextSibling; - } - } - - return element; - } - - __exports__.setInnerHTML = setInnerHTML;function isSimpleClick(event) { - var modifier = event.shiftKey || event.metaKey || event.altKey || event.ctrlKey, - secondaryClick = event.which > 1; // IE9 may return undefined + function isSimpleClick(event) { + var modifier = event.shiftKey || event.metaKey || event.altKey || event.ctrlKey; + var secondaryClick = event.which > 1; // IE9 may return undefined return !modifier && !secondaryClick; } - __exports__.isSimpleClick = isSimpleClick; + __exports__.isSimpleClick = isSimpleClick;/** + @private + @method getViewRange + @param {Ember.View} view + */ + function getViewRange(view) { + var range = document.createRange(); + range.setStartAfter(view._morph.start); + range.setEndBefore(view._morph.end); + return range; + } + + /** + `getViewClientRects` provides information about the position of the border + box edges of a view relative to the viewport. + + It is only intended to be used by development tools like the Ember Inpsector + and may not work on older browsers. + + @private + @method getViewClientRects + @param {Ember.View} view + */ + function getViewClientRects(view) { + var range = getViewRange(view); + return range.getClientRects(); + } + + __exports__.getViewClientRects = getViewClientRects;/** + `getViewBoundingClientRect` provides information about the position of the + bounding border box edges of a view relative to the viewport. + + It is only intended to be used by development tools like the Ember Inpsector + and may not work on older browsers. + + @private + @method getViewBoundingClientRect + @param {Ember.View} view + */ + function getViewBoundingClientRect(view) { + var range = getViewRange(view); + return range.getBoundingClientRect(); + } + + __exports__.getViewBoundingClientRect = getViewBoundingClientRect; }); -define("ember-views/views/collection_view", - ["ember-metal/core","ember-metal/platform","ember-metal/binding","ember-metal/merge","ember-metal/property_get","ember-metal/property_set","ember-runtime/system/string","ember-views/views/container_view","ember-views/views/core_view","ember-views/views/view","ember-metal/mixin","ember-runtime/mixins/array","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __exports__) { +enifed("ember-views/views/collection_view", + ["ember-metal/core","ember-metal/binding","ember-metal/property_get","ember-metal/property_set","ember-runtime/system/string","ember-views/views/container_view","ember-views/views/core_view","ember-views/views/view","ember-metal/mixin","ember-views/streams/read","ember-runtime/mixins/array","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __exports__) { "use strict"; /** @@ -37973,18 +40129,17 @@ define("ember-views/views/collection_view", var Ember = __dependency1__["default"]; // Ember.assert - var create = __dependency2__.create; - var isGlobalPath = __dependency3__.isGlobalPath; - var merge = __dependency4__["default"]; - var get = __dependency5__.get; - var set = __dependency6__.set; - var fmt = __dependency7__.fmt; - var ContainerView = __dependency8__["default"]; - var CoreView = __dependency9__["default"]; - var View = __dependency10__["default"]; - var observer = __dependency11__.observer; - var beforeObserver = __dependency11__.beforeObserver; - var EmberArray = __dependency12__["default"]; + var isGlobalPath = __dependency2__.isGlobalPath; + var get = __dependency3__.get; + var set = __dependency4__.set; + var fmt = __dependency5__.fmt; + var ContainerView = __dependency6__["default"]; + var CoreView = __dependency7__["default"]; + var View = __dependency8__["default"]; + var observer = __dependency9__.observer; + var beforeObserver = __dependency9__.beforeObserver; + var readViewFactory = __dependency10__.readViewFactory; + var EmberArray = __dependency11__["default"]; /** `Ember.CollectionView` is an `Ember.View` descendent responsible for managing @@ -38007,27 +40162,32 @@ define("ember-views/views/collection_view", The view for each item in the collection will have its `content` property set to the item. - ## Specifying itemViewClass + ## Specifying `itemViewClass` By default the view class for each item in the managed collection will be an instance of `Ember.View`. You can supply a different class by setting the `CollectionView`'s `itemViewClass` property. - Given an empty `` and the following code: + Given the following application code: ```javascript - someItemsView = Ember.CollectionView.create({ + var App = Ember.Application.create(); + App.ItemListView = Ember.CollectionView.extend({ classNames: ['a-collection'], content: ['A','B','C'], itemViewClass: Ember.View.extend({ template: Ember.Handlebars.compile("the letter: {{view.content}}") }) }); - - someItemsView.appendTo('body'); ``` - Will result in the following HTML structure + And a simple application template: + + ```handlebars + {{view 'item-list'}} + ``` + + The following HTML will result: ```html
    @@ -38043,21 +40203,26 @@ define("ember-views/views/collection_view", "ul", "ol", "table", "thead", "tbody", "tfoot", "tr", or "select" will result in the item views receiving an appropriately matched `tagName` property. - Given an empty `` and the following code: + Given the following application code: ```javascript - anUnorderedListView = Ember.CollectionView.create({ + var App = Ember.Application.create(); + App.UnorderedListView = Ember.CollectionView.create({ tagName: 'ul', content: ['A','B','C'], itemViewClass: Ember.View.extend({ template: Ember.Handlebars.compile("the letter: {{view.content}}") }) }); - - anUnorderedListView.appendTo('body'); ``` - Will result in the following HTML structure + And a simple application template: + + ```handlebars + {{view 'unordered-list-view'}} + ``` + + The following HTML will result: ```html
      @@ -38068,7 +40233,7 @@ define("ember-views/views/collection_view", ``` Additional `tagName` pairs can be provided by adding to - `Ember.CollectionView.CONTAINER_MAP ` + `Ember.CollectionView.CONTAINER_MAP`. For example: ```javascript Ember.CollectionView.CONTAINER_MAP['article'] = 'section' @@ -38081,7 +40246,7 @@ define("ember-views/views/collection_view", `createChildView` method can be overidden: ```javascript - CustomCollectionView = Ember.CollectionView.extend({ + App.CustomCollectionView = Ember.CollectionView.extend({ createChildView: function(viewClass, attrs) { if (attrs.content.kind == 'album') { viewClass = App.AlbumView; @@ -38101,18 +40266,23 @@ define("ember-views/views/collection_view", will be the `CollectionView`s only child. ```javascript - aListWithNothing = Ember.CollectionView.create({ - classNames: ['nothing'] + var App = Ember.Application.create(); + App.ListWithNothing = Ember.CollectionView.create({ + classNames: ['nothing'], content: null, emptyView: Ember.View.extend({ template: Ember.Handlebars.compile("The collection is empty") }) }); - - aListWithNothing.appendTo('body'); ``` - Will result in the following HTML structure + And a simple application template: + + ```handlebars + {{view 'list-with-nothing'}} + ``` + + The following HTML will result: ```html
      @@ -38270,18 +40440,8 @@ define("ember-views/views/collection_view", // Loop through child views that correspond with the removed items. // Note that we loop from the end of the array to the beginning because // we are mutating it as we go. - var childViews = this._childViews, childView, idx, len; - - len = this._childViews.length; - - var removingAll = removedCount === len; - - if (removingAll) { - this.currentState.empty(this); - this.invokeRecursively(function(view) { - view.removedFromDOM = true; - }, false); - } + var childViews = this._childViews; + var childView, idx; for (idx = start + removedCount - 1; idx >= start; idx--) { childView = childViews[idx]; @@ -38304,29 +40464,24 @@ define("ember-views/views/collection_view", @param {Number} added number of object added to content */ arrayDidChange: function(content, start, removed, added) { - var addedViews = [], view, item, idx, len, itemViewClass, - emptyView; + var addedViews = []; + var view, item, idx, len, itemViewClass, emptyView, itemViewProps; len = content ? get(content, 'length') : 0; if (len) { + itemViewProps = this._itemViewProps || {}; itemViewClass = get(this, 'itemViewClass'); - if ('string' === typeof itemViewClass && isGlobalPath(itemViewClass)) { - itemViewClass = get(itemViewClass) || itemViewClass; - } - - Ember.assert(fmt("itemViewClass must be a subclass of Ember.View, not %@", - [itemViewClass]), - 'string' === typeof itemViewClass || View.detect(itemViewClass)); + itemViewClass = readViewFactory(itemViewClass, this.container); for (idx = start; idx < start+added; idx++) { item = content.objectAt(idx); - view = this.createChildView(itemViewClass, { - content: item, - contentIndex: idx - }); + itemViewProps.content = item; + itemViewProps.contentIndex = idx; + + view = this.createChildView(itemViewClass, itemViewProps); addedViews.push(view); } @@ -38340,6 +40495,7 @@ define("ember-views/views/collection_view", } emptyView = this.createChildView(emptyView); + addedViews.push(emptyView); set(this, 'emptyView', emptyView); @@ -38403,7 +40559,7 @@ define("ember-views/views/collection_view", __exports__["default"] = CollectionView; }); -define("ember-views/views/component", +enifed("ember-views/views/component", ["ember-metal/core","ember-views/mixins/component_template_deprecation","ember-runtime/mixins/target_action_support","ember-views/views/view","ember-metal/property_get","ember-metal/property_set","ember-metal/is_none","ember-metal/computed","exports"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) { "use strict"; @@ -38416,7 +40572,7 @@ define("ember-views/views/component", var get = __dependency5__.get; var set = __dependency6__.set; - var isNone = __dependency7__.isNone; + var isNone = __dependency7__["default"]; var computed = __dependency8__.computed; @@ -38522,7 +40678,6 @@ define("ember-views/views/component", init: function() { this._super(); - set(this, 'origContext', get(this, 'context')); set(this, 'context', this); set(this, 'controller', this); }, @@ -38553,8 +40708,8 @@ define("ember-views/views/component", template: computed(function(key, value) { if (value !== undefined) { return value; } - var templateName = get(this, 'templateName'), - template = this.templateForName(templateName, 'template'); + var templateName = get(this, 'templateName'); + var template = this.templateForName(templateName, 'template'); Ember.assert("You specified the templateName " + templateName + " for " + this + ", but it did not exist.", !templateName || template); @@ -38570,18 +40725,14 @@ define("ember-views/views/component", */ templateName: null, - // during render, isolate keywords - cloneKeywords: function() { - return { - view: this, - controller: this - }; + _setupKeywords: function() { + this._keywords.view.setSource(this); }, _yield: function(context, options) { - var view = options.data.view, - parentView = this._parentView, - template = get(this, 'template'); + var view = options.data.view; + var parentView = this._parentView; + var template = get(this, 'template'); if (template) { Ember.assert("A Component must have a parent view in order to yield.", parentView); @@ -38591,9 +40742,9 @@ define("ember-views/views/component", tagName: '', _contextView: parentView, template: template, - context: options.data.insideGroup ? get(this, 'origContext') : get(parentView, 'context'), + context: get(parentView, 'context'), controller: get(parentView, 'controller'), - templateData: { keywords: parentView.cloneKeywords(), insideGroup: options.data.insideGroup } + templateData: { keywords: {} } }); } }, @@ -38692,8 +40843,8 @@ define("ember-views/views/component", @param [context] {*} a context to send with the action */ sendAction: function(action) { - var actionName, - contexts = a_slice.call(arguments, 1); + var actionName; + var contexts = a_slice.call(arguments, 1); // Send the default action if (action === undefined) { @@ -38716,14 +40867,38 @@ define("ember-views/views/component", action: actionName, actionContext: contexts }); + }, + + send: function(actionName) { + var args = [].slice.call(arguments, 1); + var target; + var hasAction = this._actions && this._actions[actionName]; + + if (hasAction) { + if (this._actions[actionName].apply(this, args) === true) { + // handler returned true, so this action will bubble + } else { + return; + } + } + + if (target = get(this, 'target')) { + Ember.assert("The `target` for " + this + " (" + target + + ") does not have a `send` method", typeof target.send === 'function'); + target.send.apply(target, arguments); + } else { + if (!hasAction) { + throw new Error(Ember.inspect(this) + ' had no action handler for: ' + actionName); + } + } } }); __exports__["default"] = Component; }); -define("ember-views/views/container_view", - ["ember-metal/core","ember-metal/merge","ember-runtime/mixins/mutable_array","ember-metal/property_get","ember-metal/property_set","ember-views/views/view","ember-views/views/view_collection","ember-views/views/states","ember-metal/error","ember-metal/enumerable_utils","ember-metal/computed","ember-metal/run_loop","ember-metal/properties","ember-views/system/render_buffer","ember-metal/mixin","ember-runtime/system/native_array","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __exports__) { +enifed("ember-views/views/container_view", + ["ember-metal/core","ember-metal/merge","ember-runtime/mixins/mutable_array","ember-metal/property_get","ember-metal/property_set","ember-views/views/view","ember-views/views/states","ember-metal/error","ember-metal/enumerable_utils","ember-metal/computed","ember-metal/run_loop","ember-metal/properties","ember-metal/mixin","ember-runtime/system/native_array","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __exports__) { "use strict"; var Ember = __dependency1__["default"]; // Ember.assert, Ember.K @@ -38734,22 +40909,20 @@ define("ember-views/views/container_view", var set = __dependency5__.set; var View = __dependency6__["default"]; - var ViewCollection = __dependency7__["default"]; - var cloneStates = __dependency8__.cloneStates; - var EmberViewStates = __dependency8__.states; + var cloneStates = __dependency7__.cloneStates; + var EmberViewStates = __dependency7__.states; - var EmberError = __dependency9__["default"]; + var EmberError = __dependency8__["default"]; - var forEach = __dependency10__.forEach; + var forEach = __dependency9__.forEach; - var computed = __dependency11__.computed; - var run = __dependency12__["default"]; - var defineProperty = __dependency13__.defineProperty; - var renderBuffer = __dependency14__["default"]; - var observer = __dependency15__.observer; - var beforeObserver = __dependency15__.beforeObserver; - var emberA = __dependency16__.A; + var computed = __dependency10__.computed; + var run = __dependency11__["default"]; + var defineProperty = __dependency12__.defineProperty; + var observer = __dependency13__.observer; + var beforeObserver = __dependency13__.beforeObserver; + var emberA = __dependency14__.A; /** @module ember @@ -38922,6 +41095,7 @@ define("ember-views/views/container_view", this._super(); var childViews = get(this, 'childViews'); + Ember.deprecate('Setting `childViews` on a Container is deprecated.', Ember.isEmpty(childViews)); // redefine view's childViews property that was obliterated defineProperty(this, 'childViews', View.childViewsProperty); @@ -38987,9 +41161,18 @@ define("ember-views/views/container_view", @param {Ember.RenderBuffer} buffer the buffer to render to */ render: function(buffer) { - this.forEachChildView(function(view) { - view.renderToBuffer(buffer); - }); + var element = buffer.element(); + var dom = buffer.dom; + + if (this.tagName === '') { + element = dom.createDocumentFragment(); + buffer._element = element; + this._childViewsMorph = dom.appendMorph(element, this._morph.contextualElement); + } else { + this._childViewsMorph = dom.createMorph(element, element.lastChild, null); + } + + return element; }, instrumentName: 'container', @@ -39099,7 +41282,9 @@ define("ember-views/views/container_view", merge(states.hasElement, { childViewsWillChange: function(view, views, start, removed) { for (var i=start; i=lengthBefore; i--) { - if (childViews[i]) { childViews[i].destroy(); } - } - }, - /** Iterates over the view's `classNameBindings` array, inserts the value of the specified property into the `classNames` array, then creates an @@ -40918,28 +42860,38 @@ define("ember-views/views/view", @private */ _applyClassNameBindings: function(classBindings) { - var classNames = this.classNames, - elem, newClass, dasherizedClass; + var classNames = this.classNames; + var elem, newClass, dasherizedClass; // Loop through all of the configured bindings. These will be either // property names ('isUrgent') or property paths relative to the view // ('content.isUrgent') forEach(classBindings, function(binding) { - Ember.assert("classNameBindings must not have spaces in them. Multiple class name bindings can be provided as elements of an array, e.g. ['foo', ':bar']", binding.indexOf(' ') === -1); + var parsedPath; + + if (typeof binding === 'string') { + Ember.assert("classNameBindings must not have spaces in them. Multiple class name bindings can be provided as elements of an array, e.g. ['foo', ':bar']", binding.indexOf(' ') === -1); + parsedPath = View._parsePropertyPath(binding); + if (parsedPath.path === '') { + parsedPath.stream = new SimpleStream(true); + } else { + parsedPath.stream = this.getStream('_view.' + parsedPath.path); + } + } else { + parsedPath = binding; + } // Variable in which the old class value is saved. The observer function // closes over this variable, so it knows which string to remove when // the property changes. var oldClass; - // Extract just the property name from bindings like 'foo:bar' - var parsedPath = View._parsePropertyPath(binding); // Set up an observer on the context. If the property changes, toggle the // class name. - var observer = function() { + var observer = this._wrapAsScheduled(function() { // Get the current value of the property - newClass = this._classStringForProperty(binding); + newClass = this._classStringForProperty(parsedPath); elem = this.$(); // If we had previously added a class to the element, remove it. @@ -40958,10 +42910,10 @@ define("ember-views/views/view", } else { oldClass = null; } - }; + }); // Get the class name for the property at its current value - dasherizedClass = this._classStringForProperty(binding); + dasherizedClass = this._classStringForProperty(parsedPath); if (dasherizedClass) { // Ensure that it gets into the classNames array @@ -40974,7 +42926,7 @@ define("ember-views/views/view", oldClass = dasherizedClass; } - this.registerObserver(this, parsedPath.path, observer); + parsedPath.stream.subscribe(observer, this); // Remove className so when the view is rerendered, // the className is added based on binding reevaluation this.one('willClearRender', function() { @@ -40998,13 +42950,15 @@ define("ember-views/views/view", @private */ _applyAttributeBindings: function(buffer, attributeBindings) { - var attributeValue, - unspecifiedAttributeBindings = this._unspecifiedAttributeBindings = this._unspecifiedAttributeBindings || {}; + var attributeValue; + var unspecifiedAttributeBindings = this._unspecifiedAttributeBindings = this._unspecifiedAttributeBindings || {}; forEach(attributeBindings, function(binding) { - var split = binding.split(':'), - property = split[0], - attributeName = split[1] || property; + var split = binding.split(':'); + var property = split[0]; + var attributeName = split[1] || property; + + Ember.assert('You cannot use class as an attributeBinding, use classNameBindings instead.', attributeName !== 'class'); if (property in this) { this._setupAttributeBindingObservation(property, attributeName); @@ -41072,16 +43026,8 @@ define("ember-views/views/view", @param property @private */ - _classStringForProperty: function(property) { - var parsedPath = View._parsePropertyPath(property); - var path = parsedPath.path; - - var val = get(this, path); - if (val === undefined && isGlobalPath(path)) { - val = get(Ember.lookup, path); - } - - return View._classStringForValue(path, val, parsedPath.className, parsedPath.falsyClassName); + _classStringForProperty: function(parsedPath) { + return View._classStringForValue(parsedPath.path, parsedPath.stream.value(), parsedPath.className, parsedPath.falsyClassName); }, // .......................................................... @@ -41094,13 +43040,7 @@ define("ember-views/views/view", @property element @type DOMElement */ - element: computed('_parentView', function(key, value) { - if (value !== undefined) { - return this.currentState.setElement(this, value); - } else { - return this.currentState.getElement(this); - } - }), + element: null, /** Returns a jQuery object for this view's element. If you pass in a selector @@ -41119,9 +43059,9 @@ define("ember-views/views/view", }, mutateChildViews: function(callback) { - var childViews = this._childViews, - idx = childViews.length, - view; + var childViews = this._childViews; + var idx = childViews.length; + var view; while(--idx >= 0) { view = childViews[idx]; @@ -41136,8 +43076,8 @@ define("ember-views/views/view", if (!childViews) { return this; } - var len = childViews.length, - view, idx; + var len = childViews.length; + var view, idx; for (idx = 0; idx < len; idx++) { view = childViews[idx]; @@ -41167,14 +43107,13 @@ define("ember-views/views/view", @param {String|DOMElement|jQuery} A selector, element, HTML string, or jQuery object @return {Ember.View} receiver */ - appendTo: function(target) { - // Schedule the DOM element to be created and appended to the given - // element after bindings have synchronized. - this._insertElementLater(function() { - Ember.assert("You tried to append to (" + target + ") but that isn't in the DOM", jQuery(target).length > 0); - Ember.assert("You cannot append to an existing Ember.View. Consider using Ember.ContainerView instead.", !jQuery(target).is('.ember-view') && !jQuery(target).parents().is('.ember-view')); - this.$().appendTo(target); - }); + appendTo: function(selector) { + var target = jQuery(selector); + + Ember.assert("You tried to append to (" + selector + ") but that isn't in the DOM", target.length > 0); + Ember.assert("You cannot append to an existing Ember.View. Consider using Ember.ContainerView instead.", !target.is('.ember-view') && !target.parents().is('.ember-view')); + + this.constructor.renderer.appendTo(this, target[0]); return this; }, @@ -41192,49 +43131,17 @@ define("ember-views/views/view", @param {String|DOMElement|jQuery} target A selector, element, HTML string, or jQuery object @return {Ember.View} received */ - replaceIn: function(target) { - Ember.assert("You tried to replace in (" + target + ") but that isn't in the DOM", jQuery(target).length > 0); - Ember.assert("You cannot replace an existing Ember.View. Consider using Ember.ContainerView instead.", !jQuery(target).is('.ember-view') && !jQuery(target).parents().is('.ember-view')); + replaceIn: function(selector) { + var target = jQuery(selector); - this._insertElementLater(function() { - jQuery(target).empty(); - this.$().appendTo(target); - }); + Ember.assert("You tried to replace in (" + selector + ") but that isn't in the DOM", target.length > 0); + Ember.assert("You cannot replace an existing Ember.View. Consider using Ember.ContainerView instead.", !target.is('.ember-view') && !target.parents().is('.ember-view')); + + this.constructor.renderer.replaceIn(this, target[0]); return this; }, - /** - Schedules a DOM operation to occur during the next render phase. This - ensures that all bindings have finished synchronizing before the view is - rendered. - - To use, pass a function that performs a DOM operation. - - Before your function is called, this view and all child views will receive - the `willInsertElement` event. After your function is invoked, this view - and all of its child views will receive the `didInsertElement` event. - - ```javascript - view._insertElementLater(function() { - this.createElement(); - this.$().appendTo('body'); - }); - ``` - - @method _insertElementLater - @param {Function} fn the function that inserts the element into the DOM - @private - */ - _insertElementLater: function(fn) { - this._scheduledInsert = run.scheduleOnce('render', this, '_insertElement', fn); - }, - - _insertElement: function (fn) { - this._scheduledInsert = null; - this.currentState.insertElement(this, fn); - }, - /** Appends the view's element to the document body. If the view does not have an HTML representation yet, `createElement()` will be called @@ -41268,9 +43175,6 @@ define("ember-views/views/view", // In the interim, we will just re-render if that happens. It is more // important than elements get garbage collected. if (!this.removedFromDOM) { this.destroyElement(); } - this.invokeRecursively(function(view) { - if (view.clearRenderedChildren) { view.clearRenderedChildren(); } - }); }, elementId: null, @@ -41292,20 +43196,20 @@ define("ember-views/views/view", }, /** - Creates a DOM representation of the view and all of its - child views by recursively calling the `render()` method. + Creates a DOM representation of the view and all of its child views by + recursively calling the `render()` method. - After the element has been created, `didInsertElement` will + After the element has been inserted into the DOM, `didInsertElement` will be called on this view and all of its child views. @method createElement @return {Ember.View} receiver */ createElement: function() { - if (get(this, 'element')) { return this; } + if (this.element) { return this; } - var buffer = this.renderToBuffer(); - set(this, 'element', buffer.element()); + this._didCreateElementWithoutMorph = true; + this.constructor.renderer.renderTree(this); return this; }, @@ -41322,6 +43226,9 @@ define("ember-views/views/view", or after the view was re-rendered. Override this function to do any set up that requires an element in the document body. + When a view has children, didInsertElement will be called on the + child view(s) first, bubbling upwards through the hierarchy. + @event didInsertElement */ didInsertElement: Ember.K, @@ -41335,65 +43242,6 @@ define("ember-views/views/view", */ willClearRender: Ember.K, - /** - Run this callback on the current view (unless includeSelf is false) and recursively on child views. - - @method invokeRecursively - @param fn {Function} - @param includeSelf {Boolean} Includes itself if true. - @private - */ - invokeRecursively: function(fn, includeSelf) { - var childViews = (includeSelf === false) ? this._childViews : [this]; - var currentViews, view, currentChildViews; - - while (childViews.length) { - currentViews = childViews.slice(); - childViews = []; - - for (var i=0, l=currentViews.length; i=0; i--) { - childViews[i].removedFromDOM = true; - } - // remove from non-virtual parent view if viewName was specified if (viewName && nonVirtualParentView) { nonVirtualParentView.set(viewName, null); } - childLen = childViews.length; - for (i=childLen-1; i>=0; i--) { - childViews[i].destroy(); - } - return this; }, @@ -41840,16 +43645,18 @@ define("ember-views/views/view", _toggleVisibility: function() { var $el = this.$(); - if (!$el) { return; } - var isVisible = get(this, 'isVisible'); if (this._isVisible === isVisible) { return ; } - $el.toggle(isVisible); - + // It's important to keep these in sync, even if we don't yet have + // an element in the DOM to manipulate: this._isVisible = isVisible; + if (!$el) { return; } + + $el.toggle(isVisible); + if (this._isAncestorHidden()) { return; } if (isVisible) { @@ -41893,10 +43700,6 @@ define("ember-views/views/view", return false; }, - - clearBuffer: function() { - this.invokeRecursively(nullViewsBuffer); - }, transitionTo: function(state, children) { Ember.deprecate("Ember.View#transitionTo has been deprecated, it is for internal use only"); this._transitionTo(state, children); @@ -41904,18 +43707,10 @@ define("ember-views/views/view", _transitionTo: function(state, children) { var priorState = this.currentState; var currentState = this.currentState = this._states[state]; - this._state = state; if (priorState && priorState.exit) { priorState.exit(this); } if (currentState.enter) { currentState.enter(this); } - if (state === 'inDOM') { meta(this).cache.element = undefined; } - - if (children !== false) { - this.forEachChildView(function(view) { - view._transitionTo(state); - }); - } }, // ....................................................... @@ -41944,22 +43739,78 @@ define("ember-views/views/view", return; } - var view = this, - stateCheckedObserver = function() { - view.currentState.invokeObserver(this, observer); - }, - scheduledObserver = function() { - run.scheduleOnce('render', this, stateCheckedObserver); - }; + var scheduledObserver = this._wrapAsScheduled(observer); addObserver(root, path, target, scheduledObserver); this.one('willClearRender', function() { removeObserver(root, path, target, scheduledObserver); }); - } + }, + _wrapAsScheduled: function(fn) { + var view = this; + var stateCheckedFn = function() { + view.currentState.invokeObserver(this, fn); + }; + var scheduledFn = function() { + run.scheduleOnce('render', this, stateCheckedFn); + }; + return scheduledFn; + }, + + getStream: function(path) { + return this._getContextStream().get(path); + }, + + _getBindingForStream: function(path) { + if (this._streamBindings === undefined) { + this._streamBindings = create(null); + this.one('willDestroyElement', this, this._destroyStreamBindings); + } + + if (this._streamBindings[path] !== undefined) { + return this._streamBindings[path]; + } else { + var stream = this._getContextStream().get(path); + return this._streamBindings[path] = new StreamBinding(stream); + } + }, + + _destroyStreamBindings: function() { + var streamBindings = this._streamBindings; + for (var path in streamBindings) { + streamBindings[path].destroy(); + } + this._streamBindings = undefined; + }, + + _getContextStream: function() { + if (this._contextStream === undefined) { + this._baseContext = new KeyStream(this, 'context'); + this._contextStream = new ContextStream(this); + this.one('willDestroyElement', this, this._destroyContextStream); + } + + return this._contextStream; + }, + + _destroyContextStream: function() { + this._baseContext.destroy(); + this._baseContext = undefined; + this._contextStream.destroy(); + this._contextStream = undefined; + }, + + _unsubscribeFromStreamBindings: function() { + for (var key in this._streamBindingSubscriptions) { + var streamBinding = this[key + 'Binding']; + var callback = this._streamBindingSubscriptions[key]; + streamBinding.unsubscribe(callback); + } + } }); + deprecateProperty(View.prototype, 'state', '_state'); deprecateProperty(View.prototype, 'states', '_states'); @@ -41990,52 +43841,6 @@ define("ember-views/views/view", // once the view has been inserted into the DOM, legal manipulations // are done on the DOM element. - function notifyMutationListeners() { - run.once(View, 'notifyMutationListeners'); - } - - var DOMManager = { - prepend: function(view, html) { - view.$().prepend(html); - notifyMutationListeners(); - }, - - after: function(view, html) { - view.$().after(html); - notifyMutationListeners(); - }, - - html: function(view, html) { - view.$().html(html); - notifyMutationListeners(); - }, - - replace: function(view) { - var element = get(view, 'element'); - - set(view, 'element', null); - - view._insertElementLater(function() { - jQuery(element).replaceWith(get(view, 'element')); - notifyMutationListeners(); - }); - }, - - remove: function(view) { - view.$().remove(); - notifyMutationListeners(); - }, - - empty: function(view) { - view.$().empty(); - notifyMutationListeners(); - } - }; - - View.reopen({ - domManager: DOMManager - }); - View.reopenClass({ /** @@ -42058,11 +43863,10 @@ define("ember-views/views/view", @private */ _parsePropertyPath: function(path) { - var split = path.split(':'), - propertyPath = split[0], - classNames = "", - className, - falsyClassName; + var split = path.split(':'); + var propertyPath = split[0]; + var classNames = ""; + var className, falsyClassName; // check if the property is defined as prop:class or prop:trueClass:falseClass if (split.length > 1) { @@ -42074,6 +43878,7 @@ define("ember-views/views/view", } return { + stream: undefined, path: propertyPath, classNames: classNames, className: (className === '') ? undefined : className, @@ -42146,7 +43951,7 @@ define("ember-views/views/view", }); var mutation = EmberObject.extend(Evented).create(); - + // TODO MOVE TO RENDERER HOOKS View.addMutationListener = function(callback) { mutation.on('change', callback); }; @@ -42204,82 +44009,11 @@ define("ember-views/views/view", __exports__["default"] = View; }); -define("ember-views/views/view_collection", - ["ember-metal/enumerable_utils","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var forEach = __dependency1__.forEach; - - function ViewCollection(initialViews) { - var views = this.views = initialViews || []; - this.length = views.length; - } - - ViewCollection.prototype = { - length: 0, - - trigger: function(eventName) { - var views = this.views, view; - for (var i = 0, l = views.length; i < l; i++) { - view = views[i]; - if (view.trigger) { view.trigger(eventName); } - } - }, - - triggerRecursively: function(eventName) { - var views = this.views; - for (var i = 0, l = views.length; i < l; i++) { - views[i].triggerRecursively(eventName); - } - }, - - invokeRecursively: function(fn) { - var views = this.views, view; - - for (var i = 0, l = views.length; i < l; i++) { - view = views[i]; - fn(view); - } - }, - - transitionTo: function(state, children) { - var views = this.views; - for (var i = 0, l = views.length; i < l; i++) { - views[i]._transitionTo(state, children); - } - }, - - push: function() { - this.length += arguments.length; - var views = this.views; - return views.push.apply(views, arguments); - }, - - objectAt: function(idx) { - return this.views[idx]; - }, - - forEach: function(callback) { - var views = this.views; - return forEach(views, callback); - }, - - clear: function() { - this.length = 0; - this.views.length = 0; - } - }; - - __exports__["default"] = ViewCollection; - }); -define("ember", +enifed("ember", ["ember-metal","ember-runtime","ember-handlebars","ember-views","ember-routing","ember-routing-handlebars","ember-application","ember-extension-support"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__) { - // Remove "use strict"; from transpiled module until - // https://bugs.webkit.org/show_bug.cgi?id=138038 is fixed - // - // REMOVE_USE_STRICT: true - + "use strict"; + /* global navigator */ // require the main entry points for each of these packages // this is so that the global exports occur properly @@ -42295,521 +44029,831 @@ define("ember", @module ember */ - function throwWithMessage(msg) { - return function() { - throw new Ember.Error(msg); - }; - } - - function generateRemovedClass(className) { - var msg = " has been moved into a plugin: https://github.com/emberjs/ember-states"; - - return { - extend: throwWithMessage(className + msg), - create: throwWithMessage(className + msg) - }; - } - - Ember.StateManager = generateRemovedClass("Ember.StateManager"); - - /** - This was exported to ember-states plugin for v 1.0.0 release. See: https://github.com/emberjs/ember-states - - @class StateManager - @namespace Ember - */ - - Ember.State = generateRemovedClass("Ember.State"); - - /** - This was exported to ember-states plugin for v 1.0.0 release. See: https://github.com/emberjs/ember-states - - @class State - @namespace Ember - */ + Ember.deprecate('Usage of Ember is deprecated for Internet Explorer 6 and 7, support will be removed in the next major version.', !navigator.userAgent.match(/MSIE [67]/)); }); -define("metamorph", - [], - function() { +enifed("morph", + ["./morph/morph","./morph/dom-helper","exports"], + function(__dependency1__, __dependency2__, __exports__) { "use strict"; - // ========================================================================== - // Project: metamorph - // Copyright: ©2014 Tilde, Inc. All rights reserved. - // ========================================================================== + var Morph = __dependency1__["default"]; + var Morph; + __exports__.Morph = Morph; + var DOMHelper = __dependency2__["default"]; + var DOMHelper; + __exports__.DOMHelper = DOMHelper; + }); +enifed("morph/dom-helper", + ["../morph/morph","./dom-helper/build-html-dom","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var Morph = __dependency1__["default"]; + var buildHTMLDOM = __dependency2__.buildHTMLDOM; + var svgNamespace = __dependency2__.svgNamespace; + var svgHTMLIntegrationPoints = __dependency2__.svgHTMLIntegrationPoints; - var K = function() {}, - guid = 0, - disableRange = (function(){ - if ('undefined' !== typeof MetamorphENV) { - return MetamorphENV.DISABLE_RANGE_API; - } else if ('undefined' !== ENV) { - return ENV.DISABLE_RANGE_API; - } else { - return false; - } - })(), + var deletesBlankTextNodes = (function(){ + var element = document.createElement('div'); + element.appendChild( document.createTextNode('') ); + var clonedElement = element.cloneNode(true); + return clonedElement.childNodes.length === 0; + })(); - // Feature-detect the W3C range API, the extended check is for IE9 which only partially supports ranges - supportsRange = (!disableRange) && typeof document !== 'undefined' && ('createRange' in document) && (typeof Range !== 'undefined') && Range.prototype.createContextualFragment, + var ignoresCheckedAttribute = (function(){ + var element = document.createElement('input'); + element.setAttribute('checked', 'checked'); + var clonedElement = element.cloneNode(false); + return !clonedElement.checked; + })(); - // Internet Explorer prior to 9 does not allow setting innerHTML if the first element - // is a "zero-scope" element. This problem can be worked around by making - // the first node an invisible text node. We, like Modernizr, use ­ - needsShy = typeof document !== 'undefined' && (function() { - var testEl = document.createElement('div'); - testEl.innerHTML = "
      "; - testEl.firstChild.innerHTML = ""; - return testEl.firstChild.innerHTML === ''; - })(), + function isSVG(ns){ + return ns === svgNamespace; + } - - // IE 8 (and likely earlier) likes to move whitespace preceeding - // a script tag to appear after it. This means that we can - // accidentally remove whitespace when updating a morph. - movesWhitespace = document && (function() { - var testEl = document.createElement('div'); - testEl.innerHTML = "Test: Value"; - return testEl.childNodes[0].nodeValue === 'Test:' && - testEl.childNodes[2].nodeValue === ' Value'; - })(); - - // Constructor that supports either Metamorph('foo') or new - // Metamorph('foo'); - // - // Takes a string of HTML as the argument. - - var Metamorph = function(html) { - var self; - - if (this instanceof Metamorph) { - self = this; + // This is not the namespace of the element, but of + // the elements inside that elements. + function interiorNamespace(element){ + if ( + element && + element.namespaceURI === svgNamespace && + !svgHTMLIntegrationPoints[element.tagName] + ) { + return svgNamespace; } else { - self = new K(); + return null; } + } - self.innerHTML = html; - var myGuid = 'metamorph-'+(guid++); - self.start = myGuid + '-start'; - self.end = myGuid + '-end'; + // The HTML spec allows for "omitted start tags". These tags are optional + // when their intended child is the first thing in the parent tag. For + // example, this is a tbody start tag: + // + // + // + // + // + // The tbody may be omitted, and the browser will accept and render: + // + //
      + // + // + // However, the omitted start tag will still be added to the DOM. Here + // we test the string and context to see if the browser is about to + // perform this cleanup. + // + // http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html#optional-tags + // describes which tags are omittable. The spec for tbody and colgroup + // explains this behavior: + // + // http://www.whatwg.org/specs/web-apps/current-work/multipage/tables.html#the-tbody-element + // http://www.whatwg.org/specs/web-apps/current-work/multipage/tables.html#the-colgroup-element + // - return self; + var omittedStartTagChildTest = /<([\w:]+)/; + function detectOmittedStartTag(string, contextualElement){ + // Omitted start tags are only inside table tags. + if (contextualElement.tagName === 'TABLE') { + var omittedStartTagChildMatch = omittedStartTagChildTest.exec(string); + if (omittedStartTagChildMatch) { + var omittedStartTagChild = omittedStartTagChildMatch[1]; + // It is already asserted that the contextual element is a table + // and not the proper start tag. Just see if a tag was omitted. + return omittedStartTagChild === 'tr' || + omittedStartTagChild === 'col'; + } + } + } + + function buildSVGDOM(html, dom){ + var div = dom.document.createElement('div'); + div.innerHTML = ''+html+''; + return div.firstChild.childNodes; + } + + /* + * A class wrapping DOM functions to address environment compatibility, + * namespaces, contextual elements for morph un-escaped content + * insertion. + * + * When entering a template, a DOMHelper should be passed: + * + * template(context, { hooks: hooks, dom: new DOMHelper() }); + * + * TODO: support foreignObject as a passed contextual element. It has + * a namespace (svg) that does not match its internal namespace + * (xhtml). + * + * @class DOMHelper + * @constructor + * @param {HTMLDocument} _document The document DOM methods are proxied to + */ + function DOMHelper(_document){ + this.document = _document || window.document; + this.namespace = null; + } + + var prototype = DOMHelper.prototype; + prototype.constructor = DOMHelper; + + prototype.insertBefore = function(element, childElement, referenceChild) { + return element.insertBefore(childElement, referenceChild); }; - K.prototype = Metamorph.prototype; - - var rangeFor, htmlFunc, removeFunc, outerHTMLFunc, appendToFunc, afterFunc, prependFunc, startTagFunc, endTagFunc; - - outerHTMLFunc = function() { - return this.startTag() + this.innerHTML + this.endTag(); + prototype.appendChild = function(element, childElement) { + return element.appendChild(childElement); }; - startTagFunc = function() { - /* - * We replace chevron by its hex code in order to prevent escaping problems. - * Check this thread for more explaination: - * http://stackoverflow.com/questions/8231048/why-use-x3c-instead-of-when-generating-html-from-javascript - */ - return "
      hi
      "; - * div.firstChild.firstChild.tagName //=> "" - * - * If our script markers are inside such a node, we need to find that - * node and use *it* as the marker. - */ - var realNode = function(start) { - while (start.parentNode.tagName === "") { - start = start.parentNode; - } - - return start; - }; - - /* - * When automatically adding a tbody, Internet Explorer inserts the - * tbody immediately before the first . Other browsers create it - * before the first node, no matter what. - * - * This means the the following code: - * - * div = document.createElement("div"); - * div.innerHTML = "
      hi
      - * - * Generates the following DOM in IE: - * - * + div - * + table - * - script id='first' - * + tbody - * + tr - * + td - * - "hi" - * - script id='last' - * - * Which means that the two script tags, even though they were - * inserted at the same point in the hierarchy in the original - * HTML, now have different parents. - * - * This code reparents the first script tag by making it the tbody's - * first child. - * - */ - var fixParentage = function(start, end) { - if (start.parentNode !== end.parentNode) { - end.parentNode.insertBefore(start, end.parentNode.firstChild); - } - }; - - htmlFunc = function(html, outerToo) { - // get the real starting node. see realNode for details. - var start = realNode(document.getElementById(this.start)); - var end = document.getElementById(this.end); - var parentNode = end.parentNode; - var node, nextSibling, last; - - // make sure that the start and end nodes share the same - // parent. If not, fix it. - fixParentage(start, end); - - // remove all of the nodes after the starting placeholder and - // before the ending placeholder. - node = start.nextSibling; - while (node) { - nextSibling = node.nextSibling; - last = node === end; - - // if this is the last node, and we want to remove it as well, - // set the `end` node to the next sibling. This is because - // for the rest of the function, we insert the new nodes - // before the end (note that insertBefore(node, null) is - // the same as appendChild(node)). - // - // if we do not want to remove it, just break. - if (last) { - if (outerToo) { end = node.nextSibling; } else { break; } - } - - node.parentNode.removeChild(node); - - // if this is the last node and we didn't break before - // (because we wanted to remove the outer nodes), break - // now. - if (last) { break; } - - node = nextSibling; - } - - // get the first node for the HTML string, even in cases like - // tables and lists where a simple innerHTML on a div would - // swallow some of the content. - node = firstNodeFor(start.parentNode, html); - - if (outerToo) { - start.parentNode.removeChild(start); - } - - // copy the nodes for the HTML between the starting and ending - // placeholder. - while (node) { - nextSibling = node.nextSibling; - parentNode.insertBefore(node, end); - node = nextSibling; - } - }; - - // remove the nodes in the DOM representing this metamorph. - // - // this includes the starting and ending placeholders. - removeFunc = function() { - var start = realNode(document.getElementById(this.start)); - var end = document.getElementById(this.end); - - this.html(''); - start.parentNode.removeChild(start); - end.parentNode.removeChild(end); - }; - - appendToFunc = function(parentNode) { - var node = firstNodeFor(parentNode, this.outerHTML()); - var nextSibling; - - while (node) { - nextSibling = node.nextSibling; - parentNode.appendChild(node); - node = nextSibling; - } - }; - - afterFunc = function(html) { - // get the real starting node. see realNode for details. - var end = document.getElementById(this.end); - var insertBefore = end.nextSibling; - var parentNode = end.parentNode; - var nextSibling; - var node; - - // get the first node for the HTML string, even in cases like - // tables and lists where a simple innerHTML on a div would - // swallow some of the content. - node = firstNodeFor(parentNode, html); - - // copy the nodes for the HTML between the starting and ending - // placeholder. - while (node) { - nextSibling = node.nextSibling; - parentNode.insertBefore(node, insertBefore); - node = nextSibling; - } - }; - - prependFunc = function(html) { - var start = document.getElementById(this.start); - var parentNode = start.parentNode; - var nextSibling; - var node; - - node = firstNodeFor(parentNode, html); - var insertBefore = start.nextSibling; - - while (node) { - nextSibling = node.nextSibling; - parentNode.insertBefore(node, insertBefore); - node = nextSibling; - } + prototype.createElement = function(tagName) { + return this.document.createElement(tagName); }; } - Metamorph.prototype.html = function(html) { - this.checkRemoved(); - if (html === undefined) { return this.innerHTML; } - - htmlFunc.call(this, html); - - this.innerHTML = html; + prototype.setNamespace = function(ns) { + this.namespace = ns; }; - Metamorph.prototype.replaceWith = function(html) { - this.checkRemoved(); - htmlFunc.call(this, html, true); + prototype.detectNamespace = function(element) { + this.namespace = interiorNamespace(element); }; - Metamorph.prototype.remove = removeFunc; - Metamorph.prototype.outerHTML = outerHTMLFunc; - Metamorph.prototype.appendTo = appendToFunc; - Metamorph.prototype.after = afterFunc; - Metamorph.prototype.prepend = prependFunc; - Metamorph.prototype.startTag = startTagFunc; - Metamorph.prototype.endTag = endTagFunc; - - Metamorph.prototype.isRemoved = function() { - var before = document.getElementById(this.start); - var after = document.getElementById(this.end); - - return !before || !after; + prototype.createDocumentFragment = function(){ + return this.document.createDocumentFragment(); }; - Metamorph.prototype.checkRemoved = function() { - if (this.isRemoved()) { - throw new Error("Cannot perform operations on a Metamorph that is not in the DOM."); + prototype.createTextNode = function(text){ + return this.document.createTextNode(text); + }; + + prototype.repairClonedNode = function(element, blankChildTextNodes, isChecked){ + if (deletesBlankTextNodes && blankChildTextNodes.length > 0) { + for (var i=0, len=blankChildTextNodes.length;i]*selected/; + detectAutoSelectedOption = function detectAutoSelectedOption(select, option, html) { //jshint ignore:line + return select.selectedIndex === 0 && + !detectAutoSelectedOptionRegex.test(html); + }; + } else { + detectAutoSelectedOption = function detectAutoSelectedOption(select, option, html) { //jshint ignore:line + var selectedAttribute = option.getAttribute('selected'); + return select.selectedIndex === 0 && ( + selectedAttribute === null || + ( selectedAttribute !== '' && selectedAttribute.toLowerCase() !== 'selected' ) + ); + }; + } + + // IE 9 and earlier don't allow us to set innerHTML on col, colgroup, frameset, + // html, style, table, tbody, tfoot, thead, title, tr. Detect this and add + // them to an initial list of corrected tags. + // + // Here we are only dealing with the ones which can have child nodes. + // + var tagNamesRequiringInnerHTMLFix, tableNeedsInnerHTMLFix; + var tableInnerHTMLTestElement = document.createElement('table'); + try { + tableInnerHTMLTestElement.innerHTML = ''; + } catch (e) { + } finally { + tableNeedsInnerHTMLFix = (tableInnerHTMLTestElement.childNodes.length === 0); + } + if (tableNeedsInnerHTMLFix) { + tagNamesRequiringInnerHTMLFix = { + colgroup: ['table'], + table: [], + tbody: ['table'], + tfoot: ['table'], + thead: ['table'], + tr: ['table', 'tbody'] + }; + } + + // IE 8 doesn't allow setting innerHTML on a select tag. Detect this and + // add it to the list of corrected tags. + // + var selectInnerHTMLTestElement = document.createElement('select'); + selectInnerHTMLTestElement.innerHTML = ''; + if (selectInnerHTMLTestElement) { + tagNamesRequiringInnerHTMLFix = tagNamesRequiringInnerHTMLFix || {}; + tagNamesRequiringInnerHTMLFix.select = []; + } + + function scriptSafeInnerHTML(element, html) { + // without a leading text node, IE will drop a leading script tag. + html = '­'+html; + + element.innerHTML = html; + + var nodes = element.childNodes; + + // Look for ­ to remove it. + var shyElement = nodes[0]; + while (shyElement.nodeType === 1 && !shyElement.nodeName) { + shyElement = shyElement.firstChild; + } + // At this point it's the actual unicode character. + if (shyElement.nodeType === 3 && shyElement.nodeValue.charAt(0) === "\u00AD") { + var newValue = shyElement.nodeValue.slice(1); + if (newValue.length) { + shyElement.nodeValue = shyElement.nodeValue.slice(1); + } else { + shyElement.parentNode.removeChild(shyElement); + } + } + + return nodes; + } + + function buildDOMWithFix(html, contextualElement){ + var tagName = contextualElement.tagName; + + // Firefox versions < 11 do not have support for element.outerHTML. + var outerHTML = contextualElement.outerHTML || new XMLSerializer().serializeToString(contextualElement); + if (!outerHTML) { + throw "Can't set innerHTML on "+tagName+" in this browser"; + } + + var wrappingTags = tagNamesRequiringInnerHTMLFix[tagName.toLowerCase()]; + var startTag = outerHTML.match(new RegExp("<"+tagName+"([^>]*)>", 'i'))[0]; + var endTag = ''; + + var wrappedHTML = [startTag, html, endTag]; + + var i = wrappingTags.length; + var wrappedDepth = 1 + i; + while(i--) { + wrappedHTML.unshift('<'+wrappingTags[i]+'>'); + wrappedHTML.push(''); + } + + var wrapper = document.createElement('div'); + scriptSafeInnerHTML(wrapper, wrappedHTML.join('')); + var element = wrapper; + while (wrappedDepth--) { + element = element.firstChild; + while (element && element.nodeType !== 1) { + element = element.nextSibling; + } + } + while (element && element.tagName !== tagName) { + element = element.nextSibling; + } + return element ? element.childNodes : []; + } + + var buildDOM; + if (needsShy) { + buildDOM = function buildDOM(html, contextualElement, dom){ + contextualElement = dom.cloneNode(contextualElement, false); + scriptSafeInnerHTML(contextualElement, html); + return contextualElement.childNodes; + }; + } else { + buildDOM = function buildDOM(html, contextualElement, dom){ + contextualElement = dom.cloneNode(contextualElement, false); + contextualElement.innerHTML = html; + return contextualElement.childNodes; + }; + } + + var buildIESafeDOM; + if (tagNamesRequiringInnerHTMLFix || movesWhitespace) { + buildIESafeDOM = function buildIESafeDOM(html, contextualElement, dom) { + // Make a list of the leading text on script nodes. Include + // script tags without any whitespace for easier processing later. + var spacesBefore = []; + var spacesAfter = []; + html = html.replace(/(\s*)()(\s*)/g, function(match, tag, spaces) { + spacesAfter.push(spaces); + return tag; + }); + + // Fetch nodes + var nodes; + if (tagNamesRequiringInnerHTMLFix[contextualElement.tagName.toLowerCase()]) { + // buildDOMWithFix uses string wrappers for problematic innerHTML. + nodes = buildDOMWithFix(html, contextualElement); + } else { + nodes = buildDOM(html, contextualElement, dom); + } + + // Build a list of script tags, the nodes themselves will be + // mutated as we add test nodes. + var i, j, node, nodeScriptNodes; + var scriptNodes = []; + for (i=0;node=nodes[i];i++) { + if (node.nodeType !== 1) { + continue; + } + if (node.tagName === 'SCRIPT') { + scriptNodes.push(node); + } else { + nodeScriptNodes = node.getElementsByTagName('script'); + for (j=0;j 0) { + textNode = dom.document.createTextNode(spaceBefore); + scriptNode.parentNode.insertBefore(textNode, scriptNode); + } + + spaceAfter = spacesAfter[i]; + if (spaceAfter && spaceAfter.length > 0) { + textNode = dom.document.createTextNode(spaceAfter); + scriptNode.parentNode.insertBefore(textNode, scriptNode.nextSibling); + } + } + + return nodes; + }; + } else { + buildIESafeDOM = buildDOM; + } + + // When parsing innerHTML, the browser may set up DOM with some things + // not desired. For example, with a select element context and option + // innerHTML the first option will be marked selected. + // + // This method cleans up some of that, resetting those values back to + // their defaults. + // + function buildSafeDOM(html, contextualElement, dom) { + var childNodes = buildIESafeDOM(html, contextualElement, dom); + + if (contextualElement.tagName === 'SELECT') { + // Walk child nodes + for (var i = 0; childNodes[i]; i++) { + // Find and process the first option child node + if (childNodes[i].tagName === 'OPTION') { + if (detectAutoSelectedOption(childNodes[i].parentNode, childNodes[i], html)) { + // If the first node is selected but does not have an attribute, + // presume it is not really selected. + childNodes[i].parentNode.selectedIndex = -1; + } + break; + } + } + } + + return childNodes; + } + + var buildHTMLDOM; + if (needsIntegrationPointFix) { + buildHTMLDOM = function buildHTMLDOM(html, contextualElement, dom){ + if (svgHTMLIntegrationPoints[contextualElement.tagName]) { + return buildSafeDOM(html, document.createElement('div'), dom); + } else { + return buildSafeDOM(html, contextualElement, dom); + } + }; + } else { + buildHTMLDOM = buildSafeDOM; + } + + __exports__.buildHTMLDOM = buildHTMLDOM; + }); +enifed("morph/morph", + ["exports"], + function(__exports__) { + "use strict"; + var splice = Array.prototype.splice; + + function ensureStartEnd(start, end) { + if (start === null || end === null) { + throw new Error('a fragment parent must have boundary nodes in order to detect insertion'); + } + } + + function ensureContext(contextualElement) { + if (!contextualElement || contextualElement.nodeType !== 1) { + throw new Error('An element node must be provided for a contextualElement, you provided ' + + (contextualElement ? 'nodeType ' + contextualElement.nodeType : 'nothing')); + } + } + + // TODO: this is an internal API, this should be an assert + function Morph(parent, start, end, domHelper, contextualElement) { + if (parent.nodeType === 11) { + ensureStartEnd(start, end); + this.element = null; + } else { + this.element = parent; + } + this._parent = parent; + this.start = start; + this.end = end; + this.domHelper = domHelper; + ensureContext(contextualElement); + this.contextualElement = contextualElement; + this.reset(); + } + + Morph.prototype.reset = function() { + this.text = null; + this.owner = null; + this.morphs = null; + this.before = null; + this.after = null; + this.escaped = true; + }; + + Morph.prototype.parent = function () { + if (!this.element) { + var parent = this.start.parentNode; + if (this._parent !== parent) { + this.element = this._parent = parent; + } + } + return this._parent; + }; + + Morph.prototype.destroy = function () { + if (this.owner) { + this.owner.removeMorph(this); + } else { + clear(this.element || this.parent(), this.start, this.end); + } + }; + + Morph.prototype.removeMorph = function (morph) { + var morphs = this.morphs; + for (var i=0, l=morphs.length; i 0 ? morphs[index-1] : null; + var after = index < morphs.length ? morphs[index] : null; + var start = before === null ? this.start : (before.end === null ? parent.lastChild : before.end.previousSibling); + var end = after === null ? this.end : (after.start === null ? parent.firstChild : after.start.nextSibling); + var morph = new Morph(parent, start, end, this.domHelper, this.contextualElement); + + morph.owner = this; + morph._update(parent, node); + + if (before !== null) { + morph.before = before; + before.end = start.nextSibling; + before.after = morph; + } + + if (after !== null) { + morph.after = after; + after.before = morph; + after.start = end.previousSibling; + } + + this.morphs.splice(index, 0, morph); + return morph; + }; + + Morph.prototype.replace = function (index, removedLength, addedNodes) { + if (this.morphs === null) this.morphs = []; + var parent = this.element || this.parent(); + var morphs = this.morphs; + var before = index > 0 ? morphs[index-1] : null; + var after = index+removedLength < morphs.length ? morphs[index+removedLength] : null; + var start = before === null ? this.start : (before.end === null ? parent.lastChild : before.end.previousSibling); + var end = after === null ? this.end : (after.start === null ? parent.firstChild : after.start.nextSibling); + var addedLength = addedNodes === undefined ? 0 : addedNodes.length; + var args, i, current; + + if (removedLength > 0) { + clear(parent, start, end); + } + + if (addedLength === 0) { + if (before !== null) { + before.after = after; + before.end = end; + } + if (after !== null) { + after.before = before; + after.start = start; + } + morphs.splice(index, removedLength); + return; + } + + args = new Array(addedLength+2); + if (addedLength > 0) { + for (i=0; i this.lengthBeforeRender) { - this.clearRenderedChildren(); - this._childViews.length = this.lengthBeforeRender; // triage bug in ember - } - - if (context) { - buffer = Ember.RenderBuffer(); - buffer = this.renderToBuffer(buffer); - - // check again for childViews, since rendering may have added some - hasChildViews = this._childViews.length > 0; - - if (hasChildViews) { - this.invokeRecursively(willInsertElementIfNeeded, false); - } - - element.innerHTML = buffer.innerString ? buffer.innerString() : backportedInnerString(buffer); - - set(this, 'element', element); - - var transitionTo = this._transitionTo ? this._transitionTo : this.transitionTo; - - transitionTo.call(this, 'inDOM'); - - if (hasChildViews) { - this.invokeRecursively(didInsertElementIfNeeded, false); - } - } else { - element.innerHTML = ''; // when there is no context, this view should be completely empty - } - } - - /** - The `Ember.ListItemView` view class renders a - [div](https://developer.mozilla.org/en/HTML/Element/div) HTML element - with `ember-list-item-view` class. It allows you to specify a custom item - handlebars template for `Ember.ListView`. - - Example: - - ```handlebars - - ``` - - ```javascript - App.ListView = Ember.ListView.extend({ - height: 500, - rowHeight: 20, - itemViewClass: Ember.ListItemView.extend({templateName: "row_item"}) - }); - ``` - - @extends Ember.View - @class ListItemView - @namespace Ember - */ - __exports__["default"] = Ember.View.extend(ListItemViewMixin, { - updateContext: function(newContext){ - var context = get(this, 'context'); - Ember.instrument('view.updateContext.render', this, function() { - if (context !== newContext) { - set(this, 'context', newContext); - if (newContext && newContext.isController) { - set(this, 'controller', newContext); - } - } - }, this); - }, - rerender: function () { - Ember.run.scheduleOnce('render', this, rerender); - }, - _contextDidChange: Ember.observer(rerender, 'context', 'controller') - }); - }); -define("list-view/list_item_view_mixin", - ["exports"], - function(__exports__) { - "use strict"; - // jshint validthis: true - - var get = Ember.get, set = Ember.set; - - function samePosition(a, b) { - return a && b && a.x === b.x && a.y === b.y; - } - - function positionElement() { - var element, position, _position; - - Ember.instrument('view.updateContext.positionElement', this, function() { - element = get(this, 'element'); - position = this.position; - _position = this._position; - - if (!position || !element) { return; } - - // TODO: avoid needing this by avoiding unnecessary - // calls to this method in the first place - if (samePosition(position, _position)) { return; } - Ember.run.schedule('render', this, this._parentView.applyTransform, element, position.x, position.y); - this._position = position; - }, this); - } - - __exports__["default"] = Ember.Mixin.create({ - init: function(){ - this._super(); - this.one('didInsertElement', positionElement); - }, - classNames: ['ember-list-item-view'], - _position: null, - updatePosition: function(position) { - this.position = position; - this._positionElement(); - }, - _positionElement: positionElement - }); - }); -define("list-view/list_view", - ["list-view/list_view_helper","list-view/list_view_mixin","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var ListViewHelper = __dependency1__["default"]; - var ListViewMixin = __dependency2__["default"]; - - var get = Ember.get, set = Ember.set; - - /** - The `Ember.ListView` view class renders a - [div](https://developer.mozilla.org/en/HTML/Element/div) HTML element, - with `ember-list-view` class. - - The context of each item element within the `Ember.ListView` are populated - from the objects in the `Element.ListView`'s `content` property. - - ### `content` as an Array of Objects - - The simplest version of an `Ember.ListView` takes an array of object as its - `content` property. The object will be used as the `context` each item element - inside the rendered `div`. - - Example: - - ```javascript - App.ContributorsRoute = Ember.Route.extend({ - model: function() { - return [{ name: 'Stefan Penner' }, { name: 'Alex Navasardyan' }, { name: 'Ray Cohen'}]; - } - }); - ``` - - ```handlebars - {{#ember-list items=contributors height=500 rowHeight=50}} - {{name}} - {{/ember-list}} - ``` - - Would result in the following HTML: - - ```html -
      -
      -
      - Stefan Penner -
      -
      - Alex Navasardyan -
      -
      - Rey Cohen -
      -
      -
      -
      - ``` - - By default `Ember.ListView` provides support for `height`, - `rowHeight`, `width`, `elementWidth`, `scrollTop` parameters. - - Note, that `height` and `rowHeight` are required parameters. - - ```handlebars - {{#ember-list items=this height=500 rowHeight=50}} - {{name}} - {{/ember-list}} - ``` - - If you would like to have multiple columns in your view layout, you can - set `width` and `elementWidth` parameters respectively. - - ```handlebars - {{#ember-list items=this height=500 rowHeight=50 width=500 elementWidth=80}} - {{name}} - {{/ember-list}} - ``` - - ### extending `Ember.ListView` - - Example: - - ```handlebars - {{view App.ListView contentBinding="content"}} - - - ``` - - ```javascript - App.ListView = Ember.ListView.extend({ - height: 500, - width: 500, - elementWidth: 80, - rowHeight: 20, - itemViewClass: Ember.ListItemView.extend({templateName: "row_item"}) - }); - ``` - - @extends Ember.ContainerView - @class ListView - @namespace Ember - */ - __exports__["default"] = Ember.ContainerView.extend(ListViewMixin, { - css: { - position: 'relative', - overflow: 'auto', - '-webkit-overflow-scrolling': 'touch', - 'overflow-scrolling': 'touch' - }, - - applyTransform: ListViewHelper.applyTransform, - - _scrollTo: function(scrollTop) { - var element = get(this, 'element'); - - if (element) { element.scrollTop = scrollTop; } - }, - - didInsertElement: function() { - var that = this; - var element = get(this, 'element'); - - this._updateScrollableHeight(); - - this._scroll = function(e) { that.scroll(e); }; - - Ember.$(element).on('scroll', this._scroll); - }, - - willDestroyElement: function() { - var element; - - element = get(this, 'element'); - - Ember.$(element).off('scroll', this._scroll); - }, - - scroll: function(e) { - this.scrollTo(e.target.scrollTop); - }, - - scrollTo: function(y){ - var element = get(this, 'element'); - this._scrollTo(y); - this._scrollContentTo(y); - }, - - totalHeightDidChange: Ember.observer(function () { - Ember.run.scheduleOnce('afterRender', this, this._updateScrollableHeight); - }, 'totalHeight'), - - _updateScrollableHeight: function () { - var height, state; - - // Support old and new Ember versions - state = this._state || this.state; - - if (state === 'inDOM') { - // if the list is currently displaying the emptyView, remove the height - if (this._isChildEmptyView()) { - height = ''; - } else { - height = get(this, 'totalHeight'); - } - - this.$('.ember-list-container').css({ - height: height - }); - } - } - }); - }); -define("list-view/list_view_helper", - ["exports"], - function(__exports__) { - "use strict"; - // TODO - remove this! - var el = document.createElement('div'), style = el.style; - - var propPrefixes = ['Webkit', 'Moz', 'O', 'ms']; - - function testProp(prop) { - if (prop in style) return prop; - var uppercaseProp = prop.charAt(0).toUpperCase() + prop.slice(1); - for (var i=0; i 1; - }).readOnly(), - - /** - @private - - Setup a mixin. - - adding observer to content array - - creating child views based on height and length of the content array - - @method init - */ - init: function() { - this._super(); - this._cachedHeights = [0]; - this.on('didInsertElement', this._syncListContainerWidth); - this.columnCountDidChange(); - this._syncChildViews(); - this._addContentArrayObserver(); - }, - - _addContentArrayObserver: Ember.beforeObserver(function() { - addContentArrayObserver.call(this); - }, 'content'), - - /** - Called on your view when it should push strings of HTML into a - `Ember.RenderBuffer`. - - Adds a [div](https://developer.mozilla.org/en-US/docs/HTML/Element/div) - with a required `ember-list-container` class. - - @method render - @param {Ember.RenderBuffer} buffer The render buffer - */ - render: function(buffer) { - buffer.push('
      '); - this._super(buffer); - buffer.push('
      '); - }, - - willInsertElement: function() { - if (!this.get("height") || !this.get("rowHeight")) { - throw new Error("A ListView must be created with a height and a rowHeight."); - } - this._super(); - }, - - /** - @private - - Sets inline styles of the view: - - height - - width - - position - - overflow - - -webkit-overflow - - overflow-scrolling - - Called while attributes binding. - - @property {Ember.ComputedProperty} style - */ - style: Ember.computed('height', 'width', function() { - var height, width, style, css; - - height = get(this, 'height'); - width = get(this, 'width'); - css = get(this, 'css'); - - style = ''; - - if (height) { - style += 'height:' + height + 'px;'; - } - - if (width) { - style += 'width:' + width + 'px;'; - } - - for ( var rule in css ) { - if (css.hasOwnProperty(rule)) { - style += rule + ':' + css[rule] + ';'; - } - } - - return style; - }), - - /** - @private - - Performs visual scrolling. Is overridden in Ember.ListView. - - @method scrollTo - */ - scrollTo: function(y) { - throw new Error('must override to perform the visual scroll and effectively delegate to _scrollContentTo'); - }, - - /** - @private - - Internal method used to force scroll position - - @method scrollTo - */ - _scrollTo: Ember.K, - - /** - @private - @method _scrollContentTo - */ - _scrollContentTo: function(y) { - var startingIndex, endingIndex, - contentIndex, visibleEndingIndex, maxContentIndex, - contentIndexEnd, contentLength, scrollTop, content; - - scrollTop = max(0, y); - - if (this.scrollTop === scrollTop) { - return; - } - - // allow a visual overscroll, but don't scroll the content. As we are doing needless - // recycyling, and adding unexpected nodes to the DOM. - var maxScrollTop = max(0, get(this, 'totalHeight') - get(this, 'height')); - scrollTop = min(scrollTop, maxScrollTop); - - content = get(this, 'content'); - contentLength = get(content, 'length'); - startingIndex = this._startingIndex(contentLength); - - Ember.instrument('view._scrollContentTo', { - scrollTop: scrollTop, - content: content, - startingIndex: startingIndex, - endingIndex: min(max(contentLength - 1, 0), startingIndex + this._numChildViewsForViewport()) - }, function () { - this.scrollTop = scrollTop; - - maxContentIndex = max(contentLength - 1, 0); - - startingIndex = this._startingIndex(); - visibleEndingIndex = startingIndex + this._numChildViewsForViewport(); - - endingIndex = min(maxContentIndex, visibleEndingIndex); - - if (startingIndex === this._lastStartingIndex && - endingIndex === this._lastEndingIndex) { - - this.trigger('scrollYChanged', y); - return; - } else { - - Ember.run(this, function() { - this._reuseChildren(); - - this._lastStartingIndex = startingIndex; - this._lastEndingIndex = endingIndex; - this.trigger('scrollYChanged', y); - }); - } - }, this); - - }, - - /** - @private - - Computes the height for a `Ember.ListView` scrollable container div. - You must specify `rowHeight` parameter for the height to be computed properly. - - @property {Ember.ComputedProperty} totalHeight - */ - totalHeight: Ember.computed('content.length', - 'rowHeight', - 'columnCount', - 'bottomPadding', function() { - if (typeof this.heightForIndex === 'function') { - return this._totalHeightWithHeightForIndex(); - } else { - return this._totalHeightWithStaticRowHeight(); - } - }), - - _doRowHeightDidChange: function() { - this._cachedHeights = [0]; - this._cachedPos = 0; - this._syncChildViews(); - }, - - _rowHeightDidChange: Ember.observer('rowHeight', function() { - Ember.run.once(this, this._doRowHeightDidChange); - }), - - _totalHeightWithHeightForIndex: function() { - var length = this.get('content.length'); - return this._cachedHeightLookup(length); - }, - - _totalHeightWithStaticRowHeight: function() { - var contentLength, rowHeight, columnCount, bottomPadding; - - contentLength = get(this, 'content.length'); - rowHeight = get(this, 'rowHeight'); - columnCount = get(this, 'columnCount'); - bottomPadding = get(this, 'bottomPadding'); - - return ((ceil(contentLength / columnCount)) * rowHeight) + bottomPadding; - }, - - /** - @private - @method _prepareChildForReuse - */ - _prepareChildForReuse: function(childView) { - childView.prepareForReuse(); - }, - - /** - @private - @method _reuseChildForContentIndex - */ - _reuseChildForContentIndex: function(childView, contentIndex) { - var content, context, newContext, childsCurrentContentIndex, position, enableProfiling, oldChildView; - - var contentViewClass = this.itemViewForIndex(contentIndex); - - if (childView.constructor !== contentViewClass) { - // rather then associative arrays, lets move childView + contentEntry maping to a Map - var i = this._childViews.indexOf(childView); - - childView.destroy(); - childView = this.createChildView(contentViewClass); - - this.insertAt(i, childView); - } - - content = get(this, 'content'); - enableProfiling = get(this, 'enableProfiling'); - position = this.positionForIndex(contentIndex); - childView.updatePosition(position); - - set(childView, 'contentIndex', contentIndex); - - if (enableProfiling) { - Ember.instrument('view._reuseChildForContentIndex', position, function() { - - }, this); - } - - newContext = content.objectAt(contentIndex); - childView.updateContext(newContext); - }, - - /** - @private - @method positionForIndex - */ - positionForIndex: function(index) { - if (typeof this.heightForIndex !== 'function') { - return this._singleHeightPosForIndex(index); - } - else { - return this._multiHeightPosForIndex(index); - } - }, - - _singleHeightPosForIndex: function(index) { - var elementWidth, width, columnCount, rowHeight, y, x; - - elementWidth = get(this, 'elementWidth') || 1; - width = get(this, 'width') || 1; - columnCount = get(this, 'columnCount'); - rowHeight = get(this, 'rowHeight'); - - y = (rowHeight * floor(index/columnCount)); - x = (index % columnCount) * elementWidth; - - return { - y: y, - x: x - }; - }, - - // 0 maps to 0, 1 maps to heightForIndex(i) - _multiHeightPosForIndex: function(index) { - var elementWidth, width, columnCount, rowHeight, y, x; - - elementWidth = get(this, 'elementWidth') || 1; - width = get(this, 'width') || 1; - columnCount = get(this, 'columnCount'); - - x = (index % columnCount) * elementWidth; - y = this._cachedHeightLookup(index); - - return { - x: x, - y: y - }; - }, - - _cachedHeightLookup: function(index) { - for (var i = this._cachedPos; i < index; i++) { - this._cachedHeights[i + 1] = this._cachedHeights[i] + this.heightForIndex(i); - } - this._cachedPos = i; - return this._cachedHeights[index]; - }, - - /** - @private - @method _childViewCount - */ - _childViewCount: function() { - var contentLength, childViewCountForHeight; - - contentLength = get(this, 'content.length'); - childViewCountForHeight = this._numChildViewsForViewport(); - - return min(contentLength, childViewCountForHeight); - }, - - /** - @private - - Returns a number of columns in the Ember.ListView (for grid layout). - - If you want to have a multi column layout, you need to specify both - `width` and `elementWidth`. - - If no `elementWidth` is specified, it returns `1`. Otherwise, it will - try to fit as many columns as possible for a given `width`. - - @property {Ember.ComputedProperty} columnCount - */ - columnCount: Ember.computed('width', 'elementWidth', function() { - var elementWidth, width, count; - - elementWidth = get(this, 'elementWidth'); - width = get(this, 'width'); - - if (elementWidth && width > elementWidth) { - count = floor(width / elementWidth); - } else { - count = 1; - } - - return count; - }), - - /** - @private - - Fires every time column count is changed. - - @event columnCountDidChange - */ - columnCountDidChange: Ember.observer(function() { - var ratio, currentScrollTop, proposedScrollTop, maxScrollTop, - scrollTop, lastColumnCount, newColumnCount, element; - - lastColumnCount = this._lastColumnCount; - - currentScrollTop = this.scrollTop; - newColumnCount = get(this, 'columnCount'); - maxScrollTop = get(this, 'maxScrollTop'); - element = get(this, 'element'); - - this._lastColumnCount = newColumnCount; - - if (lastColumnCount) { - ratio = (lastColumnCount / newColumnCount); - proposedScrollTop = currentScrollTop * ratio; - scrollTop = min(maxScrollTop, proposedScrollTop); - - this._scrollTo(scrollTop); - this.scrollTop = scrollTop; - } - - if (arguments.length > 0) { - // invoked by observer - Ember.run.schedule('afterRender', this, this._syncListContainerWidth); - } - }, 'columnCount'), - - /** - @private - - Computes max possible scrollTop value given the visible viewport - and scrollable container div height. - - @property {Ember.ComputedProperty} maxScrollTop - */ - maxScrollTop: Ember.computed('height', 'totalHeight', function(){ - var totalHeight, viewportHeight; - - totalHeight = get(this, 'totalHeight'); - viewportHeight = get(this, 'height'); - - return max(0, totalHeight - viewportHeight); - }), - - /** - @private - - Determines whether the emptyView is the current childView. - - @method _isChildEmptyView - */ - _isChildEmptyView: function() { - var emptyView = get(this, 'emptyView'); - - return emptyView && emptyView instanceof Ember.View && - this._childViews.length === 1 && this._childViews.indexOf(emptyView) === 0; - }, - - /** - @private - - Computes the number of views that would fit in the viewport area. - You must specify `height` and `rowHeight` parameters for the number of - views to be computed properly. - - @method _numChildViewsForViewport - */ - _numChildViewsForViewport: function() { - - if (this.heightForIndex) { - return this._numChildViewsForViewportWithMultiHeight(); - } else { - return this._numChildViewsForViewportWithoutMultiHeight(); - } - }, - - _numChildViewsForViewportWithoutMultiHeight: function() { - var height, rowHeight, paddingCount, columnCount; - - height = get(this, 'height'); - rowHeight = get(this, 'rowHeight'); - paddingCount = get(this, 'paddingCount'); - columnCount = get(this, 'columnCount'); - - return (ceil(height / rowHeight) * columnCount) + (paddingCount * columnCount); - }, - - _numChildViewsForViewportWithMultiHeight: function() { - var rowHeight, paddingCount, columnCount; - var scrollTop = this.scrollTop; - var viewportHeight = this.get('height'); - var length = this.get('content.length'); - var heightfromTop = 0; - var padding = get(this, 'paddingCount'); - - var startingIndex = this._calculatedStartingIndex(); - var currentHeight = 0; - - var offsetHeight = this._cachedHeightLookup(startingIndex); - for (var i = 0; i < length; i++) { - if (this._cachedHeightLookup(startingIndex + i + 1) - offsetHeight > viewportHeight) { - break; - } - } - - return i + padding + 1; - }, - - - /** - @private - - Computes the starting index of the item views array. - Takes `scrollTop` property of the element into account. - - Is used in `_syncChildViews`. - - @method _startingIndex - */ - _startingIndex: function(_contentLength) { - var scrollTop, rowHeight, columnCount, calculatedStartingIndex, - contentLength; - - if (_contentLength === undefined) { - contentLength = get(this, 'content.length'); - } else { - contentLength = _contentLength; - } - - scrollTop = this.scrollTop; - rowHeight = get(this, 'rowHeight'); - columnCount = get(this, 'columnCount'); - - if (this.heightForIndex) { - calculatedStartingIndex = this._calculatedStartingIndex(); - } else { - calculatedStartingIndex = floor(scrollTop / rowHeight) * columnCount; - } - - var viewsNeededForViewport = this._numChildViewsForViewport(); - var paddingCount = (1 * columnCount); - var largestStartingIndex = max(contentLength - viewsNeededForViewport, 0); - - return min(calculatedStartingIndex, largestStartingIndex); - }, - - _calculatedStartingIndex: function() { - var rowHeight, paddingCount, columnCount; - var scrollTop = this.scrollTop; - var viewportHeight = this.get('height'); - var length = this.get('content.length'); - var heightfromTop = 0; - var padding = get(this, 'paddingCount'); - - for (var i = 0; i < length; i++) { - if (this._cachedHeightLookup(i + 1) >= scrollTop) { - break; - } - } - - return i; - }, - - /** - @private - @event contentWillChange - */ - contentWillChange: Ember.beforeObserver(function() { - var content; - - content = get(this, 'content'); - - if (content) { - content.removeArrayObserver(this); - } - }, 'content'), - - /**), - @private - @event contentDidChange - */ - contentDidChange: Ember.observer(function() { - addContentArrayObserver.call(this); - syncChildViews.call(this); - }, 'content'), - - /** - @private - @property {Function} needsSyncChildViews - */ - needsSyncChildViews: Ember.observer(syncChildViews, 'height', 'width', 'columnCount'), - - /** - @private - - Returns a new item view. Takes `contentIndex` to set the context - of the returned view properly. - - @param {Number} contentIndex item index in the content array - @method _addItemView - */ - _addItemView: function(contentIndex){ - var itemViewClass, childView; - - itemViewClass = this.itemViewForIndex(contentIndex); - childView = this.createChildView(itemViewClass); - - this.pushObject(childView); - }, - - /** - @public - - Returns a view class for the provided contentIndex. If the view is - different then the one currently present it will remove the existing view - and replace it with an instance of the class provided - - @param {Number} contentIndex item index in the content array - @method _addItemView - @returns {Ember.View} ember view class for this index - */ - itemViewForIndex: function(contentIndex) { - return get(this, 'itemViewClass'); - }, - - /** - @public - - Returns a view class for the provided contentIndex. If the view is - different then the one currently present it will remove the existing view - and replace it with an instance of the class provided - - @param {Number} contentIndex item index in the content array - @method _addItemView - @returns {Ember.View} ember view class for this index - */ - heightForIndex: null, - - /** - @private - - Intelligently manages the number of childviews. - - @method _syncChildViews - **/ - _syncChildViews: function(){ - var childViews, childViewCount, - numberOfChildViews, numberOfChildViewsNeeded, - contentIndex, startingIndex, endingIndex, - contentLength, emptyView, count, delta; - - if (get(this, 'isDestroyed') || get(this, 'isDestroying')) { - return; - } - - contentLength = get(this, 'content.length'); - emptyView = get(this, 'emptyView'); - - childViewCount = this._childViewCount(); - childViews = this.positionOrderedChildViews(); - - if (this._isChildEmptyView()) { - removeEmptyView.call(this); - } - - startingIndex = this._startingIndex(); - endingIndex = startingIndex + childViewCount; - - numberOfChildViewsNeeded = childViewCount; - numberOfChildViews = childViews.length; - - delta = numberOfChildViewsNeeded - numberOfChildViews; - - if (delta === 0) { - // no change - } else if (delta > 0) { - // more views are needed - contentIndex = this._lastEndingIndex; - - for (count = 0; count < delta; count++, contentIndex++) { - this._addItemView(contentIndex); - } - } else { - // less views are needed - forEach.call( - childViews.splice(numberOfChildViewsNeeded, numberOfChildViews), - removeAndDestroy, - this - ); - } - - this._reuseChildren(); - - this._lastStartingIndex = startingIndex; - this._lastEndingIndex = this._lastEndingIndex + delta; - - if (contentLength === 0 || contentLength === undefined) { - addEmptyView.call(this); - } - }, - - /** - @private - - Applies an inline width style to the list container. - - @method _syncListContainerWidth - **/ - _syncListContainerWidth: function() { - var elementWidth, columnCount, containerWidth, element; - - elementWidth = get(this, 'elementWidth'); - columnCount = get(this, 'columnCount'); - containerWidth = elementWidth * columnCount; - element = this.$('.ember-list-container'); - - if (containerWidth && element) { - element.css('width', containerWidth); - } - }, - - /** - @private - @method _reuseChildren - */ - _reuseChildren: function(){ - var contentLength, childViews, childViewsLength, - startingIndex, endingIndex, childView, attrs, - contentIndex, visibleEndingIndex, maxContentIndex, - contentIndexEnd, scrollTop; - - scrollTop = this.scrollTop; - contentLength = get(this, 'content.length'); - maxContentIndex = max(contentLength - 1, 0); - childViews = this.getReusableChildViews(); - childViewsLength = childViews.length; - - startingIndex = this._startingIndex(); - visibleEndingIndex = startingIndex + this._numChildViewsForViewport(); - - endingIndex = min(maxContentIndex, visibleEndingIndex); - - contentIndexEnd = min(visibleEndingIndex, startingIndex + childViewsLength); - - for (contentIndex = startingIndex; contentIndex < contentIndexEnd; contentIndex++) { - childView = childViews[contentIndex % childViewsLength]; - this._reuseChildForContentIndex(childView, contentIndex); - } - }, - - /** - @private - @method getReusableChildViews - */ - getReusableChildViews: function() { - return this._childViews; - }, - - /** - @private - @method positionOrderedChildViews - */ - positionOrderedChildViews: function() { - return this.getReusableChildViews().sort(sortByContentIndex); - }, - - arrayWillChange: Ember.K, - - /** - @private - @event arrayDidChange - */ - // TODO: refactor - arrayDidChange: function(content, start, removedCount, addedCount) { - var index, contentIndex, state; - - if (this._isChildEmptyView()) { - removeEmptyView.call(this); - } - - // Support old and new Ember versions - state = this._state || this.state; - - if (state === 'inDOM') { - // ignore if all changes are out of the visible change - if (start >= this._lastStartingIndex || start < this._lastEndingIndex) { - index = 0; - // ignore all changes not in the visible range - // this can re-position many, rather then causing a cascade of re-renders - forEach.call( - this.positionOrderedChildViews(), - function(childView) { - contentIndex = this._lastStartingIndex + index; - this._reuseChildForContentIndex(childView, contentIndex); - index++; - }, - this - ); - } - - syncChildViews.call(this); - } - }, - - destroy: function () { - if (!this._super()) { return; } - - if (this._createdEmptyView) { - this._createdEmptyView.destroy(); - } - - return this; - } - }); - }); -define("list-view/main", - ["list-view/reusable_list_item_view","list-view/virtual_list_view","list-view/list_item_view","list-view/helper","list-view/list_view","list-view/list_view_helper"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__) { - "use strict"; - var ReusableListItemView = __dependency1__["default"]; - var VirtualListView = __dependency2__["default"]; - var ListItemView = __dependency3__["default"]; - var EmberList = __dependency4__["default"]; - var ListView = __dependency5__["default"]; - var ListViewHelper = __dependency6__["default"]; - - Ember.ReusableListItemView = ReusableListItemView; - Ember.VirtualListView = VirtualListView; - Ember.ListItemView = ListItemView; - Ember.ListView = ListView; - Ember.ListViewHelper = ListViewHelper; - - Ember.Handlebars.registerHelper('ember-list', EmberList); - }); -define("list-view/reusable_list_item_view", - ["list-view/list_item_view_mixin","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var ListItemViewMixin = __dependency1__["default"]; - - var get = Ember.get, set = Ember.set; - - __exports__["default"] = Ember.View.extend(ListItemViewMixin, { - init: function(){ - this._super(); - var context = Ember.ObjectProxy.create(); - this.set('context', context); - this._proxyContext = context; - }, - isVisible: Ember.computed('context.content', function(){ - return !!this.get('context.content'); - }), - updateContext: function(newContext){ - var context = get(this._proxyContext, 'content'), state; - - // Support old and new Ember versions - state = this._state || this.state; - - if (context !== newContext) { - if (state === 'inDOM') { - this.prepareForReuse(newContext); - } - - set(this._proxyContext, 'content', newContext); - - if (newContext && newContext.isController) { - set(this, 'controller', newContext); - } - } - }, - prepareForReuse: Ember.K - }); - }); -define("list-view/virtual_list_scroller_events", - ["exports"], - function(__exports__) { - "use strict"; - // jshint validthis: true - var fieldRegex = /input|textarea|select/i, - hasTouch = ('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch, - handleStart, handleMove, handleEnd, handleCancel, - startEvent, moveEvent, endEvent, cancelEvent; - if (hasTouch) { - startEvent = 'touchstart'; - handleStart = function (e) { - var touch = e.touches[0], - target = touch && touch.target; - // avoid e.preventDefault() on fields - if (target && fieldRegex.test(target.tagName)) { - return; - } - bindWindow(this.scrollerEventHandlers); - this.willBeginScroll(e.touches, e.timeStamp); - e.preventDefault(); - }; - moveEvent = 'touchmove'; - handleMove = function (e) { - this.continueScroll(e.touches, e.timeStamp); - }; - endEvent = 'touchend'; - handleEnd = function (e) { - // if we didn't end up scrolling we need to - // synthesize click since we did e.preventDefault() - // on touchstart - if (!this._isScrolling) { - synthesizeClick(e); - } - unbindWindow(this.scrollerEventHandlers); - this.endScroll(e.timeStamp); - }; - cancelEvent = 'touchcancel'; - handleCancel = function (e) { - unbindWindow(this.scrollerEventHandlers); - this.endScroll(e.timeStamp); - }; - } else { - startEvent = 'mousedown'; - handleStart = function (e) { - if (e.which !== 1) return; - var target = e.target; - // avoid e.preventDefault() on fields - if (target && fieldRegex.test(target.tagName)) { - return; - } - bindWindow(this.scrollerEventHandlers); - this.willBeginScroll([e], e.timeStamp); - e.preventDefault(); - }; - moveEvent = 'mousemove'; - handleMove = function (e) { - this.continueScroll([e], e.timeStamp); - }; - endEvent = 'mouseup'; - handleEnd = function (e) { - unbindWindow(this.scrollerEventHandlers); - this.endScroll(e.timeStamp); - }; - cancelEvent = 'mouseout'; - handleCancel = function (e) { - if (e.relatedTarget) return; - unbindWindow(this.scrollerEventHandlers); - this.endScroll(e.timeStamp); - }; - } - - function handleWheel(e) { - this.mouseWheel(e); - e.preventDefault(); - } - - function bindElement(el, handlers) { - el.addEventListener(startEvent, handlers.start, false); - el.addEventListener('mousewheel', handlers.wheel, false); - } - - function unbindElement(el, handlers) { - el.removeEventListener(startEvent, handlers.start, false); - el.removeEventListener('mousewheel', handlers.wheel, false); - } - - function bindWindow(handlers) { - window.addEventListener(moveEvent, handlers.move, true); - window.addEventListener(endEvent, handlers.end, true); - window.addEventListener(cancelEvent, handlers.cancel, true); - } - - function unbindWindow(handlers) { - window.removeEventListener(moveEvent, handlers.move, true); - window.removeEventListener(endEvent, handlers.end, true); - window.removeEventListener(cancelEvent, handlers.cancel, true); - } - - __exports__["default"] = Ember.Mixin.create({ - init: function() { - this.on('didInsertElement', this, 'bindScrollerEvents'); - this.on('willDestroyElement', this, 'unbindScrollerEvents'); - this.scrollerEventHandlers = { - start: bind(this, handleStart), - move: bind(this, handleMove), - end: bind(this, handleEnd), - cancel: bind(this, handleCancel), - wheel: bind(this, handleWheel) - }; - return this._super(); - }, - scrollElement: Ember.computed.oneWay('element').readOnly(), - bindScrollerEvents: function() { - var el = this.get('scrollElement'), - handlers = this.scrollerEventHandlers; - bindElement(el, handlers); - }, - unbindScrollerEvents: function() { - var el = this.get('scrollElement'), - handlers = this.scrollerEventHandlers; - unbindElement(el, handlers); - unbindWindow(handlers); - } - }); - - function bind(view, handler) { - return function (evt) { - handler.call(view, evt); - }; - } - - function synthesizeClick(e) { - var point = e.changedTouches[0], - target = point.target, - ev; - if (target && fieldRegex.test(target.tagName)) { - ev = document.createEvent('MouseEvents'); - ev.initMouseEvent('click', true, true, e.view, 1, point.screenX, point.screenY, point.clientX, point.clientY, e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, 0, null); - return target.dispatchEvent(ev); - } - } - }); -define("list-view/virtual_list_view", - ["list-view/list_view_mixin","list-view/list_view_helper","list-view/virtual_list_scroller_events","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - /* - global Scroller - */ - - var ListViewMixin = __dependency1__["default"]; - var ListViewHelper = __dependency2__["default"]; - var VirtualListScrollerEvents = __dependency3__["default"]; - - var max = Math.max, get = Ember.get, set = Ember.set; - - function updateScrollerDimensions(target) { - var width, height, totalHeight; - - target = target || this; // jshint ignore:line - - width = get(target, 'width'); - height = get(target, 'height'); - totalHeight = get(target, 'totalHeight'); // jshint ignore:line - - target.scroller.setDimensions(width, height, width, totalHeight); - target.trigger('scrollerDimensionsDidChange'); - } - - /** - VirtualListView - - @class VirtualListView - @namespace Ember - */ - __exports__["default"] = Ember.ContainerView.extend(ListViewMixin, VirtualListScrollerEvents, { - _isScrolling: false, - _mouseWheel: null, - css: { - position: 'relative', - overflow: 'hidden' - }, - - init: function(){ - this._super(); - this.setupScroller(); - this.setupPullToRefresh(); - }, - _scrollerTop: 0, - applyTransform: ListViewHelper.apply3DTransform, - - setupScroller: function(){ - var view, y; - - view = this; - - view.scroller = new Scroller(function(left, top, zoom) { - // Support old and new Ember versions - var state = view._state || view.state; - - if (state !== 'inDOM') { return; } - - if (view.listContainerElement) { - view._scrollerTop = top; - view._scrollContentTo(top); - view.applyTransform(view.listContainerElement, 0, -top); - } - }, { - scrollingX: false, - scrollingComplete: function(){ - view.trigger('scrollingDidComplete'); - } - }); - - view.trigger('didInitializeScroller'); - updateScrollerDimensions(view); - }, - setupPullToRefresh: function() { - if (!this.pullToRefreshViewClass) { return; } - this._insertPullToRefreshView(); - this._activateScrollerPullToRefresh(); - }, - _insertPullToRefreshView: function(){ - this.pullToRefreshView = this.createChildView(this.pullToRefreshViewClass); - this.insertAt(0, this.pullToRefreshView); - var view = this; - this.pullToRefreshView.on('didInsertElement', function(){ - Ember.run.schedule('afterRender', this, function(){ - view.applyTransform(this.get('element'), 0, -1 * view.pullToRefreshViewHeight); - }); - }); - }, - _activateScrollerPullToRefresh: function(){ - var view = this; - function activatePullToRefresh(){ - view.pullToRefreshView.set('active', true); - view.trigger('activatePullToRefresh'); - } - function deactivatePullToRefresh() { - view.pullToRefreshView.set('active', false); - view.trigger('deactivatePullToRefresh'); - } - function startPullToRefresh() { - Ember.run(function(){ - view.pullToRefreshView.set('refreshing', true); - - function finishRefresh(){ - if (view && !view.get('isDestroyed') && !view.get('isDestroying')) { - view.scroller.finishPullToRefresh(); - view.pullToRefreshView.set('refreshing', false); - } - } - view.startRefresh(finishRefresh); - }); - } - this.scroller.activatePullToRefresh( - this.pullToRefreshViewHeight, - activatePullToRefresh, - deactivatePullToRefresh, - startPullToRefresh - ); - }, - - getReusableChildViews: function(){ - var firstView = this._childViews[0]; - if (firstView && firstView === this.pullToRefreshView) { - return this._childViews.slice(1); - } else { - return this._childViews; - } - }, - - scrollerDimensionsNeedToChange: Ember.observer(function() { - Ember.run.once(this, updateScrollerDimensions); - }, 'width', 'height', 'totalHeight'), - - didInsertElement: function() { - this.listContainerElement = this.$('> .ember-list-container')[0]; - }, - - willBeginScroll: function(touches, timeStamp) { - this._isScrolling = false; - this.trigger('scrollingDidStart'); - - this.scroller.doTouchStart(touches, timeStamp); - }, - - continueScroll: function(touches, timeStamp) { - var startingScrollTop, endingScrollTop, event; - - if (this._isScrolling) { - this.scroller.doTouchMove(touches, timeStamp); - } else { - startingScrollTop = this._scrollerTop; - - this.scroller.doTouchMove(touches, timeStamp); - - endingScrollTop = this._scrollerTop; - - if (startingScrollTop !== endingScrollTop) { - event = Ember.$.Event("scrollerstart"); - Ember.$(touches[0].target).trigger(event); - - this._isScrolling = true; - } - } - }, - - endScroll: function(timeStamp) { - this.scroller.doTouchEnd(timeStamp); - }, - - // api - scrollTo: function(y, animate) { - if (animate === undefined) { - animate = true; - } - - this.scroller.scrollTo(0, y, animate, 1); - }, - - // events - mouseWheel: function(e){ - var inverted, delta, candidatePosition; - - inverted = e.webkitDirectionInvertedFromDevice; - delta = e.wheelDeltaY * (inverted ? 0.8 : -0.8); - candidatePosition = this.scroller.__scrollTop + delta; - - if ((candidatePosition >= 0) && (candidatePosition <= this.scroller.__maxScrollTop)) { - this.scroller.scrollBy(0, delta, true); - e.stopPropagation(); - } - - return false; - } - }); - }); - requireModule('list-view/main'); -})(this); \ No newline at end of file diff --git a/vendor/assets/javascripts/ember-cloaking.js b/vendor/assets/javascripts/ember-cloaking.js index 07b3506def..cc698f39d1 100644 --- a/vendor/assets/javascripts/ember-cloaking.js +++ b/vendor/assets/javascripts/ember-cloaking.js @@ -8,10 +8,13 @@ @namespace Ember **/ Ember.CloakedCollectionView = Ember.CollectionView.extend({ + cloakView: Ember.computed.alias('itemViewClass'), topVisible: null, bottomVisible: null, offsetFixedTopElement: null, offsetFixedBottomElement: null, + loadingHTML: 'Loading...', + scrollDebounce: 10, init: function() { var cloakView = this.get('cloakView'), @@ -25,7 +28,7 @@ this.set('itemViewClass', Ember.CloakedView.extend({ classNames: [cloakView + '-cloak'], cloaks: cloakView, - preservesContext: this.get('preservesContext') === "true", + preservesContext: this.get('preservesContext') === 'true', cloaksController: this.get('itemController'), defaultHeight: this.get('defaultHeight'), @@ -198,13 +201,13 @@ for (var j=bottomView; j"'`]/; function escapeChar(chr) { - return escape[chr] || "&"; + return escape[chr]; } - function extend(obj, value) { - for(var key in value) { - if(Object.prototype.hasOwnProperty.call(value, key)) { - obj[key] = value[key]; + function extend(obj /* , ...source */) { + for (var i = 1; i < arguments.length; i++) { + for (var key in arguments[i]) { + if (Object.prototype.hasOwnProperty.call(arguments[i], key)) { + obj[key] = arguments[i][key]; + } } } + + return obj; } __exports__.extend = extend;var toString = Object.prototype.toString; @@ -82,6 +94,7 @@ var __module3__ = (function(__dependency1__) { return typeof value === 'function'; }; // fallback for older versions of Chrome and Safari + /* istanbul ignore next */ if (isFunction(/x/)) { isFunction = function(value) { return typeof value === 'function' && toString.call(value) === '[object Function]'; @@ -89,6 +102,7 @@ var __module3__ = (function(__dependency1__) { } var isFunction; __exports__.isFunction = isFunction; + /* istanbul ignore next */ var isArray = Array.isArray || function(value) { return (value && typeof value === 'object') ? toString.call(value) === '[object Array]' : false; }; @@ -98,8 +112,10 @@ var __module3__ = (function(__dependency1__) { // don't escape SafeStrings, since they're already safe if (string instanceof SafeString) { return string.toString(); - } else if (!string && string !== 0) { + } else if (string == null) { return ""; + } else if (!string) { + return string + ''; } // Force a string conversion as this will be done by the append regardless and @@ -121,7 +137,11 @@ var __module3__ = (function(__dependency1__) { } } - __exports__.isEmpty = isEmpty; + __exports__.isEmpty = isEmpty;function appendContextPath(contextPath, id) { + return (contextPath ? contextPath + '.' : '') + id; + } + + __exports__.appendContextPath = appendContextPath; return __exports__; })(__module4__); @@ -166,14 +186,16 @@ var __module2__ = (function(__dependency1__, __dependency2__) { var Utils = __dependency1__; var Exception = __dependency2__; - var VERSION = "1.3.0"; - __exports__.VERSION = VERSION;var COMPILER_REVISION = 4; + var VERSION = "2.0.0"; + __exports__.VERSION = VERSION;var COMPILER_REVISION = 6; __exports__.COMPILER_REVISION = COMPILER_REVISION; var REVISION_CHANGES = { 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it 2: '== 1.0.0-rc.3', 3: '== 1.0.0-rc.4', - 4: '>= 1.0.0' + 4: '== 1.x.x', + 5: '== 2.0.0-alpha.x', + 6: '>= 2.0.0-beta.1' }; __exports__.REVISION_CHANGES = REVISION_CHANGES; var isArray = Utils.isArray, @@ -194,38 +216,44 @@ var __module2__ = (function(__dependency1__, __dependency2__) { logger: logger, log: log, - registerHelper: function(name, fn, inverse) { + registerHelper: function(name, fn) { if (toString.call(name) === objectType) { - if (inverse || fn) { throw new Exception('Arg not supported with multiple helpers'); } + if (fn) { throw new Exception('Arg not supported with multiple helpers'); } Utils.extend(this.helpers, name); } else { - if (inverse) { fn.not = inverse; } this.helpers[name] = fn; } }, + unregisterHelper: function(name) { + delete this.helpers[name]; + }, - registerPartial: function(name, str) { + registerPartial: function(name, partial) { if (toString.call(name) === objectType) { Utils.extend(this.partials, name); } else { - this.partials[name] = str; + this.partials[name] = partial; } + }, + unregisterPartial: function(name) { + delete this.partials[name]; } }; function registerDefaultHelpers(instance) { - instance.registerHelper('helperMissing', function(arg) { - if(arguments.length === 2) { + instance.registerHelper('helperMissing', function(/* [args, ]options */) { + if(arguments.length === 1) { + // A missing field in a {{foo}} constuct. return undefined; } else { - throw new Exception("Missing helper: '" + arg + "'"); + // Someone is actually trying to call something, blow up. + throw new Exception("Missing helper: '" + arguments[arguments.length-1].name + "'"); } }); instance.registerHelper('blockHelperMissing', function(context, options) { - var inverse = options.inverse || function() {}, fn = options.fn; - - if (isFunction(context)) { context = context.call(this); } + var inverse = options.inverse, + fn = options.fn; if(context === true) { return fn(this); @@ -233,19 +261,38 @@ var __module2__ = (function(__dependency1__, __dependency2__) { return inverse(this); } else if (isArray(context)) { if(context.length > 0) { + if (options.ids) { + options.ids = [options.name]; + } + return instance.helpers.each(context, options); } else { return inverse(this); } } else { - return fn(context); + if (options.data && options.ids) { + var data = createFrame(options.data); + data.contextPath = Utils.appendContextPath(options.data.contextPath, options.name); + options = {data: data}; + } + + return fn(context, options); } }); instance.registerHelper('each', function(context, options) { + if (!options) { + throw new Exception('Must pass iterator to #each'); + } + var fn = options.fn, inverse = options.inverse; var i = 0, ret = "", data; + var contextPath; + if (options.data && options.ids) { + contextPath = Utils.appendContextPath(options.data.contextPath, options.ids[0]) + '.'; + } + if (isFunction(context)) { context = context.call(this); } if (options.data) { @@ -259,16 +306,24 @@ var __module2__ = (function(__dependency1__, __dependency2__) { data.index = i; data.first = (i === 0); data.last = (i === (context.length-1)); + + if (contextPath) { + data.contextPath = contextPath + i; + } } ret = ret + fn(context[i], { data: data }); } } else { for(var key in context) { if(context.hasOwnProperty(key)) { - if(data) { - data.key = key; + if(data) { + data.key = key; data.index = i; data.first = (i === 0); + + if (contextPath) { + data.contextPath = contextPath + key; + } } ret = ret + fn(context[key], {data: data}); i++; @@ -304,12 +359,28 @@ var __module2__ = (function(__dependency1__, __dependency2__) { instance.registerHelper('with', function(context, options) { if (isFunction(context)) { context = context.call(this); } - if (!Utils.isEmpty(context)) return options.fn(context); + var fn = options.fn; + + if (!Utils.isEmpty(context)) { + if (options.data && options.ids) { + var data = createFrame(options.data); + data.contextPath = Utils.appendContextPath(options.data.contextPath, options.ids[0]); + options = {data:data}; + } + + return fn(context, options); + } else { + return options.inverse(this); + } }); - instance.registerHelper('log', function(context, options) { + instance.registerHelper('log', function(message, options) { var level = options.data && options.data.level != null ? parseInt(options.data.level, 10) : 1; - instance.log(level, context); + instance.log(level, message); + }); + + instance.registerHelper('lookup', function(obj, field) { + return obj && obj[field]; }); } @@ -324,22 +395,22 @@ var __module2__ = (function(__dependency1__, __dependency2__) { level: 3, // can be overridden in the host environment - log: function(level, obj) { + log: function(level, message) { if (logger.level <= level) { var method = logger.methodMap[level]; if (typeof console !== 'undefined' && console[method]) { - console[method].call(console, obj); + console[method].call(console, message); } } } }; __exports__.logger = logger; - function log(level, obj) { logger.log(level, obj); } - - __exports__.log = log;var createFrame = function(object) { - var obj = {}; - Utils.extend(obj, object); - return obj; + var log = logger.log; + __exports__.log = log; + var createFrame = function(object) { + var frame = Utils.extend({}, object); + frame._parent = object; + return frame; }; __exports__.createFrame = createFrame; return __exports__; @@ -353,6 +424,7 @@ var __module6__ = (function(__dependency1__, __dependency2__, __dependency3__) { var Exception = __dependency2__; var COMPILER_REVISION = __dependency3__.COMPILER_REVISION; var REVISION_CHANGES = __dependency3__.REVISION_CHANGES; + var createFrame = __dependency3__.createFrame; function checkRevision(compilerInfo) { var compilerRevision = compilerInfo && compilerInfo[0] || 1, @@ -375,20 +447,43 @@ var __module6__ = (function(__dependency1__, __dependency2__, __dependency3__) { __exports__.checkRevision = checkRevision;// TODO: Remove this line and break up compilePartial function template(templateSpec, env) { + /* istanbul ignore next */ if (!env) { throw new Exception("No environment passed to template"); } + if (!templateSpec || !templateSpec.main) { + throw new Exception('Unknown template object: ' + typeof templateSpec); + } // Note: Using env.VM references rather than local var references throughout this section to allow // for external users to override these as psuedo-supported APIs. - var invokePartialWrapper = function(partial, name, context, helpers, partials, data) { - var result = env.VM.invokePartial.apply(this, arguments); - if (result != null) { return result; } + env.VM.checkRevision(templateSpec.compiler); - if (env.compile) { - var options = { helpers: helpers, partials: partials, data: data }; - partials[name] = env.compile(partial, { data: data !== undefined }, env); - return partials[name](context, options); + var invokePartialWrapper = function(partial, indent, name, context, hash, helpers, partials, data, depths) { + if (hash) { + context = Utils.extend({}, context, hash); + } + + var result = env.VM.invokePartial.call(this, partial, name, context, helpers, partials, data, depths); + + if (result == null && env.compile) { + var options = { helpers: helpers, partials: partials, data: data, depths: depths }; + partials[name] = env.compile(partial, { data: data !== undefined, compat: templateSpec.compat }, env); + result = partials[name](context, options); + } + if (result != null) { + if (indent) { + var lines = result.split('\n'); + for (var i = 0, l = lines.length; i < l; i++) { + if (!lines[i] && i + 1 === l) { + break; + } + + lines[i] = indent + lines[i]; + } + result = lines.join('\n'); + } + return result; } else { throw new Exception("The partial " + name + " could not be compiled when running in runtime-only mode"); } @@ -396,84 +491,110 @@ var __module6__ = (function(__dependency1__, __dependency2__, __dependency3__) { // Just add water var container = { + lookup: function(depths, name) { + var len = depths.length; + for (var i = 0; i < len; i++) { + if (depths[i] && depths[i][name] != null) { + return depths[i][name]; + } + } + }, + lambda: function(current, context) { + return typeof current === 'function' ? current.call(context) : current; + }, + escapeExpression: Utils.escapeExpression, invokePartial: invokePartialWrapper, + + fn: function(i) { + return templateSpec[i]; + }, + programs: [], - program: function(i, fn, data) { - var programWrapper = this.programs[i]; - if(data) { - programWrapper = program(i, fn, data); + program: function(i, data, depths) { + var programWrapper = this.programs[i], + fn = this.fn(i); + if (data || depths) { + programWrapper = program(this, i, fn, data, depths); } else if (!programWrapper) { - programWrapper = this.programs[i] = program(i, fn); + programWrapper = this.programs[i] = program(this, i, fn); } return programWrapper; }, + + data: function(data, depth) { + while (data && depth--) { + data = data._parent; + } + return data; + }, merge: function(param, common) { var ret = param || common; if (param && common && (param !== common)) { - ret = {}; - Utils.extend(ret, common); - Utils.extend(ret, param); + ret = Utils.extend({}, common, param); } + return ret; }, - programWithDepth: env.VM.programWithDepth, + noop: env.VM.noop, - compilerInfo: null + compilerInfo: templateSpec.compiler }; - return function(context, options) { + var ret = function(context, options) { options = options || {}; - var namespace = options.partial ? options : env, - helpers, - partials; + var data = options.data; - if (!options.partial) { - helpers = options.helpers; - partials = options.partials; + ret._setup(options); + if (!options.partial && templateSpec.useData) { + data = initData(context, data); } - var result = templateSpec.call( - container, - namespace, context, - helpers, - partials, - options.data); - - if (!options.partial) { - env.VM.checkRevision(container.compilerInfo); + var depths; + if (templateSpec.useDepths) { + depths = options.depths ? [context].concat(options.depths) : [context]; } - return result; + return templateSpec.main.call(container, context, container.helpers, container.partials, data, depths); }; + ret.isTop = true; + + ret._setup = function(options) { + if (!options.partial) { + container.helpers = container.merge(options.helpers, env.helpers); + + if (templateSpec.usePartial) { + container.partials = container.merge(options.partials, env.partials); + } + } else { + container.helpers = options.helpers; + container.partials = options.partials; + } + }; + + ret._child = function(i, data, depths) { + if (templateSpec.useDepths && !depths) { + throw new Exception('must pass parent depths'); + } + + return program(container, i, templateSpec[i], data, depths); + }; + return ret; } - __exports__.template = template;function programWithDepth(i, fn, data /*, $depth */) { - var args = Array.prototype.slice.call(arguments, 3); - + __exports__.template = template;function program(container, i, fn, data, depths) { var prog = function(context, options) { options = options || {}; - return fn.apply(this, [context, options.data || data].concat(args)); + return fn.call(container, context, container.helpers, container.partials, options.data || data, depths && [context].concat(depths)); }; prog.program = i; - prog.depth = args.length; + prog.depth = depths ? depths.length : 0; return prog; } - __exports__.programWithDepth = programWithDepth;function program(i, fn, data) { - var prog = function(context, options) { - options = options || {}; - - return fn(context, options.data || data); - }; - prog.program = i; - prog.depth = 0; - return prog; - } - - __exports__.program = program;function invokePartial(partial, name, context, helpers, partials, data) { - var options = { partial: true, helpers: helpers, partials: partials, data: data }; + __exports__.program = program;function invokePartial(partial, name, context, helpers, partials, data, depths) { + var options = { partial: true, helpers: helpers, partials: partials, data: data, depths: depths }; if(partial === undefined) { throw new Exception("The partial " + name + " could not be found"); @@ -484,7 +605,13 @@ var __module6__ = (function(__dependency1__, __dependency2__, __dependency3__) { __exports__.invokePartial = invokePartial;function noop() { return ""; } - __exports__.noop = noop; + __exports__.noop = noop;function initData(context, data) { + if (!data || !('root' in data)) { + data = data ? createFrame(data) : {}; + data.root = context; + } + return data; + } return __exports__; })(__module3__, __module5__, __module2__); @@ -510,6 +637,7 @@ var __module1__ = (function(__dependency1__, __dependency2__, __dependency3__, _ hb.SafeString = SafeString; hb.Exception = Exception; hb.Utils = Utils; + hb.escapeExpression = Utils.escapeExpression; hb.VM = runtime; hb.template = function(spec) { @@ -522,6 +650,8 @@ var __module1__ = (function(__dependency1__, __dependency2__, __dependency3__, _ var Handlebars = create(); Handlebars.create = create; + Handlebars['default'] = Handlebars; + __exports__ = Handlebars; return __exports__; })(__module2__, __module4__, __module5__, __module3__, __module6__); @@ -532,7 +662,7 @@ var __module7__ = (function(__dependency1__) { var __exports__; var Exception = __dependency1__; - function LocationInfo(locInfo){ + function LocationInfo(locInfo) { locInfo = locInfo || {}; this.firstLine = locInfo.first_line; this.firstColumn = locInfo.first_column; @@ -541,38 +671,11 @@ var __module7__ = (function(__dependency1__) { } var AST = { - ProgramNode: function(statements, inverseStrip, inverse, locInfo) { - var inverseLocationInfo, firstInverseNode; - if (arguments.length === 3) { - locInfo = inverse; - inverse = null; - } else if (arguments.length === 2) { - locInfo = inverseStrip; - inverseStrip = null; - } - + ProgramNode: function(statements, strip, locInfo) { LocationInfo.call(this, locInfo); this.type = "program"; this.statements = statements; - this.strip = {}; - - if(inverse) { - firstInverseNode = inverse[0]; - if (firstInverseNode) { - inverseLocationInfo = { - first_line: firstInverseNode.firstLine, - last_line: firstInverseNode.lastLine, - last_column: firstInverseNode.lastColumn, - first_column: firstInverseNode.firstColumn - }; - this.inverse = new AST.ProgramNode(inverse, inverseStrip, inverseLocationInfo); - } else { - this.inverse = new AST.ProgramNode(inverse, inverseStrip); - } - this.strip.right = inverseStrip.left; - } else if (inverseStrip) { - this.strip.left = inverseStrip.right; - } + this.strip = strip; }, MustacheNode: function(rawParams, hash, open, strip, locInfo) { @@ -596,8 +699,6 @@ var __module7__ = (function(__dependency1__) { this.sexpr = new AST.SexprNode(rawParams, hash); } - this.sexpr.isRoot = true; - // Support old AST API that stored this info in MustacheNode this.id = this.sexpr.id; this.params = this.sexpr.params; @@ -615,57 +716,63 @@ var __module7__ = (function(__dependency1__) { var id = this.id = rawParams[0]; var params = this.params = rawParams.slice(1); - // a mustache is an eligible helper if: - // * its id is simple (a single part, not `this` or `..`) - var eligibleHelper = this.eligibleHelper = id.isSimple; - // a mustache is definitely a helper if: // * it is an eligible helper, and // * it has at least one parameter or hash segment - this.isHelper = eligibleHelper && (params.length || hash); + this.isHelper = !!(params.length || hash); + + // a mustache is an eligible helper if: + // * its id is simple (a single part, not `this` or `..`) + this.eligibleHelper = this.isHelper || id.isSimple; // if a mustache is an eligible helper but not a definite // helper, it is ambiguous, and will be resolved in a later // pass or at runtime. }, - PartialNode: function(partialName, context, strip, locInfo) { + PartialNode: function(partialName, context, hash, strip, locInfo) { LocationInfo.call(this, locInfo); this.type = "partial"; this.partialName = partialName; this.context = context; + this.hash = hash; this.strip = strip; + + this.strip.inlineStandalone = true; }, - BlockNode: function(mustache, program, inverse, close, locInfo) { + BlockNode: function(mustache, program, inverse, strip, locInfo) { LocationInfo.call(this, locInfo); - if(mustache.sexpr.id.original !== close.path.original) { - throw new Exception(mustache.sexpr.id.original + " doesn't match " + close.path.original, this); - } - this.type = 'block'; this.mustache = mustache; this.program = program; this.inverse = inverse; - - this.strip = { - left: mustache.strip.left, - right: close.strip.right - }; - - (program || inverse).strip.left = mustache.strip.right; - (inverse || program).strip.right = close.strip.left; + this.strip = strip; if (inverse && !program) { this.isInverse = true; } }, + RawBlockNode: function(mustache, content, close, locInfo) { + LocationInfo.call(this, locInfo); + + if (mustache.sexpr.id.original !== close) { + throw new Exception(mustache.sexpr.id.original + " doesn't match " + close, this); + } + + content = new AST.ContentNode(content, locInfo); + + this.type = 'block'; + this.mustache = mustache; + this.program = new AST.ProgramNode([content], {}, locInfo); + }, + ContentNode: function(string, locInfo) { LocationInfo.call(this, locInfo); this.type = "content"; - this.string = string; + this.original = this.string = string; }, HashNode: function(pairs, locInfo) { @@ -680,7 +787,8 @@ var __module7__ = (function(__dependency1__) { var original = "", dig = [], - depth = 0; + depth = 0, + depthString = ''; for(var i=0,l=parts.length; i)/,/^(?:\{\{(~)?#)/,/^(?:\{\{(~)?\/)/,/^(?:\{\{(~)?\^)/,/^(?:\{\{(~)?\s*else\b)/,/^(?:\{\{(~)?\{)/,/^(?:\{\{(~)?&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{(~)?)/,/^(?:=)/,/^(?:\.\.)/,/^(?:\.(?=([=~}\s\/.)])))/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}(~)?\}\})/,/^(?:(~)?\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=([~}\s)])))/,/^(?:false(?=([~}\s)])))/,/^(?:-?[0-9]+(?=([~}\s)])))/,/^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)]))))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/]; - lexer.conditions = {"mu":{"rules":[4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"com":{"rules":[3],"inclusive":false},"INITIAL":{"rules":[0,1,32],"inclusive":true}}; + lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/,/^(?:\{\{\{\{\/[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.])\}\}\}\})/,/^(?:[^\x00]*?(?=(\{\{\{\{\/)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\()/,/^(?:\))/,/^(?:\{\{\{\{)/,/^(?:\}\}\}\})/,/^(?:\{\{(~)?>)/,/^(?:\{\{(~)?#)/,/^(?:\{\{(~)?\/)/,/^(?:\{\{(~)?\^\s*(~)?\}\})/,/^(?:\{\{(~)?\s*else\s*(~)?\}\})/,/^(?:\{\{(~)?\^)/,/^(?:\{\{(~)?\s*else\b)/,/^(?:\{\{(~)?\{)/,/^(?:\{\{(~)?&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{(~)?)/,/^(?:=)/,/^(?:\.\.)/,/^(?:\.(?=([=~}\s\/.)])))/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}(~)?\}\})/,/^(?:(~)?\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=([~}\s)])))/,/^(?:false(?=([~}\s)])))/,/^(?:-?[0-9]+(?:\.[0-9]+)?(?=([~}\s)])))/,/^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)]))))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/]; + lexer.conditions = {"mu":{"rules":[6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"com":{"rules":[5],"inclusive":false},"raw":{"rules":[3,4],"inclusive":false},"INITIAL":{"rules":[0,1,38],"inclusive":true}}; return lexer;})() parser.lexer = lexer; function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser; @@ -1255,32 +1382,234 @@ var __module9__ = (function() { return __exports__; })(); +// handlebars/compiler/helpers.js +var __module10__ = (function(__dependency1__) { + "use strict"; + var __exports__ = {}; + var Exception = __dependency1__; + + function stripFlags(open, close) { + return { + left: open.charAt(2) === '~', + right: close.charAt(close.length-3) === '~' + }; + } + + __exports__.stripFlags = stripFlags; + function prepareBlock(mustache, program, inverseAndProgram, close, inverted, locInfo) { + /*jshint -W040 */ + if (mustache.sexpr.id.original !== close.path.original) { + throw new Exception(mustache.sexpr.id.original + ' doesn\'t match ' + close.path.original, mustache); + } + + var inverse = inverseAndProgram && inverseAndProgram.program; + + var strip = { + left: mustache.strip.left, + right: close.strip.right, + + // Determine the standalone candiacy. Basically flag our content as being possibly standalone + // so our parent can determine if we actually are standalone + openStandalone: isNextWhitespace(program.statements), + closeStandalone: isPrevWhitespace((inverse || program).statements) + }; + + if (mustache.strip.right) { + omitRight(program.statements, null, true); + } + + if (inverse) { + var inverseStrip = inverseAndProgram.strip; + + if (inverseStrip.left) { + omitLeft(program.statements, null, true); + } + if (inverseStrip.right) { + omitRight(inverse.statements, null, true); + } + if (close.strip.left) { + omitLeft(inverse.statements, null, true); + } + + // Find standalone else statments + if (isPrevWhitespace(program.statements) + && isNextWhitespace(inverse.statements)) { + + omitLeft(program.statements); + omitRight(inverse.statements); + } + } else { + if (close.strip.left) { + omitLeft(program.statements, null, true); + } + } + + if (inverted) { + return new this.BlockNode(mustache, inverse, program, strip, locInfo); + } else { + return new this.BlockNode(mustache, program, inverse, strip, locInfo); + } + } + + __exports__.prepareBlock = prepareBlock; + function prepareProgram(statements, isRoot) { + for (var i = 0, l = statements.length; i < l; i++) { + var current = statements[i], + strip = current.strip; + + if (!strip) { + continue; + } + + var _isPrevWhitespace = isPrevWhitespace(statements, i, isRoot, current.type === 'partial'), + _isNextWhitespace = isNextWhitespace(statements, i, isRoot), + + openStandalone = strip.openStandalone && _isPrevWhitespace, + closeStandalone = strip.closeStandalone && _isNextWhitespace, + inlineStandalone = strip.inlineStandalone && _isPrevWhitespace && _isNextWhitespace; + + if (strip.right) { + omitRight(statements, i, true); + } + if (strip.left) { + omitLeft(statements, i, true); + } + + if (inlineStandalone) { + omitRight(statements, i); + + if (omitLeft(statements, i)) { + // If we are on a standalone node, save the indent info for partials + if (current.type === 'partial') { + current.indent = (/([ \t]+$)/).exec(statements[i-1].original) ? RegExp.$1 : ''; + } + } + } + if (openStandalone) { + omitRight((current.program || current.inverse).statements); + + // Strip out the previous content node if it's whitespace only + omitLeft(statements, i); + } + if (closeStandalone) { + // Always strip the next node + omitRight(statements, i); + + omitLeft((current.inverse || current.program).statements); + } + } + + return statements; + } + + __exports__.prepareProgram = prepareProgram;function isPrevWhitespace(statements, i, isRoot) { + if (i === undefined) { + i = statements.length; + } + + // Nodes that end with newlines are considered whitespace (but are special + // cased for strip operations) + var prev = statements[i-1], + sibling = statements[i-2]; + if (!prev) { + return isRoot; + } + + if (prev.type === 'content') { + return (sibling || !isRoot ? (/\r?\n\s*?$/) : (/(^|\r?\n)\s*?$/)).test(prev.original); + } + } + function isNextWhitespace(statements, i, isRoot) { + if (i === undefined) { + i = -1; + } + + var next = statements[i+1], + sibling = statements[i+2]; + if (!next) { + return isRoot; + } + + if (next.type === 'content') { + return (sibling || !isRoot ? (/^\s*?\r?\n/) : (/^\s*?(\r?\n|$)/)).test(next.original); + } + } + + // Marks the node to the right of the position as omitted. + // I.e. {{foo}}' ' will mark the ' ' node as omitted. + // + // If i is undefined, then the first child will be marked as such. + // + // If mulitple is truthy then all whitespace will be stripped out until non-whitespace + // content is met. + function omitRight(statements, i, multiple) { + var current = statements[i == null ? 0 : i + 1]; + if (!current || current.type !== 'content' || (!multiple && current.rightStripped)) { + return; + } + + var original = current.string; + current.string = current.string.replace(multiple ? (/^\s+/) : (/^[ \t]*\r?\n?/), ''); + current.rightStripped = current.string !== original; + } + + // Marks the node to the left of the position as omitted. + // I.e. ' '{{foo}} will mark the ' ' node as omitted. + // + // If i is undefined then the last child will be marked as such. + // + // If mulitple is truthy then all whitespace will be stripped out until non-whitespace + // content is met. + function omitLeft(statements, i, multiple) { + var current = statements[i == null ? statements.length - 1 : i - 1]; + if (!current || current.type !== 'content' || (!multiple && current.leftStripped)) { + return; + } + + // We omit the last node if it's whitespace only and not preceeded by a non-content node. + var original = current.string; + current.string = current.string.replace(multiple ? (/\s+$/) : (/[ \t]+$/), ''); + current.leftStripped = current.string !== original; + return current.leftStripped; + } + return __exports__; +})(__module5__); + // handlebars/compiler/base.js -var __module8__ = (function(__dependency1__, __dependency2__) { +var __module8__ = (function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; var __exports__ = {}; var parser = __dependency1__; var AST = __dependency2__; + var Helpers = __dependency3__; + var extend = __dependency4__.extend; __exports__.parser = parser; + var yy = {}; + extend(yy, Helpers, AST); + function parse(input) { // Just return if an already-compile AST was passed in. - if(input.constructor === AST.ProgramNode) { return input; } + if (input.constructor === AST.ProgramNode) { return input; } + + parser.yy = yy; - parser.yy = AST; return parser.parse(input); } __exports__.parse = parse; return __exports__; -})(__module9__, __module7__); +})(__module9__, __module7__, __module10__, __module3__); // handlebars/compiler/compiler.js -var __module10__ = (function(__dependency1__) { +var __module11__ = (function(__dependency1__, __dependency2__) { "use strict"; var __exports__ = {}; var Exception = __dependency1__; + var isArray = __dependency2__.isArray; + + var slice = [].slice; function Compiler() {} @@ -1292,30 +1621,6 @@ var __module10__ = (function(__dependency1__) { Compiler.prototype = { compiler: Compiler, - disassemble: function() { - var opcodes = this.opcodes, opcode, out = [], params, param; - - for (var i=0, l=opcodes.length; i 0) { - this.source[1] = this.source[1] + ", " + locals.join(", "); + varDeclarations += ", " + locals.join(", "); } // Generate minimizer alias mappings - if (!this.isChild) { - for (var alias in this.context.aliases) { - if (this.context.aliases.hasOwnProperty(alias)) { - this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias]; - } + for (var alias in this.aliases) { + if (this.aliases.hasOwnProperty(alias)) { + varDeclarations += ', ' + alias + '=' + this.aliases[alias]; } } - if (this.source[1]) { - this.source[1] = "var " + this.source[1].substring(2) + ";"; - } + var params = ["depth0", "helpers", "partials", "data"]; - // Merge children - if (!this.isChild) { - this.source[1] += '\n' + this.context.programs.join('\n') + '\n'; - } - - if (!this.environment.isSimple) { - this.pushSource("return buffer;"); - } - - var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"]; - - for(var i=0, l=this.environment.depths.list.length; i').append(inputClone)[0].reset(); // Detaching allows to insert the fileInput on another form // without loosing the file input value: @@ -1187,7 +1215,7 @@ this._getFileInputFiles(data.fileInput).always(function (files) { data.files = files; if (that.options.replaceFileInput) { - that._replaceFileInput(data.fileInput); + that._replaceFileInput(data); } if (that._trigger( 'change', @@ -1240,24 +1268,21 @@ } }, - _onDragOver: function (e) { - e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer; - var dataTransfer = e.dataTransfer; - if (dataTransfer && $.inArray('Files', dataTransfer.types) !== -1 && - this._trigger( - 'dragover', - $.Event('dragover', {delegatedEvent: e}) - ) !== false) { - e.preventDefault(); - dataTransfer.dropEffect = 'copy'; - } - }, + _onDragOver: getDragHandler('dragover'), + + _onDragEnter: getDragHandler('dragenter'), + + _onDragLeave: getDragHandler('dragleave'), _initEventHandlers: function () { if (this._isXHRUpload(this.options)) { this._on(this.options.dropZone, { dragover: this._onDragOver, - drop: this._onDrop + drop: this._onDrop, + // event.preventDefault() on dragenter is required for IE10+: + dragenter: this._onDragEnter, + // dragleave is not required, but added for completeness: + dragleave: this._onDragLeave }); this._on(this.options.pasteZone, { paste: this._onPaste @@ -1271,7 +1296,7 @@ }, _destroyEventHandlers: function () { - this._off(this.options.dropZone, 'dragover drop'); + this._off(this.options.dropZone, 'dragenter dragleave dragover drop'); this._off(this.options.pasteZone, 'paste'); this._off(this.options.fileInput, 'change'); }, @@ -1319,10 +1344,13 @@ _initDataAttributes: function () { var that = this, options = this.options, - clone = $(this.element[0].cloneNode(false)); + clone = $(this.element[0].cloneNode(false)), + data = clone.data(); + // Avoid memory leaks: + clone.remove(); // Initialize options set via HTML5 data-attributes: $.each( - clone.data(), + data, function (key, value) { var dataAttributeName = 'data-' + // Convert camelCase to hyphen-ated key: diff --git a/vendor/assets/javascripts/jquery.iframe-transport.js b/vendor/assets/javascripts/jquery.iframe-transport.js index 4749f46993..b7581f23f4 100644 --- a/vendor/assets/javascripts/jquery.iframe-transport.js +++ b/vendor/assets/javascripts/jquery.iframe-transport.js @@ -1,5 +1,5 @@ /* - * jQuery Iframe Transport Plugin 1.5 + * jQuery Iframe Transport Plugin 1.8.3 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2011, Sebastian Tschan @@ -9,14 +9,16 @@ * http://www.opensource.org/licenses/MIT */ -/*jslint unparam: true, nomen: true */ -/*global define, window, document */ +/* global define, require, window, document */ (function (factory) { 'use strict'; if (typeof define === 'function' && define.amd) { // Register as an anonymous AMD module: define(['jquery'], factory); + } else if (typeof exports === 'object') { + // Node/CommonJS: + factory(require('jquery')); } else { // Browser globals: factory(window.jQuery); @@ -27,7 +29,7 @@ // Helper variable to create unique names for the transport iframes: var counter = 0; - // The iframe transport accepts three additional options: + // The iframe transport accepts four additional options: // options.fileInput: a jQuery collection of file input fields // options.paramName: the parameter name for the file form data, // overrides the name property of the file input field(s), @@ -35,22 +37,41 @@ // options.formData: an array of objects with name and value properties, // equivalent to the return data of .serializeArray(), e.g.: // [{name: 'a', value: 1}, {name: 'b', value: 2}] + // options.initialIframeSrc: the URL of the initial iframe src, + // by default set to "javascript:false;" $.ajaxTransport('iframe', function (options) { - if (options.async && (options.type === 'POST' || options.type === 'GET')) { - var form, - iframe; + if (options.async) { + // javascript:false as initial iframe src + // prevents warning popups on HTTPS in IE6: + /*jshint scripturl: true */ + var initialIframeSrc = options.initialIframeSrc || 'javascript:false;', + /*jshint scripturl: false */ + form, + iframe, + addParamChar; return { send: function (_, completeCallback) { form = $('
      '); form.attr('accept-charset', options.formAcceptCharset); - // javascript:false as initial iframe src - // prevents warning popups on HTTPS in IE6. + addParamChar = /\?/.test(options.url) ? '&' : '?'; + // XDomainRequest only supports GET and POST: + if (options.type === 'DELETE') { + options.url = options.url + addParamChar + '_method=DELETE'; + options.type = 'POST'; + } else if (options.type === 'PUT') { + options.url = options.url + addParamChar + '_method=PUT'; + options.type = 'POST'; + } else if (options.type === 'PATCH') { + options.url = options.url + addParamChar + '_method=PATCH'; + options.type = 'POST'; + } // IE versions below IE8 cannot set the name property of // elements that have already been added to the DOM, // so we set the name along with the iframe HTML markup: + counter += 1; iframe = $( - '' + '' ).bind('load', function () { var fileInputClones, paramNames = $.isArray(options.paramName) ? @@ -81,9 +102,14 @@ ); // Fix for IE endless progress bar activity bug // (happens on form submits to iframe targets): - $('') + $('') .appendTo(form); - form.remove(); + window.setTimeout(function () { + // Removing the form in a setTimeout call + // allows Chrome's developer tools to display + // the response result + form.remove(); + }, 0); }); form .prop('target', iframe.prop('name')) @@ -119,6 +145,8 @@ .prop('enctype', 'multipart/form-data') // enctype must be set as encoding for IE: .prop('encoding', 'multipart/form-data'); + // Remove the HTML5 form attribute from the input(s): + options.fileInput.removeAttr('form'); } form.submit(); // Insert the file input fields at their original location @@ -126,7 +154,10 @@ if (fileInputClones && fileInputClones.length) { options.fileInput.each(function (index, input) { var clone = $(fileInputClones[index]); - $(input).prop('name', clone.prop('name')); + // Restore the original name and form properties: + $(input) + .prop('name', clone.prop('name')) + .attr('form', clone.attr('form')); clone.replaceWith(input); }); } @@ -140,7 +171,7 @@ // concat is used to avoid the "Script URL" JSLint error: iframe .unbind('load') - .prop('src', 'javascript'.concat(':false;')); + .prop('src', initialIframeSrc); } if (form) { form.remove(); @@ -151,20 +182,34 @@ }); // The iframe transport returns the iframe content document as response. - // The following adds converters from iframe to text, json, html, and script: + // The following adds converters from iframe to text, json, html, xml + // and script. + // Please note that the Content-Type for JSON responses has to be text/plain + // or text/html, if the browser doesn't include application/json in the + // Accept header, else IE will show a download dialog. + // The Content-Type for XML responses on the other hand has to be always + // application/xml or text/xml, so IE properly parses the XML response. + // See also + // https://github.com/blueimp/jQuery-File-Upload/wiki/Setup#content-type-negotiation $.ajaxSetup({ converters: { 'iframe text': function (iframe) { - return $(iframe[0].body).text(); + return iframe && $(iframe[0].body).text(); }, 'iframe json': function (iframe) { - return $.parseJSON($(iframe[0].body).text()); + return iframe && $.parseJSON($(iframe[0].body).text()); }, 'iframe html': function (iframe) { - return $(iframe[0].body).html(); + return iframe && $(iframe[0].body).html(); + }, + 'iframe xml': function (iframe) { + var xmlDoc = iframe && iframe[0]; + return xmlDoc && $.isXMLDoc(xmlDoc) ? xmlDoc : + $.parseXML((xmlDoc.XMLDocument && xmlDoc.XMLDocument.xml) || + $(xmlDoc.body).html()); }, 'iframe script': function (iframe) { - return $.globalEval($(iframe[0].body).text()); + return iframe && $.globalEval($(iframe[0].body).text()); } } }); diff --git a/vendor/assets/javascripts/jquery.ui.widget.js b/vendor/assets/javascripts/jquery.ui.widget.js index c430419971..5ac2ed5a57 100644 --- a/vendor/assets/javascripts/jquery.ui.widget.js +++ b/vendor/assets/javascripts/jquery.ui.widget.js @@ -1,6 +1,27 @@ +/*! jQuery UI - v1.11.1+CommonJS - 2014-09-17 +* http://jqueryui.com +* Includes: widget.js +* Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ + +(function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define([ "jquery" ], factory ); + + } else if (typeof exports === "object") { + // Node/CommonJS: + factory(require("jquery")); + + } else { + + // Browser globals + factory( jQuery ); + } +}(function( $ ) { /*! - * jQuery UI Widget 1.10.4+amd - * https://github.com/blueimp/jQuery-File-Upload + * jQuery UI Widget 1.11.1 + * http://jqueryui.com * * Copyright 2014 jQuery Foundation and other contributors * Released under the MIT license. @@ -9,28 +30,28 @@ * http://api.jqueryui.com/jQuery.widget/ */ -(function (factory) { - if (typeof define === "function" && define.amd) { - // Register as an anonymous AMD module: - define(["jquery"], factory); - } else { - // Browser globals: - factory(jQuery); - } -}(function( $, undefined ) { -var uuid = 0, - slice = Array.prototype.slice, - _cleanData = $.cleanData; -$.cleanData = function( elems ) { - for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { - try { - $( elem ).triggerHandler( "remove" ); - // http://bugs.jquery.com/ticket/8235 - } catch( e ) {} - } - _cleanData( elems ); -}; +var widget_uuid = 0, + widget_slice = Array.prototype.slice; + +$.cleanData = (function( orig ) { + return function( elems ) { + var events, elem, i; + for ( i = 0; (elem = elems[i]) != null; i++ ) { + try { + + // Only trigger remove when necessary to save time + events = $._data( elem, "events" ); + if ( events && events.remove ) { + $( elem ).triggerHandler( "remove" ); + } + + // http://bugs.jquery.com/ticket/8235 + } catch( e ) {} + } + orig( elems ); + }; +})( $.cleanData ); $.widget = function( name, base, prototype ) { var fullName, existingConstructor, constructor, basePrototype, @@ -143,10 +164,12 @@ $.widget = function( name, base, prototype ) { } $.widget.bridge( name, constructor ); + + return constructor; }; $.widget.extend = function( target ) { - var input = slice.call( arguments, 1 ), + var input = widget_slice.call( arguments, 1 ), inputIndex = 0, inputLength = input.length, key, @@ -175,7 +198,7 @@ $.widget.bridge = function( name, object ) { var fullName = object.prototype.widgetFullName || name; $.fn[ name ] = function( options ) { var isMethodCall = typeof options === "string", - args = slice.call( arguments, 1 ), + args = widget_slice.call( arguments, 1 ), returnValue = this; // allow multiple hashes to be passed on init @@ -187,6 +210,10 @@ $.widget.bridge = function( name, object ) { this.each(function() { var methodValue, instance = $.data( this, fullName ); + if ( options === "instance" ) { + returnValue = instance; + return false; + } if ( !instance ) { return $.error( "cannot call methods on " + name + " prior to initialization; " + "attempted to call method '" + options + "'" ); @@ -206,7 +233,10 @@ $.widget.bridge = function( name, object ) { this.each(function() { var instance = $.data( this, fullName ); if ( instance ) { - instance.option( options || {} )._init(); + instance.option( options || {} ); + if ( instance._init ) { + instance._init(); + } } else { $.data( this, fullName, new object( options, this ) ); } @@ -233,7 +263,7 @@ $.Widget.prototype = { _createWidget: function( options, element ) { element = $( element || this.defaultElement || this )[ 0 ]; this.element = $( element ); - this.uuid = uuid++; + this.uuid = widget_uuid++; this.eventNamespace = "." + this.widgetName + this.uuid; this.options = $.widget.extend( {}, this.options, @@ -276,9 +306,6 @@ $.Widget.prototype = { // all event bindings should go through this._on() this.element .unbind( this.eventNamespace ) - // 1.9 BC for #7810 - // TODO remove dual storage - .removeData( this.widgetName ) .removeData( this.widgetFullName ) // support: jquery <1.6.3 // http://bugs.jquery.com/ticket/9413 @@ -354,20 +381,23 @@ $.Widget.prototype = { if ( key === "disabled" ) { this.widget() - .toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value ) - .attr( "aria-disabled", value ); - this.hoverable.removeClass( "ui-state-hover" ); - this.focusable.removeClass( "ui-state-focus" ); + .toggleClass( this.widgetFullName + "-disabled", !!value ); + + // If the widget is becoming disabled, then nothing is interactive + if ( value ) { + this.hoverable.removeClass( "ui-state-hover" ); + this.focusable.removeClass( "ui-state-focus" ); + } } return this; }, enable: function() { - return this._setOption( "disabled", false ); + return this._setOptions({ disabled: false }); }, disable: function() { - return this._setOption( "disabled", true ); + return this._setOptions({ disabled: true }); }, _on: function( suppressDisabledCheck, element, handlers ) { @@ -387,7 +417,6 @@ $.Widget.prototype = { element = this.element; delegateElement = this.widget(); } else { - // accept selectors, DOM elements element = delegateElement = $( element ); this.bindings = this.bindings.add( element ); } @@ -412,7 +441,7 @@ $.Widget.prototype = { handler.guid || handlerProxy.guid || $.guid++; } - var match = event.match( /^(\w+)\s*(.*)$/ ), + var match = event.match( /^([\w:-]*)\s*(.*)$/ ), eventName = match[1] + instance.eventNamespace, selector = match[2]; if ( selector ) { @@ -527,4 +556,8 @@ $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) { }; }); +var widget = $.widget; + + + })); diff --git a/vendor/assets/javascripts/jshint.js b/vendor/assets/javascripts/jshint.js index 19155b58f9..538f9a7099 100644 --- a/vendor/assets/javascripts/jshint.js +++ b/vendor/assets/javascripts/jshint.js @@ -1,50083 +1,44 @@ -// 2.4.0 +/*! 2.6.0 */ var JSHINT; if (typeof window === 'undefined') window = {}; (function () { var require; -require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= 65 && i <= 90 || // A-Z - i === 95 || // _ - i >= 97 && i <= 122; // a-z + identifierStartTable[i] = + i === 36 || // $ + i >= 65 && i <= 90 || // A-Z + i === 95 || // _ + i >= 97 && i <= 122; // a-z } var identifierPartTable = []; for (var i = 0; i < 128; i++) { - identifierPartTable[i] = - identifierStartTable[i] || // $, _, A-Z, a-z - i >= 48 && i <= 57; // 0-9 + identifierPartTable[i] = + identifierStartTable[i] || // $, _, A-Z, a-z + i >= 48 && i <= 57; // 0-9 } module.exports = { - asciiIdentifierStartTable: identifierStartTable, - asciiIdentifierPartTable: identifierPartTable + asciiIdentifierStartTable: identifierStartTable, + asciiIdentifierPartTable: identifierPartTable }; },{}],2:[function(require,module,exports){ -module.exports = [ - 768, - 769, - 770, - 771, - 772, - 773, - 774, - 775, - 776, - 777, - 778, - 779, - 780, - 781, - 782, - 783, - 784, - 785, - 786, - 787, - 788, - 789, - 790, - 791, - 792, - 793, - 794, - 795, - 796, - 797, - 798, - 799, - 800, - 801, - 802, - 803, - 804, - 805, - 806, - 807, - 808, - 809, - 810, - 811, - 812, - 813, - 814, - 815, - 816, - 817, - 818, - 819, - 820, - 821, - 822, - 823, - 824, - 825, - 826, - 827, - 828, - 829, - 830, - 831, - 832, - 833, - 834, - 835, - 836, - 837, - 838, - 839, - 840, - 841, - 842, - 843, - 844, - 845, - 846, - 847, - 848, - 849, - 850, - 851, - 852, - 853, - 854, - 855, - 856, - 857, - 858, - 859, - 860, - 861, - 862, - 863, - 864, - 865, - 866, - 867, - 868, - 869, - 870, - 871, - 872, - 873, - 874, - 875, - 876, - 877, - 878, - 879, - 1155, - 1156, - 1157, - 1158, - 1159, - 1425, - 1426, - 1427, - 1428, - 1429, - 1430, - 1431, - 1432, - 1433, - 1434, - 1435, - 1436, - 1437, - 1438, - 1439, - 1440, - 1441, - 1442, - 1443, - 1444, - 1445, - 1446, - 1447, - 1448, - 1449, - 1450, - 1451, - 1452, - 1453, - 1454, - 1455, - 1456, - 1457, - 1458, - 1459, - 1460, - 1461, - 1462, - 1463, - 1464, - 1465, - 1466, - 1467, - 1468, - 1469, - 1471, - 1473, - 1474, - 1476, - 1477, - 1479, - 1552, - 1553, - 1554, - 1555, - 1556, - 1557, - 1558, - 1559, - 1560, - 1561, - 1562, - 1611, - 1612, - 1613, - 1614, - 1615, - 1616, - 1617, - 1618, - 1619, - 1620, - 1621, - 1622, - 1623, - 1624, - 1625, - 1626, - 1627, - 1628, - 1629, - 1630, - 1631, - 1632, - 1633, - 1634, - 1635, - 1636, - 1637, - 1638, - 1639, - 1640, - 1641, - 1648, - 1750, - 1751, - 1752, - 1753, - 1754, - 1755, - 1756, - 1759, - 1760, - 1761, - 1762, - 1763, - 1764, - 1767, - 1768, - 1770, - 1771, - 1772, - 1773, - 1776, - 1777, - 1778, - 1779, - 1780, - 1781, - 1782, - 1783, - 1784, - 1785, - 1809, - 1840, - 1841, - 1842, - 1843, - 1844, - 1845, - 1846, - 1847, - 1848, - 1849, - 1850, - 1851, - 1852, - 1853, - 1854, - 1855, - 1856, - 1857, - 1858, - 1859, - 1860, - 1861, - 1862, - 1863, - 1864, - 1865, - 1866, - 1958, - 1959, - 1960, - 1961, - 1962, - 1963, - 1964, - 1965, - 1966, - 1967, - 1968, - 1984, - 1985, - 1986, - 1987, - 1988, - 1989, - 1990, - 1991, - 1992, - 1993, - 2027, - 2028, - 2029, - 2030, - 2031, - 2032, - 2033, - 2034, - 2035, - 2070, - 2071, - 2072, - 2073, - 2075, - 2076, - 2077, - 2078, - 2079, - 2080, - 2081, - 2082, - 2083, - 2085, - 2086, - 2087, - 2089, - 2090, - 2091, - 2092, - 2093, - 2137, - 2138, - 2139, - 2276, - 2277, - 2278, - 2279, - 2280, - 2281, - 2282, - 2283, - 2284, - 2285, - 2286, - 2287, - 2288, - 2289, - 2290, - 2291, - 2292, - 2293, - 2294, - 2295, - 2296, - 2297, - 2298, - 2299, - 2300, - 2301, - 2302, - 2304, - 2305, - 2306, - 2307, - 2362, - 2363, - 2364, - 2366, - 2367, - 2368, - 2369, - 2370, - 2371, - 2372, - 2373, - 2374, - 2375, - 2376, - 2377, - 2378, - 2379, - 2380, - 2381, - 2382, - 2383, - 2385, - 2386, - 2387, - 2388, - 2389, - 2390, - 2391, - 2402, - 2403, - 2406, - 2407, - 2408, - 2409, - 2410, - 2411, - 2412, - 2413, - 2414, - 2415, - 2433, - 2434, - 2435, - 2492, - 2494, - 2495, - 2496, - 2497, - 2498, - 2499, - 2500, - 2503, - 2504, - 2507, - 2508, - 2509, - 2519, - 2530, - 2531, - 2534, - 2535, - 2536, - 2537, - 2538, - 2539, - 2540, - 2541, - 2542, - 2543, - 2561, - 2562, - 2563, - 2620, - 2622, - 2623, - 2624, - 2625, - 2626, - 2631, - 2632, - 2635, - 2636, - 2637, - 2641, - 2662, - 2663, - 2664, - 2665, - 2666, - 2667, - 2668, - 2669, - 2670, - 2671, - 2672, - 2673, - 2677, - 2689, - 2690, - 2691, - 2748, - 2750, - 2751, - 2752, - 2753, - 2754, - 2755, - 2756, - 2757, - 2759, - 2760, - 2761, - 2763, - 2764, - 2765, - 2786, - 2787, - 2790, - 2791, - 2792, - 2793, - 2794, - 2795, - 2796, - 2797, - 2798, - 2799, - 2817, - 2818, - 2819, - 2876, - 2878, - 2879, - 2880, - 2881, - 2882, - 2883, - 2884, - 2887, - 2888, - 2891, - 2892, - 2893, - 2902, - 2903, - 2914, - 2915, - 2918, - 2919, - 2920, - 2921, - 2922, - 2923, - 2924, - 2925, - 2926, - 2927, - 2946, - 3006, - 3007, - 3008, - 3009, - 3010, - 3014, - 3015, - 3016, - 3018, - 3019, - 3020, - 3021, - 3031, - 3046, - 3047, - 3048, - 3049, - 3050, - 3051, - 3052, - 3053, - 3054, - 3055, - 3073, - 3074, - 3075, - 3134, - 3135, - 3136, - 3137, - 3138, - 3139, - 3140, - 3142, - 3143, - 3144, - 3146, - 3147, - 3148, - 3149, - 3157, - 3158, - 3170, - 3171, - 3174, - 3175, - 3176, - 3177, - 3178, - 3179, - 3180, - 3181, - 3182, - 3183, - 3202, - 3203, - 3260, - 3262, - 3263, - 3264, - 3265, - 3266, - 3267, - 3268, - 3270, - 3271, - 3272, - 3274, - 3275, - 3276, - 3277, - 3285, - 3286, - 3298, - 3299, - 3302, - 3303, - 3304, - 3305, - 3306, - 3307, - 3308, - 3309, - 3310, - 3311, - 3330, - 3331, - 3390, - 3391, - 3392, - 3393, - 3394, - 3395, - 3396, - 3398, - 3399, - 3400, - 3402, - 3403, - 3404, - 3405, - 3415, - 3426, - 3427, - 3430, - 3431, - 3432, - 3433, - 3434, - 3435, - 3436, - 3437, - 3438, - 3439, - 3458, - 3459, - 3530, - 3535, - 3536, - 3537, - 3538, - 3539, - 3540, - 3542, - 3544, - 3545, - 3546, - 3547, - 3548, - 3549, - 3550, - 3551, - 3570, - 3571, - 3633, - 3636, - 3637, - 3638, - 3639, - 3640, - 3641, - 3642, - 3655, - 3656, - 3657, - 3658, - 3659, - 3660, - 3661, - 3662, - 3664, - 3665, - 3666, - 3667, - 3668, - 3669, - 3670, - 3671, - 3672, - 3673, - 3761, - 3764, - 3765, - 3766, - 3767, - 3768, - 3769, - 3771, - 3772, - 3784, - 3785, - 3786, - 3787, - 3788, - 3789, - 3792, - 3793, - 3794, - 3795, - 3796, - 3797, - 3798, - 3799, - 3800, - 3801, - 3864, - 3865, - 3872, - 3873, - 3874, - 3875, - 3876, - 3877, - 3878, - 3879, - 3880, - 3881, - 3893, - 3895, - 3897, - 3902, - 3903, - 3953, - 3954, - 3955, - 3956, - 3957, - 3958, - 3959, - 3960, - 3961, - 3962, - 3963, - 3964, - 3965, - 3966, - 3967, - 3968, - 3969, - 3970, - 3971, - 3972, - 3974, - 3975, - 3981, - 3982, - 3983, - 3984, - 3985, - 3986, - 3987, - 3988, - 3989, - 3990, - 3991, - 3993, - 3994, - 3995, - 3996, - 3997, - 3998, - 3999, - 4000, - 4001, - 4002, - 4003, - 4004, - 4005, - 4006, - 4007, - 4008, - 4009, - 4010, - 4011, - 4012, - 4013, - 4014, - 4015, - 4016, - 4017, - 4018, - 4019, - 4020, - 4021, - 4022, - 4023, - 4024, - 4025, - 4026, - 4027, - 4028, - 4038, - 4139, - 4140, - 4141, - 4142, - 4143, - 4144, - 4145, - 4146, - 4147, - 4148, - 4149, - 4150, - 4151, - 4152, - 4153, - 4154, - 4155, - 4156, - 4157, - 4158, - 4160, - 4161, - 4162, - 4163, - 4164, - 4165, - 4166, - 4167, - 4168, - 4169, - 4182, - 4183, - 4184, - 4185, - 4190, - 4191, - 4192, - 4194, - 4195, - 4196, - 4199, - 4200, - 4201, - 4202, - 4203, - 4204, - 4205, - 4209, - 4210, - 4211, - 4212, - 4226, - 4227, - 4228, - 4229, - 4230, - 4231, - 4232, - 4233, - 4234, - 4235, - 4236, - 4237, - 4239, - 4240, - 4241, - 4242, - 4243, - 4244, - 4245, - 4246, - 4247, - 4248, - 4249, - 4250, - 4251, - 4252, - 4253, - 4957, - 4958, - 4959, - 5906, - 5907, - 5908, - 5938, - 5939, - 5940, - 5970, - 5971, - 6002, - 6003, - 6068, - 6069, - 6070, - 6071, - 6072, - 6073, - 6074, - 6075, - 6076, - 6077, - 6078, - 6079, - 6080, - 6081, - 6082, - 6083, - 6084, - 6085, - 6086, - 6087, - 6088, - 6089, - 6090, - 6091, - 6092, - 6093, - 6094, - 6095, - 6096, - 6097, - 6098, - 6099, - 6109, - 6112, - 6113, - 6114, - 6115, - 6116, - 6117, - 6118, - 6119, - 6120, - 6121, - 6155, - 6156, - 6157, - 6160, - 6161, - 6162, - 6163, - 6164, - 6165, - 6166, - 6167, - 6168, - 6169, - 6313, - 6432, - 6433, - 6434, - 6435, - 6436, - 6437, - 6438, - 6439, - 6440, - 6441, - 6442, - 6443, - 6448, - 6449, - 6450, - 6451, - 6452, - 6453, - 6454, - 6455, - 6456, - 6457, - 6458, - 6459, - 6470, - 6471, - 6472, - 6473, - 6474, - 6475, - 6476, - 6477, - 6478, - 6479, - 6576, - 6577, - 6578, - 6579, - 6580, - 6581, - 6582, - 6583, - 6584, - 6585, - 6586, - 6587, - 6588, - 6589, - 6590, - 6591, - 6592, - 6600, - 6601, - 6608, - 6609, - 6610, - 6611, - 6612, - 6613, - 6614, - 6615, - 6616, - 6617, - 6679, - 6680, - 6681, - 6682, - 6683, - 6741, - 6742, - 6743, - 6744, - 6745, - 6746, - 6747, - 6748, - 6749, - 6750, - 6752, - 6753, - 6754, - 6755, - 6756, - 6757, - 6758, - 6759, - 6760, - 6761, - 6762, - 6763, - 6764, - 6765, - 6766, - 6767, - 6768, - 6769, - 6770, - 6771, - 6772, - 6773, - 6774, - 6775, - 6776, - 6777, - 6778, - 6779, - 6780, - 6783, - 6784, - 6785, - 6786, - 6787, - 6788, - 6789, - 6790, - 6791, - 6792, - 6793, - 6800, - 6801, - 6802, - 6803, - 6804, - 6805, - 6806, - 6807, - 6808, - 6809, - 6912, - 6913, - 6914, - 6915, - 6916, - 6964, - 6965, - 6966, - 6967, - 6968, - 6969, - 6970, - 6971, - 6972, - 6973, - 6974, - 6975, - 6976, - 6977, - 6978, - 6979, - 6980, - 6992, - 6993, - 6994, - 6995, - 6996, - 6997, - 6998, - 6999, - 7000, - 7001, - 7019, - 7020, - 7021, - 7022, - 7023, - 7024, - 7025, - 7026, - 7027, - 7040, - 7041, - 7042, - 7073, - 7074, - 7075, - 7076, - 7077, - 7078, - 7079, - 7080, - 7081, - 7082, - 7083, - 7084, - 7085, - 7088, - 7089, - 7090, - 7091, - 7092, - 7093, - 7094, - 7095, - 7096, - 7097, - 7142, - 7143, - 7144, - 7145, - 7146, - 7147, - 7148, - 7149, - 7150, - 7151, - 7152, - 7153, - 7154, - 7155, - 7204, - 7205, - 7206, - 7207, - 7208, - 7209, - 7210, - 7211, - 7212, - 7213, - 7214, - 7215, - 7216, - 7217, - 7218, - 7219, - 7220, - 7221, - 7222, - 7223, - 7232, - 7233, - 7234, - 7235, - 7236, - 7237, - 7238, - 7239, - 7240, - 7241, - 7248, - 7249, - 7250, - 7251, - 7252, - 7253, - 7254, - 7255, - 7256, - 7257, - 7376, - 7377, - 7378, - 7380, - 7381, - 7382, - 7383, - 7384, - 7385, - 7386, - 7387, - 7388, - 7389, - 7390, - 7391, - 7392, - 7393, - 7394, - 7395, - 7396, - 7397, - 7398, - 7399, - 7400, - 7405, - 7410, - 7411, - 7412, - 7616, - 7617, - 7618, - 7619, - 7620, - 7621, - 7622, - 7623, - 7624, - 7625, - 7626, - 7627, - 7628, - 7629, - 7630, - 7631, - 7632, - 7633, - 7634, - 7635, - 7636, - 7637, - 7638, - 7639, - 7640, - 7641, - 7642, - 7643, - 7644, - 7645, - 7646, - 7647, - 7648, - 7649, - 7650, - 7651, - 7652, - 7653, - 7654, - 7676, - 7677, - 7678, - 7679, - 8204, - 8205, - 8255, - 8256, - 8276, - 8400, - 8401, - 8402, - 8403, - 8404, - 8405, - 8406, - 8407, - 8408, - 8409, - 8410, - 8411, - 8412, - 8417, - 8421, - 8422, - 8423, - 8424, - 8425, - 8426, - 8427, - 8428, - 8429, - 8430, - 8431, - 8432, - 11503, - 11504, - 11505, - 11647, - 11744, - 11745, - 11746, - 11747, - 11748, - 11749, - 11750, - 11751, - 11752, - 11753, - 11754, - 11755, - 11756, - 11757, - 11758, - 11759, - 11760, - 11761, - 11762, - 11763, - 11764, - 11765, - 11766, - 11767, - 11768, - 11769, - 11770, - 11771, - 11772, - 11773, - 11774, - 11775, - 12330, - 12331, - 12332, - 12333, - 12334, - 12335, - 12441, - 12442, - 42528, - 42529, - 42530, - 42531, - 42532, - 42533, - 42534, - 42535, - 42536, - 42537, - 42607, - 42612, - 42613, - 42614, - 42615, - 42616, - 42617, - 42618, - 42619, - 42620, - 42621, - 42655, - 42736, - 42737, - 43010, - 43014, - 43019, - 43043, - 43044, - 43045, - 43046, - 43047, - 43136, - 43137, - 43188, - 43189, - 43190, - 43191, - 43192, - 43193, - 43194, - 43195, - 43196, - 43197, - 43198, - 43199, - 43200, - 43201, - 43202, - 43203, - 43204, - 43216, - 43217, - 43218, - 43219, - 43220, - 43221, - 43222, - 43223, - 43224, - 43225, - 43232, - 43233, - 43234, - 43235, - 43236, - 43237, - 43238, - 43239, - 43240, - 43241, - 43242, - 43243, - 43244, - 43245, - 43246, - 43247, - 43248, - 43249, - 43264, - 43265, - 43266, - 43267, - 43268, - 43269, - 43270, - 43271, - 43272, - 43273, - 43302, - 43303, - 43304, - 43305, - 43306, - 43307, - 43308, - 43309, - 43335, - 43336, - 43337, - 43338, - 43339, - 43340, - 43341, - 43342, - 43343, - 43344, - 43345, - 43346, - 43347, - 43392, - 43393, - 43394, - 43395, - 43443, - 43444, - 43445, - 43446, - 43447, - 43448, - 43449, - 43450, - 43451, - 43452, - 43453, - 43454, - 43455, - 43456, - 43472, - 43473, - 43474, - 43475, - 43476, - 43477, - 43478, - 43479, - 43480, - 43481, - 43561, - 43562, - 43563, - 43564, - 43565, - 43566, - 43567, - 43568, - 43569, - 43570, - 43571, - 43572, - 43573, - 43574, - 43587, - 43596, - 43597, - 43600, - 43601, - 43602, - 43603, - 43604, - 43605, - 43606, - 43607, - 43608, - 43609, - 43643, - 43696, - 43698, - 43699, - 43700, - 43703, - 43704, - 43710, - 43711, - 43713, - 43755, - 43756, - 43757, - 43758, - 43759, - 43765, - 43766, - 44003, - 44004, - 44005, - 44006, - 44007, - 44008, - 44009, - 44010, - 44012, - 44013, - 44016, - 44017, - 44018, - 44019, - 44020, - 44021, - 44022, - 44023, - 44024, - 44025, - 64286, - 65024, - 65025, - 65026, - 65027, - 65028, - 65029, - 65030, - 65031, - 65032, - 65033, - 65034, - 65035, - 65036, - 65037, - 65038, - 65039, - 65056, - 65057, - 65058, - 65059, - 65060, - 65061, - 65062, - 65075, - 65076, - 65101, - 65102, - 65103, - 65296, - 65297, - 65298, - 65299, - 65300, - 65301, - 65302, - 65303, - 65304, - 65305, - 65343 -]; - +var str = '768,769,770,771,772,773,774,775,776,777,778,779,780,781,782,783,784,785,786,787,788,789,790,791,792,793,794,795,796,797,798,799,800,801,802,803,804,805,806,807,808,809,810,811,812,813,814,815,816,817,818,819,820,821,822,823,824,825,826,827,828,829,830,831,832,833,834,835,836,837,838,839,840,841,842,843,844,845,846,847,848,849,850,851,852,853,854,855,856,857,858,859,860,861,862,863,864,865,866,867,868,869,870,871,872,873,874,875,876,877,878,879,1155,1156,1157,1158,1159,1425,1426,1427,1428,1429,1430,1431,1432,1433,1434,1435,1436,1437,1438,1439,1440,1441,1442,1443,1444,1445,1446,1447,1448,1449,1450,1451,1452,1453,1454,1455,1456,1457,1458,1459,1460,1461,1462,1463,1464,1465,1466,1467,1468,1469,1471,1473,1474,1476,1477,1479,1552,1553,1554,1555,1556,1557,1558,1559,1560,1561,1562,1611,1612,1613,1614,1615,1616,1617,1618,1619,1620,1621,1622,1623,1624,1625,1626,1627,1628,1629,1630,1631,1632,1633,1634,1635,1636,1637,1638,1639,1640,1641,1648,1750,1751,1752,1753,1754,1755,1756,1759,1760,1761,1762,1763,1764,1767,1768,1770,1771,1772,1773,1776,1777,1778,1779,1780,1781,1782,1783,1784,1785,1809,1840,1841,1842,1843,1844,1845,1846,1847,1848,1849,1850,1851,1852,1853,1854,1855,1856,1857,1858,1859,1860,1861,1862,1863,1864,1865,1866,1958,1959,1960,1961,1962,1963,1964,1965,1966,1967,1968,1984,1985,1986,1987,1988,1989,1990,1991,1992,1993,2027,2028,2029,2030,2031,2032,2033,2034,2035,2070,2071,2072,2073,2075,2076,2077,2078,2079,2080,2081,2082,2083,2085,2086,2087,2089,2090,2091,2092,2093,2137,2138,2139,2276,2277,2278,2279,2280,2281,2282,2283,2284,2285,2286,2287,2288,2289,2290,2291,2292,2293,2294,2295,2296,2297,2298,2299,2300,2301,2302,2304,2305,2306,2307,2362,2363,2364,2366,2367,2368,2369,2370,2371,2372,2373,2374,2375,2376,2377,2378,2379,2380,2381,2382,2383,2385,2386,2387,2388,2389,2390,2391,2402,2403,2406,2407,2408,2409,2410,2411,2412,2413,2414,2415,2433,2434,2435,2492,2494,2495,2496,2497,2498,2499,2500,2503,2504,2507,2508,2509,2519,2530,2531,2534,2535,2536,2537,2538,2539,2540,2541,2542,2543,2561,2562,2563,2620,2622,2623,2624,2625,2626,2631,2632,2635,2636,2637,2641,2662,2663,2664,2665,2666,2667,2668,2669,2670,2671,2672,2673,2677,2689,2690,2691,2748,2750,2751,2752,2753,2754,2755,2756,2757,2759,2760,2761,2763,2764,2765,2786,2787,2790,2791,2792,2793,2794,2795,2796,2797,2798,2799,2817,2818,2819,2876,2878,2879,2880,2881,2882,2883,2884,2887,2888,2891,2892,2893,2902,2903,2914,2915,2918,2919,2920,2921,2922,2923,2924,2925,2926,2927,2946,3006,3007,3008,3009,3010,3014,3015,3016,3018,3019,3020,3021,3031,3046,3047,3048,3049,3050,3051,3052,3053,3054,3055,3073,3074,3075,3134,3135,3136,3137,3138,3139,3140,3142,3143,3144,3146,3147,3148,3149,3157,3158,3170,3171,3174,3175,3176,3177,3178,3179,3180,3181,3182,3183,3202,3203,3260,3262,3263,3264,3265,3266,3267,3268,3270,3271,3272,3274,3275,3276,3277,3285,3286,3298,3299,3302,3303,3304,3305,3306,3307,3308,3309,3310,3311,3330,3331,3390,3391,3392,3393,3394,3395,3396,3398,3399,3400,3402,3403,3404,3405,3415,3426,3427,3430,3431,3432,3433,3434,3435,3436,3437,3438,3439,3458,3459,3530,3535,3536,3537,3538,3539,3540,3542,3544,3545,3546,3547,3548,3549,3550,3551,3570,3571,3633,3636,3637,3638,3639,3640,3641,3642,3655,3656,3657,3658,3659,3660,3661,3662,3664,3665,3666,3667,3668,3669,3670,3671,3672,3673,3761,3764,3765,3766,3767,3768,3769,3771,3772,3784,3785,3786,3787,3788,3789,3792,3793,3794,3795,3796,3797,3798,3799,3800,3801,3864,3865,3872,3873,3874,3875,3876,3877,3878,3879,3880,3881,3893,3895,3897,3902,3903,3953,3954,3955,3956,3957,3958,3959,3960,3961,3962,3963,3964,3965,3966,3967,3968,3969,3970,3971,3972,3974,3975,3981,3982,3983,3984,3985,3986,3987,3988,3989,3990,3991,3993,3994,3995,3996,3997,3998,3999,4000,4001,4002,4003,4004,4005,4006,4007,4008,4009,4010,4011,4012,4013,4014,4015,4016,4017,4018,4019,4020,4021,4022,4023,4024,4025,4026,4027,4028,4038,4139,4140,4141,4142,4143,4144,4145,4146,4147,4148,4149,4150,4151,4152,4153,4154,4155,4156,4157,4158,4160,4161,4162,4163,4164,4165,4166,4167,4168,4169,4182,4183,4184,4185,4190,4191,4192,4194,4195,4196,4199,4200,4201,4202,4203,4204,4205,4209,4210,4211,4212,4226,4227,4228,4229,4230,4231,4232,4233,4234,4235,4236,4237,4239,4240,4241,4242,4243,4244,4245,4246,4247,4248,4249,4250,4251,4252,4253,4957,4958,4959,5906,5907,5908,5938,5939,5940,5970,5971,6002,6003,6068,6069,6070,6071,6072,6073,6074,6075,6076,6077,6078,6079,6080,6081,6082,6083,6084,6085,6086,6087,6088,6089,6090,6091,6092,6093,6094,6095,6096,6097,6098,6099,6109,6112,6113,6114,6115,6116,6117,6118,6119,6120,6121,6155,6156,6157,6160,6161,6162,6163,6164,6165,6166,6167,6168,6169,6313,6432,6433,6434,6435,6436,6437,6438,6439,6440,6441,6442,6443,6448,6449,6450,6451,6452,6453,6454,6455,6456,6457,6458,6459,6470,6471,6472,6473,6474,6475,6476,6477,6478,6479,6576,6577,6578,6579,6580,6581,6582,6583,6584,6585,6586,6587,6588,6589,6590,6591,6592,6600,6601,6608,6609,6610,6611,6612,6613,6614,6615,6616,6617,6679,6680,6681,6682,6683,6741,6742,6743,6744,6745,6746,6747,6748,6749,6750,6752,6753,6754,6755,6756,6757,6758,6759,6760,6761,6762,6763,6764,6765,6766,6767,6768,6769,6770,6771,6772,6773,6774,6775,6776,6777,6778,6779,6780,6783,6784,6785,6786,6787,6788,6789,6790,6791,6792,6793,6800,6801,6802,6803,6804,6805,6806,6807,6808,6809,6912,6913,6914,6915,6916,6964,6965,6966,6967,6968,6969,6970,6971,6972,6973,6974,6975,6976,6977,6978,6979,6980,6992,6993,6994,6995,6996,6997,6998,6999,7000,7001,7019,7020,7021,7022,7023,7024,7025,7026,7027,7040,7041,7042,7073,7074,7075,7076,7077,7078,7079,7080,7081,7082,7083,7084,7085,7088,7089,7090,7091,7092,7093,7094,7095,7096,7097,7142,7143,7144,7145,7146,7147,7148,7149,7150,7151,7152,7153,7154,7155,7204,7205,7206,7207,7208,7209,7210,7211,7212,7213,7214,7215,7216,7217,7218,7219,7220,7221,7222,7223,7232,7233,7234,7235,7236,7237,7238,7239,7240,7241,7248,7249,7250,7251,7252,7253,7254,7255,7256,7257,7376,7377,7378,7380,7381,7382,7383,7384,7385,7386,7387,7388,7389,7390,7391,7392,7393,7394,7395,7396,7397,7398,7399,7400,7405,7410,7411,7412,7616,7617,7618,7619,7620,7621,7622,7623,7624,7625,7626,7627,7628,7629,7630,7631,7632,7633,7634,7635,7636,7637,7638,7639,7640,7641,7642,7643,7644,7645,7646,7647,7648,7649,7650,7651,7652,7653,7654,7676,7677,7678,7679,8204,8205,8255,8256,8276,8400,8401,8402,8403,8404,8405,8406,8407,8408,8409,8410,8411,8412,8417,8421,8422,8423,8424,8425,8426,8427,8428,8429,8430,8431,8432,11503,11504,11505,11647,11744,11745,11746,11747,11748,11749,11750,11751,11752,11753,11754,11755,11756,11757,11758,11759,11760,11761,11762,11763,11764,11765,11766,11767,11768,11769,11770,11771,11772,11773,11774,11775,12330,12331,12332,12333,12334,12335,12441,12442,42528,42529,42530,42531,42532,42533,42534,42535,42536,42537,42607,42612,42613,42614,42615,42616,42617,42618,42619,42620,42621,42655,42736,42737,43010,43014,43019,43043,43044,43045,43046,43047,43136,43137,43188,43189,43190,43191,43192,43193,43194,43195,43196,43197,43198,43199,43200,43201,43202,43203,43204,43216,43217,43218,43219,43220,43221,43222,43223,43224,43225,43232,43233,43234,43235,43236,43237,43238,43239,43240,43241,43242,43243,43244,43245,43246,43247,43248,43249,43264,43265,43266,43267,43268,43269,43270,43271,43272,43273,43302,43303,43304,43305,43306,43307,43308,43309,43335,43336,43337,43338,43339,43340,43341,43342,43343,43344,43345,43346,43347,43392,43393,43394,43395,43443,43444,43445,43446,43447,43448,43449,43450,43451,43452,43453,43454,43455,43456,43472,43473,43474,43475,43476,43477,43478,43479,43480,43481,43561,43562,43563,43564,43565,43566,43567,43568,43569,43570,43571,43572,43573,43574,43587,43596,43597,43600,43601,43602,43603,43604,43605,43606,43607,43608,43609,43643,43696,43698,43699,43700,43703,43704,43710,43711,43713,43755,43756,43757,43758,43759,43765,43766,44003,44004,44005,44006,44007,44008,44009,44010,44012,44013,44016,44017,44018,44019,44020,44021,44022,44023,44024,44025,64286,65024,65025,65026,65027,65028,65029,65030,65031,65032,65033,65034,65035,65036,65037,65038,65039,65056,65057,65058,65059,65060,65061,65062,65075,65076,65101,65102,65103,65296,65297,65298,65299,65300,65301,65302,65303,65304,65305,65343'; +var arr = str.split(',').map(function(code) { + return parseInt(code, 10); +}); +module.exports = arr; },{}],3:[function(require,module,exports){ -module.exports = [ - 170, - 181, - 186, - 192, - 193, - 194, - 195, - 196, - 197, - 198, - 199, - 200, - 201, - 202, - 203, - 204, - 205, - 206, - 207, - 208, - 209, - 210, - 211, - 212, - 213, - 214, - 216, - 217, - 218, - 219, - 220, - 221, - 222, - 223, - 224, - 225, - 226, - 227, - 228, - 229, - 230, - 231, - 232, - 233, - 234, - 235, - 236, - 237, - 238, - 239, - 240, - 241, - 242, - 243, - 244, - 245, - 246, - 248, - 249, - 250, - 251, - 252, - 253, - 254, - 255, - 256, - 257, - 258, - 259, - 260, - 261, - 262, - 263, - 264, - 265, - 266, - 267, - 268, - 269, - 270, - 271, - 272, - 273, - 274, - 275, - 276, - 277, - 278, - 279, - 280, - 281, - 282, - 283, - 284, - 285, - 286, - 287, - 288, - 289, - 290, - 291, - 292, - 293, - 294, - 295, - 296, - 297, - 298, - 299, - 300, - 301, - 302, - 303, - 304, - 305, - 306, - 307, - 308, - 309, - 310, - 311, - 312, - 313, - 314, - 315, - 316, - 317, - 318, - 319, - 320, - 321, - 322, - 323, - 324, - 325, - 326, - 327, - 328, - 329, - 330, - 331, - 332, - 333, - 334, - 335, - 336, - 337, - 338, - 339, - 340, - 341, - 342, - 343, - 344, - 345, - 346, - 347, - 348, - 349, - 350, - 351, - 352, - 353, - 354, - 355, - 356, - 357, - 358, - 359, - 360, - 361, - 362, - 363, - 364, - 365, - 366, - 367, - 368, - 369, - 370, - 371, - 372, - 373, - 374, - 375, - 376, - 377, - 378, - 379, - 380, - 381, - 382, - 383, - 384, - 385, - 386, - 387, - 388, - 389, - 390, - 391, - 392, - 393, - 394, - 395, - 396, - 397, - 398, - 399, - 400, - 401, - 402, - 403, - 404, - 405, - 406, - 407, - 408, - 409, - 410, - 411, - 412, - 413, - 414, - 415, - 416, - 417, - 418, - 419, - 420, - 421, - 422, - 423, - 424, - 425, - 426, - 427, - 428, - 429, - 430, - 431, - 432, - 433, - 434, - 435, - 436, - 437, - 438, - 439, - 440, - 441, - 442, - 443, - 444, - 445, - 446, - 447, - 448, - 449, - 450, - 451, - 452, - 453, - 454, - 455, - 456, - 457, - 458, - 459, - 460, - 461, - 462, - 463, - 464, - 465, - 466, - 467, - 468, - 469, - 470, - 471, - 472, - 473, - 474, - 475, - 476, - 477, - 478, - 479, - 480, - 481, - 482, - 483, - 484, - 485, - 486, - 487, - 488, - 489, - 490, - 491, - 492, - 493, - 494, - 495, - 496, - 497, - 498, - 499, - 500, - 501, - 502, - 503, - 504, - 505, - 506, - 507, - 508, - 509, - 510, - 511, - 512, - 513, - 514, - 515, - 516, - 517, - 518, - 519, - 520, - 521, - 522, - 523, - 524, - 525, - 526, - 527, - 528, - 529, - 530, - 531, - 532, - 533, - 534, - 535, - 536, - 537, - 538, - 539, - 540, - 541, - 542, - 543, - 544, - 545, - 546, - 547, - 548, - 549, - 550, - 551, - 552, - 553, - 554, - 555, - 556, - 557, - 558, - 559, - 560, - 561, - 562, - 563, - 564, - 565, - 566, - 567, - 568, - 569, - 570, - 571, - 572, - 573, - 574, - 575, - 576, - 577, - 578, - 579, - 580, - 581, - 582, - 583, - 584, - 585, - 586, - 587, - 588, - 589, - 590, - 591, - 592, - 593, - 594, - 595, - 596, - 597, - 598, - 599, - 600, - 601, - 602, - 603, - 604, - 605, - 606, - 607, - 608, - 609, - 610, - 611, - 612, - 613, - 614, - 615, - 616, - 617, - 618, - 619, - 620, - 621, - 622, - 623, - 624, - 625, - 626, - 627, - 628, - 629, - 630, - 631, - 632, - 633, - 634, - 635, - 636, - 637, - 638, - 639, - 640, - 641, - 642, - 643, - 644, - 645, - 646, - 647, - 648, - 649, - 650, - 651, - 652, - 653, - 654, - 655, - 656, - 657, - 658, - 659, - 660, - 661, - 662, - 663, - 664, - 665, - 666, - 667, - 668, - 669, - 670, - 671, - 672, - 673, - 674, - 675, - 676, - 677, - 678, - 679, - 680, - 681, - 682, - 683, - 684, - 685, - 686, - 687, - 688, - 689, - 690, - 691, - 692, - 693, - 694, - 695, - 696, - 697, - 698, - 699, - 700, - 701, - 702, - 703, - 704, - 705, - 710, - 711, - 712, - 713, - 714, - 715, - 716, - 717, - 718, - 719, - 720, - 721, - 736, - 737, - 738, - 739, - 740, - 748, - 750, - 880, - 881, - 882, - 883, - 884, - 886, - 887, - 890, - 891, - 892, - 893, - 902, - 904, - 905, - 906, - 908, - 910, - 911, - 912, - 913, - 914, - 915, - 916, - 917, - 918, - 919, - 920, - 921, - 922, - 923, - 924, - 925, - 926, - 927, - 928, - 929, - 931, - 932, - 933, - 934, - 935, - 936, - 937, - 938, - 939, - 940, - 941, - 942, - 943, - 944, - 945, - 946, - 947, - 948, - 949, - 950, - 951, - 952, - 953, - 954, - 955, - 956, - 957, - 958, - 959, - 960, - 961, - 962, - 963, - 964, - 965, - 966, - 967, - 968, - 969, - 970, - 971, - 972, - 973, - 974, - 975, - 976, - 977, - 978, - 979, - 980, - 981, - 982, - 983, - 984, - 985, - 986, - 987, - 988, - 989, - 990, - 991, - 992, - 993, - 994, - 995, - 996, - 997, - 998, - 999, - 1000, - 1001, - 1002, - 1003, - 1004, - 1005, - 1006, - 1007, - 1008, - 1009, - 1010, - 1011, - 1012, - 1013, - 1015, - 1016, - 1017, - 1018, - 1019, - 1020, - 1021, - 1022, - 1023, - 1024, - 1025, - 1026, - 1027, - 1028, - 1029, - 1030, - 1031, - 1032, - 1033, - 1034, - 1035, - 1036, - 1037, - 1038, - 1039, - 1040, - 1041, - 1042, - 1043, - 1044, - 1045, - 1046, - 1047, - 1048, - 1049, - 1050, - 1051, - 1052, - 1053, - 1054, - 1055, - 1056, - 1057, - 1058, - 1059, - 1060, - 1061, - 1062, - 1063, - 1064, - 1065, - 1066, - 1067, - 1068, - 1069, - 1070, - 1071, - 1072, - 1073, - 1074, - 1075, - 1076, - 1077, - 1078, - 1079, - 1080, - 1081, - 1082, - 1083, - 1084, - 1085, - 1086, - 1087, - 1088, - 1089, - 1090, - 1091, - 1092, - 1093, - 1094, - 1095, - 1096, - 1097, - 1098, - 1099, - 1100, - 1101, - 1102, - 1103, - 1104, - 1105, - 1106, - 1107, - 1108, - 1109, - 1110, - 1111, - 1112, - 1113, - 1114, - 1115, - 1116, - 1117, - 1118, - 1119, - 1120, - 1121, - 1122, - 1123, - 1124, - 1125, - 1126, - 1127, - 1128, - 1129, - 1130, - 1131, - 1132, - 1133, - 1134, - 1135, - 1136, - 1137, - 1138, - 1139, - 1140, - 1141, - 1142, - 1143, - 1144, - 1145, - 1146, - 1147, - 1148, - 1149, - 1150, - 1151, - 1152, - 1153, - 1162, - 1163, - 1164, - 1165, - 1166, - 1167, - 1168, - 1169, - 1170, - 1171, - 1172, - 1173, - 1174, - 1175, - 1176, - 1177, - 1178, - 1179, - 1180, - 1181, - 1182, - 1183, - 1184, - 1185, - 1186, - 1187, - 1188, - 1189, - 1190, - 1191, - 1192, - 1193, - 1194, - 1195, - 1196, - 1197, - 1198, - 1199, - 1200, - 1201, - 1202, - 1203, - 1204, - 1205, - 1206, - 1207, - 1208, - 1209, - 1210, - 1211, - 1212, - 1213, - 1214, - 1215, - 1216, - 1217, - 1218, - 1219, - 1220, - 1221, - 1222, - 1223, - 1224, - 1225, - 1226, - 1227, - 1228, - 1229, - 1230, - 1231, - 1232, - 1233, - 1234, - 1235, - 1236, - 1237, - 1238, - 1239, - 1240, - 1241, - 1242, - 1243, - 1244, - 1245, - 1246, - 1247, - 1248, - 1249, - 1250, - 1251, - 1252, - 1253, - 1254, - 1255, - 1256, - 1257, - 1258, - 1259, - 1260, - 1261, - 1262, - 1263, - 1264, - 1265, - 1266, - 1267, - 1268, - 1269, - 1270, - 1271, - 1272, - 1273, - 1274, - 1275, - 1276, - 1277, - 1278, - 1279, - 1280, - 1281, - 1282, - 1283, - 1284, - 1285, - 1286, - 1287, - 1288, - 1289, - 1290, - 1291, - 1292, - 1293, - 1294, - 1295, - 1296, - 1297, - 1298, - 1299, - 1300, - 1301, - 1302, - 1303, - 1304, - 1305, - 1306, - 1307, - 1308, - 1309, - 1310, - 1311, - 1312, - 1313, - 1314, - 1315, - 1316, - 1317, - 1318, - 1319, - 1329, - 1330, - 1331, - 1332, - 1333, - 1334, - 1335, - 1336, - 1337, - 1338, - 1339, - 1340, - 1341, - 1342, - 1343, - 1344, - 1345, - 1346, - 1347, - 1348, - 1349, - 1350, - 1351, - 1352, - 1353, - 1354, - 1355, - 1356, - 1357, - 1358, - 1359, - 1360, - 1361, - 1362, - 1363, - 1364, - 1365, - 1366, - 1369, - 1377, - 1378, - 1379, - 1380, - 1381, - 1382, - 1383, - 1384, - 1385, - 1386, - 1387, - 1388, - 1389, - 1390, - 1391, - 1392, - 1393, - 1394, - 1395, - 1396, - 1397, - 1398, - 1399, - 1400, - 1401, - 1402, - 1403, - 1404, - 1405, - 1406, - 1407, - 1408, - 1409, - 1410, - 1411, - 1412, - 1413, - 1414, - 1415, - 1488, - 1489, - 1490, - 1491, - 1492, - 1493, - 1494, - 1495, - 1496, - 1497, - 1498, - 1499, - 1500, - 1501, - 1502, - 1503, - 1504, - 1505, - 1506, - 1507, - 1508, - 1509, - 1510, - 1511, - 1512, - 1513, - 1514, - 1520, - 1521, - 1522, - 1568, - 1569, - 1570, - 1571, - 1572, - 1573, - 1574, - 1575, - 1576, - 1577, - 1578, - 1579, - 1580, - 1581, - 1582, - 1583, - 1584, - 1585, - 1586, - 1587, - 1588, - 1589, - 1590, - 1591, - 1592, - 1593, - 1594, - 1595, - 1596, - 1597, - 1598, - 1599, - 1600, - 1601, - 1602, - 1603, - 1604, - 1605, - 1606, - 1607, - 1608, - 1609, - 1610, - 1646, - 1647, - 1649, - 1650, - 1651, - 1652, - 1653, - 1654, - 1655, - 1656, - 1657, - 1658, - 1659, - 1660, - 1661, - 1662, - 1663, - 1664, - 1665, - 1666, - 1667, - 1668, - 1669, - 1670, - 1671, - 1672, - 1673, - 1674, - 1675, - 1676, - 1677, - 1678, - 1679, - 1680, - 1681, - 1682, - 1683, - 1684, - 1685, - 1686, - 1687, - 1688, - 1689, - 1690, - 1691, - 1692, - 1693, - 1694, - 1695, - 1696, - 1697, - 1698, - 1699, - 1700, - 1701, - 1702, - 1703, - 1704, - 1705, - 1706, - 1707, - 1708, - 1709, - 1710, - 1711, - 1712, - 1713, - 1714, - 1715, - 1716, - 1717, - 1718, - 1719, - 1720, - 1721, - 1722, - 1723, - 1724, - 1725, - 1726, - 1727, - 1728, - 1729, - 1730, - 1731, - 1732, - 1733, - 1734, - 1735, - 1736, - 1737, - 1738, - 1739, - 1740, - 1741, - 1742, - 1743, - 1744, - 1745, - 1746, - 1747, - 1749, - 1765, - 1766, - 1774, - 1775, - 1786, - 1787, - 1788, - 1791, - 1808, - 1810, - 1811, - 1812, - 1813, - 1814, - 1815, - 1816, - 1817, - 1818, - 1819, - 1820, - 1821, - 1822, - 1823, - 1824, - 1825, - 1826, - 1827, - 1828, - 1829, - 1830, - 1831, - 1832, - 1833, - 1834, - 1835, - 1836, - 1837, - 1838, - 1839, - 1869, - 1870, - 1871, - 1872, - 1873, - 1874, - 1875, - 1876, - 1877, - 1878, - 1879, - 1880, - 1881, - 1882, - 1883, - 1884, - 1885, - 1886, - 1887, - 1888, - 1889, - 1890, - 1891, - 1892, - 1893, - 1894, - 1895, - 1896, - 1897, - 1898, - 1899, - 1900, - 1901, - 1902, - 1903, - 1904, - 1905, - 1906, - 1907, - 1908, - 1909, - 1910, - 1911, - 1912, - 1913, - 1914, - 1915, - 1916, - 1917, - 1918, - 1919, - 1920, - 1921, - 1922, - 1923, - 1924, - 1925, - 1926, - 1927, - 1928, - 1929, - 1930, - 1931, - 1932, - 1933, - 1934, - 1935, - 1936, - 1937, - 1938, - 1939, - 1940, - 1941, - 1942, - 1943, - 1944, - 1945, - 1946, - 1947, - 1948, - 1949, - 1950, - 1951, - 1952, - 1953, - 1954, - 1955, - 1956, - 1957, - 1969, - 1994, - 1995, - 1996, - 1997, - 1998, - 1999, - 2000, - 2001, - 2002, - 2003, - 2004, - 2005, - 2006, - 2007, - 2008, - 2009, - 2010, - 2011, - 2012, - 2013, - 2014, - 2015, - 2016, - 2017, - 2018, - 2019, - 2020, - 2021, - 2022, - 2023, - 2024, - 2025, - 2026, - 2036, - 2037, - 2042, - 2048, - 2049, - 2050, - 2051, - 2052, - 2053, - 2054, - 2055, - 2056, - 2057, - 2058, - 2059, - 2060, - 2061, - 2062, - 2063, - 2064, - 2065, - 2066, - 2067, - 2068, - 2069, - 2074, - 2084, - 2088, - 2112, - 2113, - 2114, - 2115, - 2116, - 2117, - 2118, - 2119, - 2120, - 2121, - 2122, - 2123, - 2124, - 2125, - 2126, - 2127, - 2128, - 2129, - 2130, - 2131, - 2132, - 2133, - 2134, - 2135, - 2136, - 2208, - 2210, - 2211, - 2212, - 2213, - 2214, - 2215, - 2216, - 2217, - 2218, - 2219, - 2220, - 2308, - 2309, - 2310, - 2311, - 2312, - 2313, - 2314, - 2315, - 2316, - 2317, - 2318, - 2319, - 2320, - 2321, - 2322, - 2323, - 2324, - 2325, - 2326, - 2327, - 2328, - 2329, - 2330, - 2331, - 2332, - 2333, - 2334, - 2335, - 2336, - 2337, - 2338, - 2339, - 2340, - 2341, - 2342, - 2343, - 2344, - 2345, - 2346, - 2347, - 2348, - 2349, - 2350, - 2351, - 2352, - 2353, - 2354, - 2355, - 2356, - 2357, - 2358, - 2359, - 2360, - 2361, - 2365, - 2384, - 2392, - 2393, - 2394, - 2395, - 2396, - 2397, - 2398, - 2399, - 2400, - 2401, - 2417, - 2418, - 2419, - 2420, - 2421, - 2422, - 2423, - 2425, - 2426, - 2427, - 2428, - 2429, - 2430, - 2431, - 2437, - 2438, - 2439, - 2440, - 2441, - 2442, - 2443, - 2444, - 2447, - 2448, - 2451, - 2452, - 2453, - 2454, - 2455, - 2456, - 2457, - 2458, - 2459, - 2460, - 2461, - 2462, - 2463, - 2464, - 2465, - 2466, - 2467, - 2468, - 2469, - 2470, - 2471, - 2472, - 2474, - 2475, - 2476, - 2477, - 2478, - 2479, - 2480, - 2482, - 2486, - 2487, - 2488, - 2489, - 2493, - 2510, - 2524, - 2525, - 2527, - 2528, - 2529, - 2544, - 2545, - 2565, - 2566, - 2567, - 2568, - 2569, - 2570, - 2575, - 2576, - 2579, - 2580, - 2581, - 2582, - 2583, - 2584, - 2585, - 2586, - 2587, - 2588, - 2589, - 2590, - 2591, - 2592, - 2593, - 2594, - 2595, - 2596, - 2597, - 2598, - 2599, - 2600, - 2602, - 2603, - 2604, - 2605, - 2606, - 2607, - 2608, - 2610, - 2611, - 2613, - 2614, - 2616, - 2617, - 2649, - 2650, - 2651, - 2652, - 2654, - 2674, - 2675, - 2676, - 2693, - 2694, - 2695, - 2696, - 2697, - 2698, - 2699, - 2700, - 2701, - 2703, - 2704, - 2705, - 2707, - 2708, - 2709, - 2710, - 2711, - 2712, - 2713, - 2714, - 2715, - 2716, - 2717, - 2718, - 2719, - 2720, - 2721, - 2722, - 2723, - 2724, - 2725, - 2726, - 2727, - 2728, - 2730, - 2731, - 2732, - 2733, - 2734, - 2735, - 2736, - 2738, - 2739, - 2741, - 2742, - 2743, - 2744, - 2745, - 2749, - 2768, - 2784, - 2785, - 2821, - 2822, - 2823, - 2824, - 2825, - 2826, - 2827, - 2828, - 2831, - 2832, - 2835, - 2836, - 2837, - 2838, - 2839, - 2840, - 2841, - 2842, - 2843, - 2844, - 2845, - 2846, - 2847, - 2848, - 2849, - 2850, - 2851, - 2852, - 2853, - 2854, - 2855, - 2856, - 2858, - 2859, - 2860, - 2861, - 2862, - 2863, - 2864, - 2866, - 2867, - 2869, - 2870, - 2871, - 2872, - 2873, - 2877, - 2908, - 2909, - 2911, - 2912, - 2913, - 2929, - 2947, - 2949, - 2950, - 2951, - 2952, - 2953, - 2954, - 2958, - 2959, - 2960, - 2962, - 2963, - 2964, - 2965, - 2969, - 2970, - 2972, - 2974, - 2975, - 2979, - 2980, - 2984, - 2985, - 2986, - 2990, - 2991, - 2992, - 2993, - 2994, - 2995, - 2996, - 2997, - 2998, - 2999, - 3000, - 3001, - 3024, - 3077, - 3078, - 3079, - 3080, - 3081, - 3082, - 3083, - 3084, - 3086, - 3087, - 3088, - 3090, - 3091, - 3092, - 3093, - 3094, - 3095, - 3096, - 3097, - 3098, - 3099, - 3100, - 3101, - 3102, - 3103, - 3104, - 3105, - 3106, - 3107, - 3108, - 3109, - 3110, - 3111, - 3112, - 3114, - 3115, - 3116, - 3117, - 3118, - 3119, - 3120, - 3121, - 3122, - 3123, - 3125, - 3126, - 3127, - 3128, - 3129, - 3133, - 3160, - 3161, - 3168, - 3169, - 3205, - 3206, - 3207, - 3208, - 3209, - 3210, - 3211, - 3212, - 3214, - 3215, - 3216, - 3218, - 3219, - 3220, - 3221, - 3222, - 3223, - 3224, - 3225, - 3226, - 3227, - 3228, - 3229, - 3230, - 3231, - 3232, - 3233, - 3234, - 3235, - 3236, - 3237, - 3238, - 3239, - 3240, - 3242, - 3243, - 3244, - 3245, - 3246, - 3247, - 3248, - 3249, - 3250, - 3251, - 3253, - 3254, - 3255, - 3256, - 3257, - 3261, - 3294, - 3296, - 3297, - 3313, - 3314, - 3333, - 3334, - 3335, - 3336, - 3337, - 3338, - 3339, - 3340, - 3342, - 3343, - 3344, - 3346, - 3347, - 3348, - 3349, - 3350, - 3351, - 3352, - 3353, - 3354, - 3355, - 3356, - 3357, - 3358, - 3359, - 3360, - 3361, - 3362, - 3363, - 3364, - 3365, - 3366, - 3367, - 3368, - 3369, - 3370, - 3371, - 3372, - 3373, - 3374, - 3375, - 3376, - 3377, - 3378, - 3379, - 3380, - 3381, - 3382, - 3383, - 3384, - 3385, - 3386, - 3389, - 3406, - 3424, - 3425, - 3450, - 3451, - 3452, - 3453, - 3454, - 3455, - 3461, - 3462, - 3463, - 3464, - 3465, - 3466, - 3467, - 3468, - 3469, - 3470, - 3471, - 3472, - 3473, - 3474, - 3475, - 3476, - 3477, - 3478, - 3482, - 3483, - 3484, - 3485, - 3486, - 3487, - 3488, - 3489, - 3490, - 3491, - 3492, - 3493, - 3494, - 3495, - 3496, - 3497, - 3498, - 3499, - 3500, - 3501, - 3502, - 3503, - 3504, - 3505, - 3507, - 3508, - 3509, - 3510, - 3511, - 3512, - 3513, - 3514, - 3515, - 3517, - 3520, - 3521, - 3522, - 3523, - 3524, - 3525, - 3526, - 3585, - 3586, - 3587, - 3588, - 3589, - 3590, - 3591, - 3592, - 3593, - 3594, - 3595, - 3596, - 3597, - 3598, - 3599, - 3600, - 3601, - 3602, - 3603, - 3604, - 3605, - 3606, - 3607, - 3608, - 3609, - 3610, - 3611, - 3612, - 3613, - 3614, - 3615, - 3616, - 3617, - 3618, - 3619, - 3620, - 3621, - 3622, - 3623, - 3624, - 3625, - 3626, - 3627, - 3628, - 3629, - 3630, - 3631, - 3632, - 3634, - 3635, - 3648, - 3649, - 3650, - 3651, - 3652, - 3653, - 3654, - 3713, - 3714, - 3716, - 3719, - 3720, - 3722, - 3725, - 3732, - 3733, - 3734, - 3735, - 3737, - 3738, - 3739, - 3740, - 3741, - 3742, - 3743, - 3745, - 3746, - 3747, - 3749, - 3751, - 3754, - 3755, - 3757, - 3758, - 3759, - 3760, - 3762, - 3763, - 3773, - 3776, - 3777, - 3778, - 3779, - 3780, - 3782, - 3804, - 3805, - 3806, - 3807, - 3840, - 3904, - 3905, - 3906, - 3907, - 3908, - 3909, - 3910, - 3911, - 3913, - 3914, - 3915, - 3916, - 3917, - 3918, - 3919, - 3920, - 3921, - 3922, - 3923, - 3924, - 3925, - 3926, - 3927, - 3928, - 3929, - 3930, - 3931, - 3932, - 3933, - 3934, - 3935, - 3936, - 3937, - 3938, - 3939, - 3940, - 3941, - 3942, - 3943, - 3944, - 3945, - 3946, - 3947, - 3948, - 3976, - 3977, - 3978, - 3979, - 3980, - 4096, - 4097, - 4098, - 4099, - 4100, - 4101, - 4102, - 4103, - 4104, - 4105, - 4106, - 4107, - 4108, - 4109, - 4110, - 4111, - 4112, - 4113, - 4114, - 4115, - 4116, - 4117, - 4118, - 4119, - 4120, - 4121, - 4122, - 4123, - 4124, - 4125, - 4126, - 4127, - 4128, - 4129, - 4130, - 4131, - 4132, - 4133, - 4134, - 4135, - 4136, - 4137, - 4138, - 4159, - 4176, - 4177, - 4178, - 4179, - 4180, - 4181, - 4186, - 4187, - 4188, - 4189, - 4193, - 4197, - 4198, - 4206, - 4207, - 4208, - 4213, - 4214, - 4215, - 4216, - 4217, - 4218, - 4219, - 4220, - 4221, - 4222, - 4223, - 4224, - 4225, - 4238, - 4256, - 4257, - 4258, - 4259, - 4260, - 4261, - 4262, - 4263, - 4264, - 4265, - 4266, - 4267, - 4268, - 4269, - 4270, - 4271, - 4272, - 4273, - 4274, - 4275, - 4276, - 4277, - 4278, - 4279, - 4280, - 4281, - 4282, - 4283, - 4284, - 4285, - 4286, - 4287, - 4288, - 4289, - 4290, - 4291, - 4292, - 4293, - 4295, - 4301, - 4304, - 4305, - 4306, - 4307, - 4308, - 4309, - 4310, - 4311, - 4312, - 4313, - 4314, - 4315, - 4316, - 4317, - 4318, - 4319, - 4320, - 4321, - 4322, - 4323, - 4324, - 4325, - 4326, - 4327, - 4328, - 4329, - 4330, - 4331, - 4332, - 4333, - 4334, - 4335, - 4336, - 4337, - 4338, - 4339, - 4340, - 4341, - 4342, - 4343, - 4344, - 4345, - 4346, - 4348, - 4349, - 4350, - 4351, - 4352, - 4353, - 4354, - 4355, - 4356, - 4357, - 4358, - 4359, - 4360, - 4361, - 4362, - 4363, - 4364, - 4365, - 4366, - 4367, - 4368, - 4369, - 4370, - 4371, - 4372, - 4373, - 4374, - 4375, - 4376, - 4377, - 4378, - 4379, - 4380, - 4381, - 4382, - 4383, - 4384, - 4385, - 4386, - 4387, - 4388, - 4389, - 4390, - 4391, - 4392, - 4393, - 4394, - 4395, - 4396, - 4397, - 4398, - 4399, - 4400, - 4401, - 4402, - 4403, - 4404, - 4405, - 4406, - 4407, - 4408, - 4409, - 4410, - 4411, - 4412, - 4413, - 4414, - 4415, - 4416, - 4417, - 4418, - 4419, - 4420, - 4421, - 4422, - 4423, - 4424, - 4425, - 4426, - 4427, - 4428, - 4429, - 4430, - 4431, - 4432, - 4433, - 4434, - 4435, - 4436, - 4437, - 4438, - 4439, - 4440, - 4441, - 4442, - 4443, - 4444, - 4445, - 4446, - 4447, - 4448, - 4449, - 4450, - 4451, - 4452, - 4453, - 4454, - 4455, - 4456, - 4457, - 4458, - 4459, - 4460, - 4461, - 4462, - 4463, - 4464, - 4465, - 4466, - 4467, - 4468, - 4469, - 4470, - 4471, - 4472, - 4473, - 4474, - 4475, - 4476, - 4477, - 4478, - 4479, - 4480, - 4481, - 4482, - 4483, - 4484, - 4485, - 4486, - 4487, - 4488, - 4489, - 4490, - 4491, - 4492, - 4493, - 4494, - 4495, - 4496, - 4497, - 4498, - 4499, - 4500, - 4501, - 4502, - 4503, - 4504, - 4505, - 4506, - 4507, - 4508, - 4509, - 4510, - 4511, - 4512, - 4513, - 4514, - 4515, - 4516, - 4517, - 4518, - 4519, - 4520, - 4521, - 4522, - 4523, - 4524, - 4525, - 4526, - 4527, - 4528, - 4529, - 4530, - 4531, - 4532, - 4533, - 4534, - 4535, - 4536, - 4537, - 4538, - 4539, - 4540, - 4541, - 4542, - 4543, - 4544, - 4545, - 4546, - 4547, - 4548, - 4549, - 4550, - 4551, - 4552, - 4553, - 4554, - 4555, - 4556, - 4557, - 4558, - 4559, - 4560, - 4561, - 4562, - 4563, - 4564, - 4565, - 4566, - 4567, - 4568, - 4569, - 4570, - 4571, - 4572, - 4573, - 4574, - 4575, - 4576, - 4577, - 4578, - 4579, - 4580, - 4581, - 4582, - 4583, - 4584, - 4585, - 4586, - 4587, - 4588, - 4589, - 4590, - 4591, - 4592, - 4593, - 4594, - 4595, - 4596, - 4597, - 4598, - 4599, - 4600, - 4601, - 4602, - 4603, - 4604, - 4605, - 4606, - 4607, - 4608, - 4609, - 4610, - 4611, - 4612, - 4613, - 4614, - 4615, - 4616, - 4617, - 4618, - 4619, - 4620, - 4621, - 4622, - 4623, - 4624, - 4625, - 4626, - 4627, - 4628, - 4629, - 4630, - 4631, - 4632, - 4633, - 4634, - 4635, - 4636, - 4637, - 4638, - 4639, - 4640, - 4641, - 4642, - 4643, - 4644, - 4645, - 4646, - 4647, - 4648, - 4649, - 4650, - 4651, - 4652, - 4653, - 4654, - 4655, - 4656, - 4657, - 4658, - 4659, - 4660, - 4661, - 4662, - 4663, - 4664, - 4665, - 4666, - 4667, - 4668, - 4669, - 4670, - 4671, - 4672, - 4673, - 4674, - 4675, - 4676, - 4677, - 4678, - 4679, - 4680, - 4682, - 4683, - 4684, - 4685, - 4688, - 4689, - 4690, - 4691, - 4692, - 4693, - 4694, - 4696, - 4698, - 4699, - 4700, - 4701, - 4704, - 4705, - 4706, - 4707, - 4708, - 4709, - 4710, - 4711, - 4712, - 4713, - 4714, - 4715, - 4716, - 4717, - 4718, - 4719, - 4720, - 4721, - 4722, - 4723, - 4724, - 4725, - 4726, - 4727, - 4728, - 4729, - 4730, - 4731, - 4732, - 4733, - 4734, - 4735, - 4736, - 4737, - 4738, - 4739, - 4740, - 4741, - 4742, - 4743, - 4744, - 4746, - 4747, - 4748, - 4749, - 4752, - 4753, - 4754, - 4755, - 4756, - 4757, - 4758, - 4759, - 4760, - 4761, - 4762, - 4763, - 4764, - 4765, - 4766, - 4767, - 4768, - 4769, - 4770, - 4771, - 4772, - 4773, - 4774, - 4775, - 4776, - 4777, - 4778, - 4779, - 4780, - 4781, - 4782, - 4783, - 4784, - 4786, - 4787, - 4788, - 4789, - 4792, - 4793, - 4794, - 4795, - 4796, - 4797, - 4798, - 4800, - 4802, - 4803, - 4804, - 4805, - 4808, - 4809, - 4810, - 4811, - 4812, - 4813, - 4814, - 4815, - 4816, - 4817, - 4818, - 4819, - 4820, - 4821, - 4822, - 4824, - 4825, - 4826, - 4827, - 4828, - 4829, - 4830, - 4831, - 4832, - 4833, - 4834, - 4835, - 4836, - 4837, - 4838, - 4839, - 4840, - 4841, - 4842, - 4843, - 4844, - 4845, - 4846, - 4847, - 4848, - 4849, - 4850, - 4851, - 4852, - 4853, - 4854, - 4855, - 4856, - 4857, - 4858, - 4859, - 4860, - 4861, - 4862, - 4863, - 4864, - 4865, - 4866, - 4867, - 4868, - 4869, - 4870, - 4871, - 4872, - 4873, - 4874, - 4875, - 4876, - 4877, - 4878, - 4879, - 4880, - 4882, - 4883, - 4884, - 4885, - 4888, - 4889, - 4890, - 4891, - 4892, - 4893, - 4894, - 4895, - 4896, - 4897, - 4898, - 4899, - 4900, - 4901, - 4902, - 4903, - 4904, - 4905, - 4906, - 4907, - 4908, - 4909, - 4910, - 4911, - 4912, - 4913, - 4914, - 4915, - 4916, - 4917, - 4918, - 4919, - 4920, - 4921, - 4922, - 4923, - 4924, - 4925, - 4926, - 4927, - 4928, - 4929, - 4930, - 4931, - 4932, - 4933, - 4934, - 4935, - 4936, - 4937, - 4938, - 4939, - 4940, - 4941, - 4942, - 4943, - 4944, - 4945, - 4946, - 4947, - 4948, - 4949, - 4950, - 4951, - 4952, - 4953, - 4954, - 4992, - 4993, - 4994, - 4995, - 4996, - 4997, - 4998, - 4999, - 5000, - 5001, - 5002, - 5003, - 5004, - 5005, - 5006, - 5007, - 5024, - 5025, - 5026, - 5027, - 5028, - 5029, - 5030, - 5031, - 5032, - 5033, - 5034, - 5035, - 5036, - 5037, - 5038, - 5039, - 5040, - 5041, - 5042, - 5043, - 5044, - 5045, - 5046, - 5047, - 5048, - 5049, - 5050, - 5051, - 5052, - 5053, - 5054, - 5055, - 5056, - 5057, - 5058, - 5059, - 5060, - 5061, - 5062, - 5063, - 5064, - 5065, - 5066, - 5067, - 5068, - 5069, - 5070, - 5071, - 5072, - 5073, - 5074, - 5075, - 5076, - 5077, - 5078, - 5079, - 5080, - 5081, - 5082, - 5083, - 5084, - 5085, - 5086, - 5087, - 5088, - 5089, - 5090, - 5091, - 5092, - 5093, - 5094, - 5095, - 5096, - 5097, - 5098, - 5099, - 5100, - 5101, - 5102, - 5103, - 5104, - 5105, - 5106, - 5107, - 5108, - 5121, - 5122, - 5123, - 5124, - 5125, - 5126, - 5127, - 5128, - 5129, - 5130, - 5131, - 5132, - 5133, - 5134, - 5135, - 5136, - 5137, - 5138, - 5139, - 5140, - 5141, - 5142, - 5143, - 5144, - 5145, - 5146, - 5147, - 5148, - 5149, - 5150, - 5151, - 5152, - 5153, - 5154, - 5155, - 5156, - 5157, - 5158, - 5159, - 5160, - 5161, - 5162, - 5163, - 5164, - 5165, - 5166, - 5167, - 5168, - 5169, - 5170, - 5171, - 5172, - 5173, - 5174, - 5175, - 5176, - 5177, - 5178, - 5179, - 5180, - 5181, - 5182, - 5183, - 5184, - 5185, - 5186, - 5187, - 5188, - 5189, - 5190, - 5191, - 5192, - 5193, - 5194, - 5195, - 5196, - 5197, - 5198, - 5199, - 5200, - 5201, - 5202, - 5203, - 5204, - 5205, - 5206, - 5207, - 5208, - 5209, - 5210, - 5211, - 5212, - 5213, - 5214, - 5215, - 5216, - 5217, - 5218, - 5219, - 5220, - 5221, - 5222, - 5223, - 5224, - 5225, - 5226, - 5227, - 5228, - 5229, - 5230, - 5231, - 5232, - 5233, - 5234, - 5235, - 5236, - 5237, - 5238, - 5239, - 5240, - 5241, - 5242, - 5243, - 5244, - 5245, - 5246, - 5247, - 5248, - 5249, - 5250, - 5251, - 5252, - 5253, - 5254, - 5255, - 5256, - 5257, - 5258, - 5259, - 5260, - 5261, - 5262, - 5263, - 5264, - 5265, - 5266, - 5267, - 5268, - 5269, - 5270, - 5271, - 5272, - 5273, - 5274, - 5275, - 5276, - 5277, - 5278, - 5279, - 5280, - 5281, - 5282, - 5283, - 5284, - 5285, - 5286, - 5287, - 5288, - 5289, - 5290, - 5291, - 5292, - 5293, - 5294, - 5295, - 5296, - 5297, - 5298, - 5299, - 5300, - 5301, - 5302, - 5303, - 5304, - 5305, - 5306, - 5307, - 5308, - 5309, - 5310, - 5311, - 5312, - 5313, - 5314, - 5315, - 5316, - 5317, - 5318, - 5319, - 5320, - 5321, - 5322, - 5323, - 5324, - 5325, - 5326, - 5327, - 5328, - 5329, - 5330, - 5331, - 5332, - 5333, - 5334, - 5335, - 5336, - 5337, - 5338, - 5339, - 5340, - 5341, - 5342, - 5343, - 5344, - 5345, - 5346, - 5347, - 5348, - 5349, - 5350, - 5351, - 5352, - 5353, - 5354, - 5355, - 5356, - 5357, - 5358, - 5359, - 5360, - 5361, - 5362, - 5363, - 5364, - 5365, - 5366, - 5367, - 5368, - 5369, - 5370, - 5371, - 5372, - 5373, - 5374, - 5375, - 5376, - 5377, - 5378, - 5379, - 5380, - 5381, - 5382, - 5383, - 5384, - 5385, - 5386, - 5387, - 5388, - 5389, - 5390, - 5391, - 5392, - 5393, - 5394, - 5395, - 5396, - 5397, - 5398, - 5399, - 5400, - 5401, - 5402, - 5403, - 5404, - 5405, - 5406, - 5407, - 5408, - 5409, - 5410, - 5411, - 5412, - 5413, - 5414, - 5415, - 5416, - 5417, - 5418, - 5419, - 5420, - 5421, - 5422, - 5423, - 5424, - 5425, - 5426, - 5427, - 5428, - 5429, - 5430, - 5431, - 5432, - 5433, - 5434, - 5435, - 5436, - 5437, - 5438, - 5439, - 5440, - 5441, - 5442, - 5443, - 5444, - 5445, - 5446, - 5447, - 5448, - 5449, - 5450, - 5451, - 5452, - 5453, - 5454, - 5455, - 5456, - 5457, - 5458, - 5459, - 5460, - 5461, - 5462, - 5463, - 5464, - 5465, - 5466, - 5467, - 5468, - 5469, - 5470, - 5471, - 5472, - 5473, - 5474, - 5475, - 5476, - 5477, - 5478, - 5479, - 5480, - 5481, - 5482, - 5483, - 5484, - 5485, - 5486, - 5487, - 5488, - 5489, - 5490, - 5491, - 5492, - 5493, - 5494, - 5495, - 5496, - 5497, - 5498, - 5499, - 5500, - 5501, - 5502, - 5503, - 5504, - 5505, - 5506, - 5507, - 5508, - 5509, - 5510, - 5511, - 5512, - 5513, - 5514, - 5515, - 5516, - 5517, - 5518, - 5519, - 5520, - 5521, - 5522, - 5523, - 5524, - 5525, - 5526, - 5527, - 5528, - 5529, - 5530, - 5531, - 5532, - 5533, - 5534, - 5535, - 5536, - 5537, - 5538, - 5539, - 5540, - 5541, - 5542, - 5543, - 5544, - 5545, - 5546, - 5547, - 5548, - 5549, - 5550, - 5551, - 5552, - 5553, - 5554, - 5555, - 5556, - 5557, - 5558, - 5559, - 5560, - 5561, - 5562, - 5563, - 5564, - 5565, - 5566, - 5567, - 5568, - 5569, - 5570, - 5571, - 5572, - 5573, - 5574, - 5575, - 5576, - 5577, - 5578, - 5579, - 5580, - 5581, - 5582, - 5583, - 5584, - 5585, - 5586, - 5587, - 5588, - 5589, - 5590, - 5591, - 5592, - 5593, - 5594, - 5595, - 5596, - 5597, - 5598, - 5599, - 5600, - 5601, - 5602, - 5603, - 5604, - 5605, - 5606, - 5607, - 5608, - 5609, - 5610, - 5611, - 5612, - 5613, - 5614, - 5615, - 5616, - 5617, - 5618, - 5619, - 5620, - 5621, - 5622, - 5623, - 5624, - 5625, - 5626, - 5627, - 5628, - 5629, - 5630, - 5631, - 5632, - 5633, - 5634, - 5635, - 5636, - 5637, - 5638, - 5639, - 5640, - 5641, - 5642, - 5643, - 5644, - 5645, - 5646, - 5647, - 5648, - 5649, - 5650, - 5651, - 5652, - 5653, - 5654, - 5655, - 5656, - 5657, - 5658, - 5659, - 5660, - 5661, - 5662, - 5663, - 5664, - 5665, - 5666, - 5667, - 5668, - 5669, - 5670, - 5671, - 5672, - 5673, - 5674, - 5675, - 5676, - 5677, - 5678, - 5679, - 5680, - 5681, - 5682, - 5683, - 5684, - 5685, - 5686, - 5687, - 5688, - 5689, - 5690, - 5691, - 5692, - 5693, - 5694, - 5695, - 5696, - 5697, - 5698, - 5699, - 5700, - 5701, - 5702, - 5703, - 5704, - 5705, - 5706, - 5707, - 5708, - 5709, - 5710, - 5711, - 5712, - 5713, - 5714, - 5715, - 5716, - 5717, - 5718, - 5719, - 5720, - 5721, - 5722, - 5723, - 5724, - 5725, - 5726, - 5727, - 5728, - 5729, - 5730, - 5731, - 5732, - 5733, - 5734, - 5735, - 5736, - 5737, - 5738, - 5739, - 5740, - 5743, - 5744, - 5745, - 5746, - 5747, - 5748, - 5749, - 5750, - 5751, - 5752, - 5753, - 5754, - 5755, - 5756, - 5757, - 5758, - 5759, - 5761, - 5762, - 5763, - 5764, - 5765, - 5766, - 5767, - 5768, - 5769, - 5770, - 5771, - 5772, - 5773, - 5774, - 5775, - 5776, - 5777, - 5778, - 5779, - 5780, - 5781, - 5782, - 5783, - 5784, - 5785, - 5786, - 5792, - 5793, - 5794, - 5795, - 5796, - 5797, - 5798, - 5799, - 5800, - 5801, - 5802, - 5803, - 5804, - 5805, - 5806, - 5807, - 5808, - 5809, - 5810, - 5811, - 5812, - 5813, - 5814, - 5815, - 5816, - 5817, - 5818, - 5819, - 5820, - 5821, - 5822, - 5823, - 5824, - 5825, - 5826, - 5827, - 5828, - 5829, - 5830, - 5831, - 5832, - 5833, - 5834, - 5835, - 5836, - 5837, - 5838, - 5839, - 5840, - 5841, - 5842, - 5843, - 5844, - 5845, - 5846, - 5847, - 5848, - 5849, - 5850, - 5851, - 5852, - 5853, - 5854, - 5855, - 5856, - 5857, - 5858, - 5859, - 5860, - 5861, - 5862, - 5863, - 5864, - 5865, - 5866, - 5870, - 5871, - 5872, - 5888, - 5889, - 5890, - 5891, - 5892, - 5893, - 5894, - 5895, - 5896, - 5897, - 5898, - 5899, - 5900, - 5902, - 5903, - 5904, - 5905, - 5920, - 5921, - 5922, - 5923, - 5924, - 5925, - 5926, - 5927, - 5928, - 5929, - 5930, - 5931, - 5932, - 5933, - 5934, - 5935, - 5936, - 5937, - 5952, - 5953, - 5954, - 5955, - 5956, - 5957, - 5958, - 5959, - 5960, - 5961, - 5962, - 5963, - 5964, - 5965, - 5966, - 5967, - 5968, - 5969, - 5984, - 5985, - 5986, - 5987, - 5988, - 5989, - 5990, - 5991, - 5992, - 5993, - 5994, - 5995, - 5996, - 5998, - 5999, - 6000, - 6016, - 6017, - 6018, - 6019, - 6020, - 6021, - 6022, - 6023, - 6024, - 6025, - 6026, - 6027, - 6028, - 6029, - 6030, - 6031, - 6032, - 6033, - 6034, - 6035, - 6036, - 6037, - 6038, - 6039, - 6040, - 6041, - 6042, - 6043, - 6044, - 6045, - 6046, - 6047, - 6048, - 6049, - 6050, - 6051, - 6052, - 6053, - 6054, - 6055, - 6056, - 6057, - 6058, - 6059, - 6060, - 6061, - 6062, - 6063, - 6064, - 6065, - 6066, - 6067, - 6103, - 6108, - 6176, - 6177, - 6178, - 6179, - 6180, - 6181, - 6182, - 6183, - 6184, - 6185, - 6186, - 6187, - 6188, - 6189, - 6190, - 6191, - 6192, - 6193, - 6194, - 6195, - 6196, - 6197, - 6198, - 6199, - 6200, - 6201, - 6202, - 6203, - 6204, - 6205, - 6206, - 6207, - 6208, - 6209, - 6210, - 6211, - 6212, - 6213, - 6214, - 6215, - 6216, - 6217, - 6218, - 6219, - 6220, - 6221, - 6222, - 6223, - 6224, - 6225, - 6226, - 6227, - 6228, - 6229, - 6230, - 6231, - 6232, - 6233, - 6234, - 6235, - 6236, - 6237, - 6238, - 6239, - 6240, - 6241, - 6242, - 6243, - 6244, - 6245, - 6246, - 6247, - 6248, - 6249, - 6250, - 6251, - 6252, - 6253, - 6254, - 6255, - 6256, - 6257, - 6258, - 6259, - 6260, - 6261, - 6262, - 6263, - 6272, - 6273, - 6274, - 6275, - 6276, - 6277, - 6278, - 6279, - 6280, - 6281, - 6282, - 6283, - 6284, - 6285, - 6286, - 6287, - 6288, - 6289, - 6290, - 6291, - 6292, - 6293, - 6294, - 6295, - 6296, - 6297, - 6298, - 6299, - 6300, - 6301, - 6302, - 6303, - 6304, - 6305, - 6306, - 6307, - 6308, - 6309, - 6310, - 6311, - 6312, - 6314, - 6320, - 6321, - 6322, - 6323, - 6324, - 6325, - 6326, - 6327, - 6328, - 6329, - 6330, - 6331, - 6332, - 6333, - 6334, - 6335, - 6336, - 6337, - 6338, - 6339, - 6340, - 6341, - 6342, - 6343, - 6344, - 6345, - 6346, - 6347, - 6348, - 6349, - 6350, - 6351, - 6352, - 6353, - 6354, - 6355, - 6356, - 6357, - 6358, - 6359, - 6360, - 6361, - 6362, - 6363, - 6364, - 6365, - 6366, - 6367, - 6368, - 6369, - 6370, - 6371, - 6372, - 6373, - 6374, - 6375, - 6376, - 6377, - 6378, - 6379, - 6380, - 6381, - 6382, - 6383, - 6384, - 6385, - 6386, - 6387, - 6388, - 6389, - 6400, - 6401, - 6402, - 6403, - 6404, - 6405, - 6406, - 6407, - 6408, - 6409, - 6410, - 6411, - 6412, - 6413, - 6414, - 6415, - 6416, - 6417, - 6418, - 6419, - 6420, - 6421, - 6422, - 6423, - 6424, - 6425, - 6426, - 6427, - 6428, - 6480, - 6481, - 6482, - 6483, - 6484, - 6485, - 6486, - 6487, - 6488, - 6489, - 6490, - 6491, - 6492, - 6493, - 6494, - 6495, - 6496, - 6497, - 6498, - 6499, - 6500, - 6501, - 6502, - 6503, - 6504, - 6505, - 6506, - 6507, - 6508, - 6509, - 6512, - 6513, - 6514, - 6515, - 6516, - 6528, - 6529, - 6530, - 6531, - 6532, - 6533, - 6534, - 6535, - 6536, - 6537, - 6538, - 6539, - 6540, - 6541, - 6542, - 6543, - 6544, - 6545, - 6546, - 6547, - 6548, - 6549, - 6550, - 6551, - 6552, - 6553, - 6554, - 6555, - 6556, - 6557, - 6558, - 6559, - 6560, - 6561, - 6562, - 6563, - 6564, - 6565, - 6566, - 6567, - 6568, - 6569, - 6570, - 6571, - 6593, - 6594, - 6595, - 6596, - 6597, - 6598, - 6599, - 6656, - 6657, - 6658, - 6659, - 6660, - 6661, - 6662, - 6663, - 6664, - 6665, - 6666, - 6667, - 6668, - 6669, - 6670, - 6671, - 6672, - 6673, - 6674, - 6675, - 6676, - 6677, - 6678, - 6688, - 6689, - 6690, - 6691, - 6692, - 6693, - 6694, - 6695, - 6696, - 6697, - 6698, - 6699, - 6700, - 6701, - 6702, - 6703, - 6704, - 6705, - 6706, - 6707, - 6708, - 6709, - 6710, - 6711, - 6712, - 6713, - 6714, - 6715, - 6716, - 6717, - 6718, - 6719, - 6720, - 6721, - 6722, - 6723, - 6724, - 6725, - 6726, - 6727, - 6728, - 6729, - 6730, - 6731, - 6732, - 6733, - 6734, - 6735, - 6736, - 6737, - 6738, - 6739, - 6740, - 6823, - 6917, - 6918, - 6919, - 6920, - 6921, - 6922, - 6923, - 6924, - 6925, - 6926, - 6927, - 6928, - 6929, - 6930, - 6931, - 6932, - 6933, - 6934, - 6935, - 6936, - 6937, - 6938, - 6939, - 6940, - 6941, - 6942, - 6943, - 6944, - 6945, - 6946, - 6947, - 6948, - 6949, - 6950, - 6951, - 6952, - 6953, - 6954, - 6955, - 6956, - 6957, - 6958, - 6959, - 6960, - 6961, - 6962, - 6963, - 6981, - 6982, - 6983, - 6984, - 6985, - 6986, - 6987, - 7043, - 7044, - 7045, - 7046, - 7047, - 7048, - 7049, - 7050, - 7051, - 7052, - 7053, - 7054, - 7055, - 7056, - 7057, - 7058, - 7059, - 7060, - 7061, - 7062, - 7063, - 7064, - 7065, - 7066, - 7067, - 7068, - 7069, - 7070, - 7071, - 7072, - 7086, - 7087, - 7098, - 7099, - 7100, - 7101, - 7102, - 7103, - 7104, - 7105, - 7106, - 7107, - 7108, - 7109, - 7110, - 7111, - 7112, - 7113, - 7114, - 7115, - 7116, - 7117, - 7118, - 7119, - 7120, - 7121, - 7122, - 7123, - 7124, - 7125, - 7126, - 7127, - 7128, - 7129, - 7130, - 7131, - 7132, - 7133, - 7134, - 7135, - 7136, - 7137, - 7138, - 7139, - 7140, - 7141, - 7168, - 7169, - 7170, - 7171, - 7172, - 7173, - 7174, - 7175, - 7176, - 7177, - 7178, - 7179, - 7180, - 7181, - 7182, - 7183, - 7184, - 7185, - 7186, - 7187, - 7188, - 7189, - 7190, - 7191, - 7192, - 7193, - 7194, - 7195, - 7196, - 7197, - 7198, - 7199, - 7200, - 7201, - 7202, - 7203, - 7245, - 7246, - 7247, - 7258, - 7259, - 7260, - 7261, - 7262, - 7263, - 7264, - 7265, - 7266, - 7267, - 7268, - 7269, - 7270, - 7271, - 7272, - 7273, - 7274, - 7275, - 7276, - 7277, - 7278, - 7279, - 7280, - 7281, - 7282, - 7283, - 7284, - 7285, - 7286, - 7287, - 7288, - 7289, - 7290, - 7291, - 7292, - 7293, - 7401, - 7402, - 7403, - 7404, - 7406, - 7407, - 7408, - 7409, - 7413, - 7414, - 7424, - 7425, - 7426, - 7427, - 7428, - 7429, - 7430, - 7431, - 7432, - 7433, - 7434, - 7435, - 7436, - 7437, - 7438, - 7439, - 7440, - 7441, - 7442, - 7443, - 7444, - 7445, - 7446, - 7447, - 7448, - 7449, - 7450, - 7451, - 7452, - 7453, - 7454, - 7455, - 7456, - 7457, - 7458, - 7459, - 7460, - 7461, - 7462, - 7463, - 7464, - 7465, - 7466, - 7467, - 7468, - 7469, - 7470, - 7471, - 7472, - 7473, - 7474, - 7475, - 7476, - 7477, - 7478, - 7479, - 7480, - 7481, - 7482, - 7483, - 7484, - 7485, - 7486, - 7487, - 7488, - 7489, - 7490, - 7491, - 7492, - 7493, - 7494, - 7495, - 7496, - 7497, - 7498, - 7499, - 7500, - 7501, - 7502, - 7503, - 7504, - 7505, - 7506, - 7507, - 7508, - 7509, - 7510, - 7511, - 7512, - 7513, - 7514, - 7515, - 7516, - 7517, - 7518, - 7519, - 7520, - 7521, - 7522, - 7523, - 7524, - 7525, - 7526, - 7527, - 7528, - 7529, - 7530, - 7531, - 7532, - 7533, - 7534, - 7535, - 7536, - 7537, - 7538, - 7539, - 7540, - 7541, - 7542, - 7543, - 7544, - 7545, - 7546, - 7547, - 7548, - 7549, - 7550, - 7551, - 7552, - 7553, - 7554, - 7555, - 7556, - 7557, - 7558, - 7559, - 7560, - 7561, - 7562, - 7563, - 7564, - 7565, - 7566, - 7567, - 7568, - 7569, - 7570, - 7571, - 7572, - 7573, - 7574, - 7575, - 7576, - 7577, - 7578, - 7579, - 7580, - 7581, - 7582, - 7583, - 7584, - 7585, - 7586, - 7587, - 7588, - 7589, - 7590, - 7591, - 7592, - 7593, - 7594, - 7595, - 7596, - 7597, - 7598, - 7599, - 7600, - 7601, - 7602, - 7603, - 7604, - 7605, - 7606, - 7607, - 7608, - 7609, - 7610, - 7611, - 7612, - 7613, - 7614, - 7615, - 7680, - 7681, - 7682, - 7683, - 7684, - 7685, - 7686, - 7687, - 7688, - 7689, - 7690, - 7691, - 7692, - 7693, - 7694, - 7695, - 7696, - 7697, - 7698, - 7699, - 7700, - 7701, - 7702, - 7703, - 7704, - 7705, - 7706, - 7707, - 7708, - 7709, - 7710, - 7711, - 7712, - 7713, - 7714, - 7715, - 7716, - 7717, - 7718, - 7719, - 7720, - 7721, - 7722, - 7723, - 7724, - 7725, - 7726, - 7727, - 7728, - 7729, - 7730, - 7731, - 7732, - 7733, - 7734, - 7735, - 7736, - 7737, - 7738, - 7739, - 7740, - 7741, - 7742, - 7743, - 7744, - 7745, - 7746, - 7747, - 7748, - 7749, - 7750, - 7751, - 7752, - 7753, - 7754, - 7755, - 7756, - 7757, - 7758, - 7759, - 7760, - 7761, - 7762, - 7763, - 7764, - 7765, - 7766, - 7767, - 7768, - 7769, - 7770, - 7771, - 7772, - 7773, - 7774, - 7775, - 7776, - 7777, - 7778, - 7779, - 7780, - 7781, - 7782, - 7783, - 7784, - 7785, - 7786, - 7787, - 7788, - 7789, - 7790, - 7791, - 7792, - 7793, - 7794, - 7795, - 7796, - 7797, - 7798, - 7799, - 7800, - 7801, - 7802, - 7803, - 7804, - 7805, - 7806, - 7807, - 7808, - 7809, - 7810, - 7811, - 7812, - 7813, - 7814, - 7815, - 7816, - 7817, - 7818, - 7819, - 7820, - 7821, - 7822, - 7823, - 7824, - 7825, - 7826, - 7827, - 7828, - 7829, - 7830, - 7831, - 7832, - 7833, - 7834, - 7835, - 7836, - 7837, - 7838, - 7839, - 7840, - 7841, - 7842, - 7843, - 7844, - 7845, - 7846, - 7847, - 7848, - 7849, - 7850, - 7851, - 7852, - 7853, - 7854, - 7855, - 7856, - 7857, - 7858, - 7859, - 7860, - 7861, - 7862, - 7863, - 7864, - 7865, - 7866, - 7867, - 7868, - 7869, - 7870, - 7871, - 7872, - 7873, - 7874, - 7875, - 7876, - 7877, - 7878, - 7879, - 7880, - 7881, - 7882, - 7883, - 7884, - 7885, - 7886, - 7887, - 7888, - 7889, - 7890, - 7891, - 7892, - 7893, - 7894, - 7895, - 7896, - 7897, - 7898, - 7899, - 7900, - 7901, - 7902, - 7903, - 7904, - 7905, - 7906, - 7907, - 7908, - 7909, - 7910, - 7911, - 7912, - 7913, - 7914, - 7915, - 7916, - 7917, - 7918, - 7919, - 7920, - 7921, - 7922, - 7923, - 7924, - 7925, - 7926, - 7927, - 7928, - 7929, - 7930, - 7931, - 7932, - 7933, - 7934, - 7935, - 7936, - 7937, - 7938, - 7939, - 7940, - 7941, - 7942, - 7943, - 7944, - 7945, - 7946, - 7947, - 7948, - 7949, - 7950, - 7951, - 7952, - 7953, - 7954, - 7955, - 7956, - 7957, - 7960, - 7961, - 7962, - 7963, - 7964, - 7965, - 7968, - 7969, - 7970, - 7971, - 7972, - 7973, - 7974, - 7975, - 7976, - 7977, - 7978, - 7979, - 7980, - 7981, - 7982, - 7983, - 7984, - 7985, - 7986, - 7987, - 7988, - 7989, - 7990, - 7991, - 7992, - 7993, - 7994, - 7995, - 7996, - 7997, - 7998, - 7999, - 8000, - 8001, - 8002, - 8003, - 8004, - 8005, - 8008, - 8009, - 8010, - 8011, - 8012, - 8013, - 8016, - 8017, - 8018, - 8019, - 8020, - 8021, - 8022, - 8023, - 8025, - 8027, - 8029, - 8031, - 8032, - 8033, - 8034, - 8035, - 8036, - 8037, - 8038, - 8039, - 8040, - 8041, - 8042, - 8043, - 8044, - 8045, - 8046, - 8047, - 8048, - 8049, - 8050, - 8051, - 8052, - 8053, - 8054, - 8055, - 8056, - 8057, - 8058, - 8059, - 8060, - 8061, - 8064, - 8065, - 8066, - 8067, - 8068, - 8069, - 8070, - 8071, - 8072, - 8073, - 8074, - 8075, - 8076, - 8077, - 8078, - 8079, - 8080, - 8081, - 8082, - 8083, - 8084, - 8085, - 8086, - 8087, - 8088, - 8089, - 8090, - 8091, - 8092, - 8093, - 8094, - 8095, - 8096, - 8097, - 8098, - 8099, - 8100, - 8101, - 8102, - 8103, - 8104, - 8105, - 8106, - 8107, - 8108, - 8109, - 8110, - 8111, - 8112, - 8113, - 8114, - 8115, - 8116, - 8118, - 8119, - 8120, - 8121, - 8122, - 8123, - 8124, - 8126, - 8130, - 8131, - 8132, - 8134, - 8135, - 8136, - 8137, - 8138, - 8139, - 8140, - 8144, - 8145, - 8146, - 8147, - 8150, - 8151, - 8152, - 8153, - 8154, - 8155, - 8160, - 8161, - 8162, - 8163, - 8164, - 8165, - 8166, - 8167, - 8168, - 8169, - 8170, - 8171, - 8172, - 8178, - 8179, - 8180, - 8182, - 8183, - 8184, - 8185, - 8186, - 8187, - 8188, - 8305, - 8319, - 8336, - 8337, - 8338, - 8339, - 8340, - 8341, - 8342, - 8343, - 8344, - 8345, - 8346, - 8347, - 8348, - 8450, - 8455, - 8458, - 8459, - 8460, - 8461, - 8462, - 8463, - 8464, - 8465, - 8466, - 8467, - 8469, - 8473, - 8474, - 8475, - 8476, - 8477, - 8484, - 8486, - 8488, - 8490, - 8491, - 8492, - 8493, - 8495, - 8496, - 8497, - 8498, - 8499, - 8500, - 8501, - 8502, - 8503, - 8504, - 8505, - 8508, - 8509, - 8510, - 8511, - 8517, - 8518, - 8519, - 8520, - 8521, - 8526, - 8544, - 8545, - 8546, - 8547, - 8548, - 8549, - 8550, - 8551, - 8552, - 8553, - 8554, - 8555, - 8556, - 8557, - 8558, - 8559, - 8560, - 8561, - 8562, - 8563, - 8564, - 8565, - 8566, - 8567, - 8568, - 8569, - 8570, - 8571, - 8572, - 8573, - 8574, - 8575, - 8576, - 8577, - 8578, - 8579, - 8580, - 8581, - 8582, - 8583, - 8584, - 11264, - 11265, - 11266, - 11267, - 11268, - 11269, - 11270, - 11271, - 11272, - 11273, - 11274, - 11275, - 11276, - 11277, - 11278, - 11279, - 11280, - 11281, - 11282, - 11283, - 11284, - 11285, - 11286, - 11287, - 11288, - 11289, - 11290, - 11291, - 11292, - 11293, - 11294, - 11295, - 11296, - 11297, - 11298, - 11299, - 11300, - 11301, - 11302, - 11303, - 11304, - 11305, - 11306, - 11307, - 11308, - 11309, - 11310, - 11312, - 11313, - 11314, - 11315, - 11316, - 11317, - 11318, - 11319, - 11320, - 11321, - 11322, - 11323, - 11324, - 11325, - 11326, - 11327, - 11328, - 11329, - 11330, - 11331, - 11332, - 11333, - 11334, - 11335, - 11336, - 11337, - 11338, - 11339, - 11340, - 11341, - 11342, - 11343, - 11344, - 11345, - 11346, - 11347, - 11348, - 11349, - 11350, - 11351, - 11352, - 11353, - 11354, - 11355, - 11356, - 11357, - 11358, - 11360, - 11361, - 11362, - 11363, - 11364, - 11365, - 11366, - 11367, - 11368, - 11369, - 11370, - 11371, - 11372, - 11373, - 11374, - 11375, - 11376, - 11377, - 11378, - 11379, - 11380, - 11381, - 11382, - 11383, - 11384, - 11385, - 11386, - 11387, - 11388, - 11389, - 11390, - 11391, - 11392, - 11393, - 11394, - 11395, - 11396, - 11397, - 11398, - 11399, - 11400, - 11401, - 11402, - 11403, - 11404, - 11405, - 11406, - 11407, - 11408, - 11409, - 11410, - 11411, - 11412, - 11413, - 11414, - 11415, - 11416, - 11417, - 11418, - 11419, - 11420, - 11421, - 11422, - 11423, - 11424, - 11425, - 11426, - 11427, - 11428, - 11429, - 11430, - 11431, - 11432, - 11433, - 11434, - 11435, - 11436, - 11437, - 11438, - 11439, - 11440, - 11441, - 11442, - 11443, - 11444, - 11445, - 11446, - 11447, - 11448, - 11449, - 11450, - 11451, - 11452, - 11453, - 11454, - 11455, - 11456, - 11457, - 11458, - 11459, - 11460, - 11461, - 11462, - 11463, - 11464, - 11465, - 11466, - 11467, - 11468, - 11469, - 11470, - 11471, - 11472, - 11473, - 11474, - 11475, - 11476, - 11477, - 11478, - 11479, - 11480, - 11481, - 11482, - 11483, - 11484, - 11485, - 11486, - 11487, - 11488, - 11489, - 11490, - 11491, - 11492, - 11499, - 11500, - 11501, - 11502, - 11506, - 11507, - 11520, - 11521, - 11522, - 11523, - 11524, - 11525, - 11526, - 11527, - 11528, - 11529, - 11530, - 11531, - 11532, - 11533, - 11534, - 11535, - 11536, - 11537, - 11538, - 11539, - 11540, - 11541, - 11542, - 11543, - 11544, - 11545, - 11546, - 11547, - 11548, - 11549, - 11550, - 11551, - 11552, - 11553, - 11554, - 11555, - 11556, - 11557, - 11559, - 11565, - 11568, - 11569, - 11570, - 11571, - 11572, - 11573, - 11574, - 11575, - 11576, - 11577, - 11578, - 11579, - 11580, - 11581, - 11582, - 11583, - 11584, - 11585, - 11586, - 11587, - 11588, - 11589, - 11590, - 11591, - 11592, - 11593, - 11594, - 11595, - 11596, - 11597, - 11598, - 11599, - 11600, - 11601, - 11602, - 11603, - 11604, - 11605, - 11606, - 11607, - 11608, - 11609, - 11610, - 11611, - 11612, - 11613, - 11614, - 11615, - 11616, - 11617, - 11618, - 11619, - 11620, - 11621, - 11622, - 11623, - 11631, - 11648, - 11649, - 11650, - 11651, - 11652, - 11653, - 11654, - 11655, - 11656, - 11657, - 11658, - 11659, - 11660, - 11661, - 11662, - 11663, - 11664, - 11665, - 11666, - 11667, - 11668, - 11669, - 11670, - 11680, - 11681, - 11682, - 11683, - 11684, - 11685, - 11686, - 11688, - 11689, - 11690, - 11691, - 11692, - 11693, - 11694, - 11696, - 11697, - 11698, - 11699, - 11700, - 11701, - 11702, - 11704, - 11705, - 11706, - 11707, - 11708, - 11709, - 11710, - 11712, - 11713, - 11714, - 11715, - 11716, - 11717, - 11718, - 11720, - 11721, - 11722, - 11723, - 11724, - 11725, - 11726, - 11728, - 11729, - 11730, - 11731, - 11732, - 11733, - 11734, - 11736, - 11737, - 11738, - 11739, - 11740, - 11741, - 11742, - 11823, - 12293, - 12294, - 12295, - 12321, - 12322, - 12323, - 12324, - 12325, - 12326, - 12327, - 12328, - 12329, - 12337, - 12338, - 12339, - 12340, - 12341, - 12344, - 12345, - 12346, - 12347, - 12348, - 12353, - 12354, - 12355, - 12356, - 12357, - 12358, - 12359, - 12360, - 12361, - 12362, - 12363, - 12364, - 12365, - 12366, - 12367, - 12368, - 12369, - 12370, - 12371, - 12372, - 12373, - 12374, - 12375, - 12376, - 12377, - 12378, - 12379, - 12380, - 12381, - 12382, - 12383, - 12384, - 12385, - 12386, - 12387, - 12388, - 12389, - 12390, - 12391, - 12392, - 12393, - 12394, - 12395, - 12396, - 12397, - 12398, - 12399, - 12400, - 12401, - 12402, - 12403, - 12404, - 12405, - 12406, - 12407, - 12408, - 12409, - 12410, - 12411, - 12412, - 12413, - 12414, - 12415, - 12416, - 12417, - 12418, - 12419, - 12420, - 12421, - 12422, - 12423, - 12424, - 12425, - 12426, - 12427, - 12428, - 12429, - 12430, - 12431, - 12432, - 12433, - 12434, - 12435, - 12436, - 12437, - 12438, - 12445, - 12446, - 12447, - 12449, - 12450, - 12451, - 12452, - 12453, - 12454, - 12455, - 12456, - 12457, - 12458, - 12459, - 12460, - 12461, - 12462, - 12463, - 12464, - 12465, - 12466, - 12467, - 12468, - 12469, - 12470, - 12471, - 12472, - 12473, - 12474, - 12475, - 12476, - 12477, - 12478, - 12479, - 12480, - 12481, - 12482, - 12483, - 12484, - 12485, - 12486, - 12487, - 12488, - 12489, - 12490, - 12491, - 12492, - 12493, - 12494, - 12495, - 12496, - 12497, - 12498, - 12499, - 12500, - 12501, - 12502, - 12503, - 12504, - 12505, - 12506, - 12507, - 12508, - 12509, - 12510, - 12511, - 12512, - 12513, - 12514, - 12515, - 12516, - 12517, - 12518, - 12519, - 12520, - 12521, - 12522, - 12523, - 12524, - 12525, - 12526, - 12527, - 12528, - 12529, - 12530, - 12531, - 12532, - 12533, - 12534, - 12535, - 12536, - 12537, - 12538, - 12540, - 12541, - 12542, - 12543, - 12549, - 12550, - 12551, - 12552, - 12553, - 12554, - 12555, - 12556, - 12557, - 12558, - 12559, - 12560, - 12561, - 12562, - 12563, - 12564, - 12565, - 12566, - 12567, - 12568, - 12569, - 12570, - 12571, - 12572, - 12573, - 12574, - 12575, - 12576, - 12577, - 12578, - 12579, - 12580, - 12581, - 12582, - 12583, - 12584, - 12585, - 12586, - 12587, - 12588, - 12589, - 12593, - 12594, - 12595, - 12596, - 12597, - 12598, - 12599, - 12600, - 12601, - 12602, - 12603, - 12604, - 12605, - 12606, - 12607, - 12608, - 12609, - 12610, - 12611, - 12612, - 12613, - 12614, - 12615, - 12616, - 12617, - 12618, - 12619, - 12620, - 12621, - 12622, - 12623, - 12624, - 12625, - 12626, - 12627, - 12628, - 12629, - 12630, - 12631, - 12632, - 12633, - 12634, - 12635, - 12636, - 12637, - 12638, - 12639, - 12640, - 12641, - 12642, - 12643, - 12644, - 12645, - 12646, - 12647, - 12648, - 12649, - 12650, - 12651, - 12652, - 12653, - 12654, - 12655, - 12656, - 12657, - 12658, - 12659, - 12660, - 12661, - 12662, - 12663, - 12664, - 12665, - 12666, - 12667, - 12668, - 12669, - 12670, - 12671, - 12672, - 12673, - 12674, - 12675, - 12676, - 12677, - 12678, - 12679, - 12680, - 12681, - 12682, - 12683, - 12684, - 12685, - 12686, - 12704, - 12705, - 12706, - 12707, - 12708, - 12709, - 12710, - 12711, - 12712, - 12713, - 12714, - 12715, - 12716, - 12717, - 12718, - 12719, - 12720, - 12721, - 12722, - 12723, - 12724, - 12725, - 12726, - 12727, - 12728, - 12729, - 12730, - 12784, - 12785, - 12786, - 12787, - 12788, - 12789, - 12790, - 12791, - 12792, - 12793, - 12794, - 12795, - 12796, - 12797, - 12798, - 12799, - 13312, - 13313, - 13314, - 13315, - 13316, - 13317, - 13318, - 13319, - 13320, - 13321, - 13322, - 13323, - 13324, - 13325, - 13326, - 13327, - 13328, - 13329, - 13330, - 13331, - 13332, - 13333, - 13334, - 13335, - 13336, - 13337, - 13338, - 13339, - 13340, - 13341, - 13342, - 13343, - 13344, - 13345, - 13346, - 13347, - 13348, - 13349, - 13350, - 13351, - 13352, - 13353, - 13354, - 13355, - 13356, - 13357, - 13358, - 13359, - 13360, - 13361, - 13362, - 13363, - 13364, - 13365, - 13366, - 13367, - 13368, - 13369, - 13370, - 13371, - 13372, - 13373, - 13374, - 13375, - 13376, - 13377, - 13378, - 13379, - 13380, - 13381, - 13382, - 13383, - 13384, - 13385, - 13386, - 13387, - 13388, - 13389, - 13390, - 13391, - 13392, - 13393, - 13394, - 13395, - 13396, - 13397, - 13398, - 13399, - 13400, - 13401, - 13402, - 13403, - 13404, - 13405, - 13406, - 13407, - 13408, - 13409, - 13410, - 13411, - 13412, - 13413, - 13414, - 13415, - 13416, - 13417, - 13418, - 13419, - 13420, - 13421, - 13422, - 13423, - 13424, - 13425, - 13426, - 13427, - 13428, - 13429, - 13430, - 13431, - 13432, - 13433, - 13434, - 13435, - 13436, - 13437, - 13438, - 13439, - 13440, - 13441, - 13442, - 13443, - 13444, - 13445, - 13446, - 13447, - 13448, - 13449, - 13450, - 13451, - 13452, - 13453, - 13454, - 13455, - 13456, - 13457, - 13458, - 13459, - 13460, - 13461, - 13462, - 13463, - 13464, - 13465, - 13466, - 13467, - 13468, - 13469, - 13470, - 13471, - 13472, - 13473, - 13474, - 13475, - 13476, - 13477, - 13478, - 13479, - 13480, - 13481, - 13482, - 13483, - 13484, - 13485, - 13486, - 13487, - 13488, - 13489, - 13490, - 13491, - 13492, - 13493, - 13494, - 13495, - 13496, - 13497, - 13498, - 13499, - 13500, - 13501, - 13502, - 13503, - 13504, - 13505, - 13506, - 13507, - 13508, - 13509, - 13510, - 13511, - 13512, - 13513, - 13514, - 13515, - 13516, - 13517, - 13518, - 13519, - 13520, - 13521, - 13522, - 13523, - 13524, - 13525, - 13526, - 13527, - 13528, - 13529, - 13530, - 13531, - 13532, - 13533, - 13534, - 13535, - 13536, - 13537, - 13538, - 13539, - 13540, - 13541, - 13542, - 13543, - 13544, - 13545, - 13546, - 13547, - 13548, - 13549, - 13550, - 13551, - 13552, - 13553, - 13554, - 13555, - 13556, - 13557, - 13558, - 13559, - 13560, - 13561, - 13562, - 13563, - 13564, - 13565, - 13566, - 13567, - 13568, - 13569, - 13570, - 13571, - 13572, - 13573, - 13574, - 13575, - 13576, - 13577, - 13578, - 13579, - 13580, - 13581, - 13582, - 13583, - 13584, - 13585, - 13586, - 13587, - 13588, - 13589, - 13590, - 13591, - 13592, - 13593, - 13594, - 13595, - 13596, - 13597, - 13598, - 13599, - 13600, - 13601, - 13602, - 13603, - 13604, - 13605, - 13606, - 13607, - 13608, - 13609, - 13610, - 13611, - 13612, - 13613, - 13614, - 13615, - 13616, - 13617, - 13618, - 13619, - 13620, - 13621, - 13622, - 13623, - 13624, - 13625, - 13626, - 13627, - 13628, - 13629, - 13630, - 13631, - 13632, - 13633, - 13634, - 13635, - 13636, - 13637, - 13638, - 13639, - 13640, - 13641, - 13642, - 13643, - 13644, - 13645, - 13646, - 13647, - 13648, - 13649, - 13650, - 13651, - 13652, - 13653, - 13654, - 13655, - 13656, - 13657, - 13658, - 13659, - 13660, - 13661, - 13662, - 13663, - 13664, - 13665, - 13666, - 13667, - 13668, - 13669, - 13670, - 13671, - 13672, - 13673, - 13674, - 13675, - 13676, - 13677, - 13678, - 13679, - 13680, - 13681, - 13682, - 13683, - 13684, - 13685, - 13686, - 13687, - 13688, - 13689, - 13690, - 13691, - 13692, - 13693, - 13694, - 13695, - 13696, - 13697, - 13698, - 13699, - 13700, - 13701, - 13702, - 13703, - 13704, - 13705, - 13706, - 13707, - 13708, - 13709, - 13710, - 13711, - 13712, - 13713, - 13714, - 13715, - 13716, - 13717, - 13718, - 13719, - 13720, - 13721, - 13722, - 13723, - 13724, - 13725, - 13726, - 13727, - 13728, - 13729, - 13730, - 13731, - 13732, - 13733, - 13734, - 13735, - 13736, - 13737, - 13738, - 13739, - 13740, - 13741, - 13742, - 13743, - 13744, - 13745, - 13746, - 13747, - 13748, - 13749, - 13750, - 13751, - 13752, - 13753, - 13754, - 13755, - 13756, - 13757, - 13758, - 13759, - 13760, - 13761, - 13762, - 13763, - 13764, - 13765, - 13766, - 13767, - 13768, - 13769, - 13770, - 13771, - 13772, - 13773, - 13774, - 13775, - 13776, - 13777, - 13778, - 13779, - 13780, - 13781, - 13782, - 13783, - 13784, - 13785, - 13786, - 13787, - 13788, - 13789, - 13790, - 13791, - 13792, - 13793, - 13794, - 13795, - 13796, - 13797, - 13798, - 13799, - 13800, - 13801, - 13802, - 13803, - 13804, - 13805, - 13806, - 13807, - 13808, - 13809, - 13810, - 13811, - 13812, - 13813, - 13814, - 13815, - 13816, - 13817, - 13818, - 13819, - 13820, - 13821, - 13822, - 13823, - 13824, - 13825, - 13826, - 13827, - 13828, - 13829, - 13830, - 13831, - 13832, - 13833, - 13834, - 13835, - 13836, - 13837, - 13838, - 13839, - 13840, - 13841, - 13842, - 13843, - 13844, - 13845, - 13846, - 13847, - 13848, - 13849, - 13850, - 13851, - 13852, - 13853, - 13854, - 13855, - 13856, - 13857, - 13858, - 13859, - 13860, - 13861, - 13862, - 13863, - 13864, - 13865, - 13866, - 13867, - 13868, - 13869, - 13870, - 13871, - 13872, - 13873, - 13874, - 13875, - 13876, - 13877, - 13878, - 13879, - 13880, - 13881, - 13882, - 13883, - 13884, - 13885, - 13886, - 13887, - 13888, - 13889, - 13890, - 13891, - 13892, - 13893, - 13894, - 13895, - 13896, - 13897, - 13898, - 13899, - 13900, - 13901, - 13902, - 13903, - 13904, - 13905, - 13906, - 13907, - 13908, - 13909, - 13910, - 13911, - 13912, - 13913, - 13914, - 13915, - 13916, - 13917, - 13918, - 13919, - 13920, - 13921, - 13922, - 13923, - 13924, - 13925, - 13926, - 13927, - 13928, - 13929, - 13930, - 13931, - 13932, - 13933, - 13934, - 13935, - 13936, - 13937, - 13938, - 13939, - 13940, - 13941, - 13942, - 13943, - 13944, - 13945, - 13946, - 13947, - 13948, - 13949, - 13950, - 13951, - 13952, - 13953, - 13954, - 13955, - 13956, - 13957, - 13958, - 13959, - 13960, - 13961, - 13962, - 13963, - 13964, - 13965, - 13966, - 13967, - 13968, - 13969, - 13970, - 13971, - 13972, - 13973, - 13974, - 13975, - 13976, - 13977, - 13978, - 13979, - 13980, - 13981, - 13982, - 13983, - 13984, - 13985, - 13986, - 13987, - 13988, - 13989, - 13990, - 13991, - 13992, - 13993, - 13994, - 13995, - 13996, - 13997, - 13998, - 13999, - 14000, - 14001, - 14002, - 14003, - 14004, - 14005, - 14006, - 14007, - 14008, - 14009, - 14010, - 14011, - 14012, - 14013, - 14014, - 14015, - 14016, - 14017, - 14018, - 14019, - 14020, - 14021, - 14022, - 14023, - 14024, - 14025, - 14026, - 14027, - 14028, - 14029, - 14030, - 14031, - 14032, - 14033, - 14034, - 14035, - 14036, - 14037, - 14038, - 14039, - 14040, - 14041, - 14042, - 14043, - 14044, - 14045, - 14046, - 14047, - 14048, - 14049, - 14050, - 14051, - 14052, - 14053, - 14054, - 14055, - 14056, - 14057, - 14058, - 14059, - 14060, - 14061, - 14062, - 14063, - 14064, - 14065, - 14066, - 14067, - 14068, - 14069, - 14070, - 14071, - 14072, - 14073, - 14074, - 14075, - 14076, - 14077, - 14078, - 14079, - 14080, - 14081, - 14082, - 14083, - 14084, - 14085, - 14086, - 14087, - 14088, - 14089, - 14090, - 14091, - 14092, - 14093, - 14094, - 14095, - 14096, - 14097, - 14098, - 14099, - 14100, - 14101, - 14102, - 14103, - 14104, - 14105, - 14106, - 14107, - 14108, - 14109, - 14110, - 14111, - 14112, - 14113, - 14114, - 14115, - 14116, - 14117, - 14118, - 14119, - 14120, - 14121, - 14122, - 14123, - 14124, - 14125, - 14126, - 14127, - 14128, - 14129, - 14130, - 14131, - 14132, - 14133, - 14134, - 14135, - 14136, - 14137, - 14138, - 14139, - 14140, - 14141, - 14142, - 14143, - 14144, - 14145, - 14146, - 14147, - 14148, - 14149, - 14150, - 14151, - 14152, - 14153, - 14154, - 14155, - 14156, - 14157, - 14158, - 14159, - 14160, - 14161, - 14162, - 14163, - 14164, - 14165, - 14166, - 14167, - 14168, - 14169, - 14170, - 14171, - 14172, - 14173, - 14174, - 14175, - 14176, - 14177, - 14178, - 14179, - 14180, - 14181, - 14182, - 14183, - 14184, - 14185, - 14186, - 14187, - 14188, - 14189, - 14190, - 14191, - 14192, - 14193, - 14194, - 14195, - 14196, - 14197, - 14198, - 14199, - 14200, - 14201, - 14202, - 14203, - 14204, - 14205, - 14206, - 14207, - 14208, - 14209, - 14210, - 14211, - 14212, - 14213, - 14214, - 14215, - 14216, - 14217, - 14218, - 14219, - 14220, - 14221, - 14222, - 14223, - 14224, - 14225, - 14226, - 14227, - 14228, - 14229, - 14230, - 14231, - 14232, - 14233, - 14234, - 14235, - 14236, - 14237, - 14238, - 14239, - 14240, - 14241, - 14242, - 14243, - 14244, - 14245, - 14246, - 14247, - 14248, - 14249, - 14250, - 14251, - 14252, - 14253, - 14254, - 14255, - 14256, - 14257, - 14258, - 14259, - 14260, - 14261, - 14262, - 14263, - 14264, - 14265, - 14266, - 14267, - 14268, - 14269, - 14270, - 14271, - 14272, - 14273, - 14274, - 14275, - 14276, - 14277, - 14278, - 14279, - 14280, - 14281, - 14282, - 14283, - 14284, - 14285, - 14286, - 14287, - 14288, - 14289, - 14290, - 14291, - 14292, - 14293, - 14294, - 14295, - 14296, - 14297, - 14298, - 14299, - 14300, - 14301, - 14302, - 14303, - 14304, - 14305, - 14306, - 14307, - 14308, - 14309, - 14310, - 14311, - 14312, - 14313, - 14314, - 14315, - 14316, - 14317, - 14318, - 14319, - 14320, - 14321, - 14322, - 14323, - 14324, - 14325, - 14326, - 14327, - 14328, - 14329, - 14330, - 14331, - 14332, - 14333, - 14334, - 14335, - 14336, - 14337, - 14338, - 14339, - 14340, - 14341, - 14342, - 14343, - 14344, - 14345, - 14346, - 14347, - 14348, - 14349, - 14350, - 14351, - 14352, - 14353, - 14354, - 14355, - 14356, - 14357, - 14358, - 14359, - 14360, - 14361, - 14362, - 14363, - 14364, - 14365, - 14366, - 14367, - 14368, - 14369, - 14370, - 14371, - 14372, - 14373, - 14374, - 14375, - 14376, - 14377, - 14378, - 14379, - 14380, - 14381, - 14382, - 14383, - 14384, - 14385, - 14386, - 14387, - 14388, - 14389, - 14390, - 14391, - 14392, - 14393, - 14394, - 14395, - 14396, - 14397, - 14398, - 14399, - 14400, - 14401, - 14402, - 14403, - 14404, - 14405, - 14406, - 14407, - 14408, - 14409, - 14410, - 14411, - 14412, - 14413, - 14414, - 14415, - 14416, - 14417, - 14418, - 14419, - 14420, - 14421, - 14422, - 14423, - 14424, - 14425, - 14426, - 14427, - 14428, - 14429, - 14430, - 14431, - 14432, - 14433, - 14434, - 14435, - 14436, - 14437, - 14438, - 14439, - 14440, - 14441, - 14442, - 14443, - 14444, - 14445, - 14446, - 14447, - 14448, - 14449, - 14450, - 14451, - 14452, - 14453, - 14454, - 14455, - 14456, - 14457, - 14458, - 14459, - 14460, - 14461, - 14462, - 14463, - 14464, - 14465, - 14466, - 14467, - 14468, - 14469, - 14470, - 14471, - 14472, - 14473, - 14474, - 14475, - 14476, - 14477, - 14478, - 14479, - 14480, - 14481, - 14482, - 14483, - 14484, - 14485, - 14486, - 14487, - 14488, - 14489, - 14490, - 14491, - 14492, - 14493, - 14494, - 14495, - 14496, - 14497, - 14498, - 14499, - 14500, - 14501, - 14502, - 14503, - 14504, - 14505, - 14506, - 14507, - 14508, - 14509, - 14510, - 14511, - 14512, - 14513, - 14514, - 14515, - 14516, - 14517, - 14518, - 14519, - 14520, - 14521, - 14522, - 14523, - 14524, - 14525, - 14526, - 14527, - 14528, - 14529, - 14530, - 14531, - 14532, - 14533, - 14534, - 14535, - 14536, - 14537, - 14538, - 14539, - 14540, - 14541, - 14542, - 14543, - 14544, - 14545, - 14546, - 14547, - 14548, - 14549, - 14550, - 14551, - 14552, - 14553, - 14554, - 14555, - 14556, - 14557, - 14558, - 14559, - 14560, - 14561, - 14562, - 14563, - 14564, - 14565, - 14566, - 14567, - 14568, - 14569, - 14570, - 14571, - 14572, - 14573, - 14574, - 14575, - 14576, - 14577, - 14578, - 14579, - 14580, - 14581, - 14582, - 14583, - 14584, - 14585, - 14586, - 14587, - 14588, - 14589, - 14590, - 14591, - 14592, - 14593, - 14594, - 14595, - 14596, - 14597, - 14598, - 14599, - 14600, - 14601, - 14602, - 14603, - 14604, - 14605, - 14606, - 14607, - 14608, - 14609, - 14610, - 14611, - 14612, - 14613, - 14614, - 14615, - 14616, - 14617, - 14618, - 14619, - 14620, - 14621, - 14622, - 14623, - 14624, - 14625, - 14626, - 14627, - 14628, - 14629, - 14630, - 14631, - 14632, - 14633, - 14634, - 14635, - 14636, - 14637, - 14638, - 14639, - 14640, - 14641, - 14642, - 14643, - 14644, - 14645, - 14646, - 14647, - 14648, - 14649, - 14650, - 14651, - 14652, - 14653, - 14654, - 14655, - 14656, - 14657, - 14658, - 14659, - 14660, - 14661, - 14662, - 14663, - 14664, - 14665, - 14666, - 14667, - 14668, - 14669, - 14670, - 14671, - 14672, - 14673, - 14674, - 14675, - 14676, - 14677, - 14678, - 14679, - 14680, - 14681, - 14682, - 14683, - 14684, - 14685, - 14686, - 14687, - 14688, - 14689, - 14690, - 14691, - 14692, - 14693, - 14694, - 14695, - 14696, - 14697, - 14698, - 14699, - 14700, - 14701, - 14702, - 14703, - 14704, - 14705, - 14706, - 14707, - 14708, - 14709, - 14710, - 14711, - 14712, - 14713, - 14714, - 14715, - 14716, - 14717, - 14718, - 14719, - 14720, - 14721, - 14722, - 14723, - 14724, - 14725, - 14726, - 14727, - 14728, - 14729, - 14730, - 14731, - 14732, - 14733, - 14734, - 14735, - 14736, - 14737, - 14738, - 14739, - 14740, - 14741, - 14742, - 14743, - 14744, - 14745, - 14746, - 14747, - 14748, - 14749, - 14750, - 14751, - 14752, - 14753, - 14754, - 14755, - 14756, - 14757, - 14758, - 14759, - 14760, - 14761, - 14762, - 14763, - 14764, - 14765, - 14766, - 14767, - 14768, - 14769, - 14770, - 14771, - 14772, - 14773, - 14774, - 14775, - 14776, - 14777, - 14778, - 14779, - 14780, - 14781, - 14782, - 14783, - 14784, - 14785, - 14786, - 14787, - 14788, - 14789, - 14790, - 14791, - 14792, - 14793, - 14794, - 14795, - 14796, - 14797, - 14798, - 14799, - 14800, - 14801, - 14802, - 14803, - 14804, - 14805, - 14806, - 14807, - 14808, - 14809, - 14810, - 14811, - 14812, - 14813, - 14814, - 14815, - 14816, - 14817, - 14818, - 14819, - 14820, - 14821, - 14822, - 14823, - 14824, - 14825, - 14826, - 14827, - 14828, - 14829, - 14830, - 14831, - 14832, - 14833, - 14834, - 14835, - 14836, - 14837, - 14838, - 14839, - 14840, - 14841, - 14842, - 14843, - 14844, - 14845, - 14846, - 14847, - 14848, - 14849, - 14850, - 14851, - 14852, - 14853, - 14854, - 14855, - 14856, - 14857, - 14858, - 14859, - 14860, - 14861, - 14862, - 14863, - 14864, - 14865, - 14866, - 14867, - 14868, - 14869, - 14870, - 14871, - 14872, - 14873, - 14874, - 14875, - 14876, - 14877, - 14878, - 14879, - 14880, - 14881, - 14882, - 14883, - 14884, - 14885, - 14886, - 14887, - 14888, - 14889, - 14890, - 14891, - 14892, - 14893, - 14894, - 14895, - 14896, - 14897, - 14898, - 14899, - 14900, - 14901, - 14902, - 14903, - 14904, - 14905, - 14906, - 14907, - 14908, - 14909, - 14910, - 14911, - 14912, - 14913, - 14914, - 14915, - 14916, - 14917, - 14918, - 14919, - 14920, - 14921, - 14922, - 14923, - 14924, - 14925, - 14926, - 14927, - 14928, - 14929, - 14930, - 14931, - 14932, - 14933, - 14934, - 14935, - 14936, - 14937, - 14938, - 14939, - 14940, - 14941, - 14942, - 14943, - 14944, - 14945, - 14946, - 14947, - 14948, - 14949, - 14950, - 14951, - 14952, - 14953, - 14954, - 14955, - 14956, - 14957, - 14958, - 14959, - 14960, - 14961, - 14962, - 14963, - 14964, - 14965, - 14966, - 14967, - 14968, - 14969, - 14970, - 14971, - 14972, - 14973, - 14974, - 14975, - 14976, - 14977, - 14978, - 14979, - 14980, - 14981, - 14982, - 14983, - 14984, - 14985, - 14986, - 14987, - 14988, - 14989, - 14990, - 14991, - 14992, - 14993, - 14994, - 14995, - 14996, - 14997, - 14998, - 14999, - 15000, - 15001, - 15002, - 15003, - 15004, - 15005, - 15006, - 15007, - 15008, - 15009, - 15010, - 15011, - 15012, - 15013, - 15014, - 15015, - 15016, - 15017, - 15018, - 15019, - 15020, - 15021, - 15022, - 15023, - 15024, - 15025, - 15026, - 15027, - 15028, - 15029, - 15030, - 15031, - 15032, - 15033, - 15034, - 15035, - 15036, - 15037, - 15038, - 15039, - 15040, - 15041, - 15042, - 15043, - 15044, - 15045, - 15046, - 15047, - 15048, - 15049, - 15050, - 15051, - 15052, - 15053, - 15054, - 15055, - 15056, - 15057, - 15058, - 15059, - 15060, - 15061, - 15062, - 15063, - 15064, - 15065, - 15066, - 15067, - 15068, - 15069, - 15070, - 15071, - 15072, - 15073, - 15074, - 15075, - 15076, - 15077, - 15078, - 15079, - 15080, - 15081, - 15082, - 15083, - 15084, - 15085, - 15086, - 15087, - 15088, - 15089, - 15090, - 15091, - 15092, - 15093, - 15094, - 15095, - 15096, - 15097, - 15098, - 15099, - 15100, - 15101, - 15102, - 15103, - 15104, - 15105, - 15106, - 15107, - 15108, - 15109, - 15110, - 15111, - 15112, - 15113, - 15114, - 15115, - 15116, - 15117, - 15118, - 15119, - 15120, - 15121, - 15122, - 15123, - 15124, - 15125, - 15126, - 15127, - 15128, - 15129, - 15130, - 15131, - 15132, - 15133, - 15134, - 15135, - 15136, - 15137, - 15138, - 15139, - 15140, - 15141, - 15142, - 15143, - 15144, - 15145, - 15146, - 15147, - 15148, - 15149, - 15150, - 15151, - 15152, - 15153, - 15154, - 15155, - 15156, - 15157, - 15158, - 15159, - 15160, - 15161, - 15162, - 15163, - 15164, - 15165, - 15166, - 15167, - 15168, - 15169, - 15170, - 15171, - 15172, - 15173, - 15174, - 15175, - 15176, - 15177, - 15178, - 15179, - 15180, - 15181, - 15182, - 15183, - 15184, - 15185, - 15186, - 15187, - 15188, - 15189, - 15190, - 15191, - 15192, - 15193, - 15194, - 15195, - 15196, - 15197, - 15198, - 15199, - 15200, - 15201, - 15202, - 15203, - 15204, - 15205, - 15206, - 15207, - 15208, - 15209, - 15210, - 15211, - 15212, - 15213, - 15214, - 15215, - 15216, - 15217, - 15218, - 15219, - 15220, - 15221, - 15222, - 15223, - 15224, - 15225, - 15226, - 15227, - 15228, - 15229, - 15230, - 15231, - 15232, - 15233, - 15234, - 15235, - 15236, - 15237, - 15238, - 15239, - 15240, - 15241, - 15242, - 15243, - 15244, - 15245, - 15246, - 15247, - 15248, - 15249, - 15250, - 15251, - 15252, - 15253, - 15254, - 15255, - 15256, - 15257, - 15258, - 15259, - 15260, - 15261, - 15262, - 15263, - 15264, - 15265, - 15266, - 15267, - 15268, - 15269, - 15270, - 15271, - 15272, - 15273, - 15274, - 15275, - 15276, - 15277, - 15278, - 15279, - 15280, - 15281, - 15282, - 15283, - 15284, - 15285, - 15286, - 15287, - 15288, - 15289, - 15290, - 15291, - 15292, - 15293, - 15294, - 15295, - 15296, - 15297, - 15298, - 15299, - 15300, - 15301, - 15302, - 15303, - 15304, - 15305, - 15306, - 15307, - 15308, - 15309, - 15310, - 15311, - 15312, - 15313, - 15314, - 15315, - 15316, - 15317, - 15318, - 15319, - 15320, - 15321, - 15322, - 15323, - 15324, - 15325, - 15326, - 15327, - 15328, - 15329, - 15330, - 15331, - 15332, - 15333, - 15334, - 15335, - 15336, - 15337, - 15338, - 15339, - 15340, - 15341, - 15342, - 15343, - 15344, - 15345, - 15346, - 15347, - 15348, - 15349, - 15350, - 15351, - 15352, - 15353, - 15354, - 15355, - 15356, - 15357, - 15358, - 15359, - 15360, - 15361, - 15362, - 15363, - 15364, - 15365, - 15366, - 15367, - 15368, - 15369, - 15370, - 15371, - 15372, - 15373, - 15374, - 15375, - 15376, - 15377, - 15378, - 15379, - 15380, - 15381, - 15382, - 15383, - 15384, - 15385, - 15386, - 15387, - 15388, - 15389, - 15390, - 15391, - 15392, - 15393, - 15394, - 15395, - 15396, - 15397, - 15398, - 15399, - 15400, - 15401, - 15402, - 15403, - 15404, - 15405, - 15406, - 15407, - 15408, - 15409, - 15410, - 15411, - 15412, - 15413, - 15414, - 15415, - 15416, - 15417, - 15418, - 15419, - 15420, - 15421, - 15422, - 15423, - 15424, - 15425, - 15426, - 15427, - 15428, - 15429, - 15430, - 15431, - 15432, - 15433, - 15434, - 15435, - 15436, - 15437, - 15438, - 15439, - 15440, - 15441, - 15442, - 15443, - 15444, - 15445, - 15446, - 15447, - 15448, - 15449, - 15450, - 15451, - 15452, - 15453, - 15454, - 15455, - 15456, - 15457, - 15458, - 15459, - 15460, - 15461, - 15462, - 15463, - 15464, - 15465, - 15466, - 15467, - 15468, - 15469, - 15470, - 15471, - 15472, - 15473, - 15474, - 15475, - 15476, - 15477, - 15478, - 15479, - 15480, - 15481, - 15482, - 15483, - 15484, - 15485, - 15486, - 15487, - 15488, - 15489, - 15490, - 15491, - 15492, - 15493, - 15494, - 15495, - 15496, - 15497, - 15498, - 15499, - 15500, - 15501, - 15502, - 15503, - 15504, - 15505, - 15506, - 15507, - 15508, - 15509, - 15510, - 15511, - 15512, - 15513, - 15514, - 15515, - 15516, - 15517, - 15518, - 15519, - 15520, - 15521, - 15522, - 15523, - 15524, - 15525, - 15526, - 15527, - 15528, - 15529, - 15530, - 15531, - 15532, - 15533, - 15534, - 15535, - 15536, - 15537, - 15538, - 15539, - 15540, - 15541, - 15542, - 15543, - 15544, - 15545, - 15546, - 15547, - 15548, - 15549, - 15550, - 15551, - 15552, - 15553, - 15554, - 15555, - 15556, - 15557, - 15558, - 15559, - 15560, - 15561, - 15562, - 15563, - 15564, - 15565, - 15566, - 15567, - 15568, - 15569, - 15570, - 15571, - 15572, - 15573, - 15574, - 15575, - 15576, - 15577, - 15578, - 15579, - 15580, - 15581, - 15582, - 15583, - 15584, - 15585, - 15586, - 15587, - 15588, - 15589, - 15590, - 15591, - 15592, - 15593, - 15594, - 15595, - 15596, - 15597, - 15598, - 15599, - 15600, - 15601, - 15602, - 15603, - 15604, - 15605, - 15606, - 15607, - 15608, - 15609, - 15610, - 15611, - 15612, - 15613, - 15614, - 15615, - 15616, - 15617, - 15618, - 15619, - 15620, - 15621, - 15622, - 15623, - 15624, - 15625, - 15626, - 15627, - 15628, - 15629, - 15630, - 15631, - 15632, - 15633, - 15634, - 15635, - 15636, - 15637, - 15638, - 15639, - 15640, - 15641, - 15642, - 15643, - 15644, - 15645, - 15646, - 15647, - 15648, - 15649, - 15650, - 15651, - 15652, - 15653, - 15654, - 15655, - 15656, - 15657, - 15658, - 15659, - 15660, - 15661, - 15662, - 15663, - 15664, - 15665, - 15666, - 15667, - 15668, - 15669, - 15670, - 15671, - 15672, - 15673, - 15674, - 15675, - 15676, - 15677, - 15678, - 15679, - 15680, - 15681, - 15682, - 15683, - 15684, - 15685, - 15686, - 15687, - 15688, - 15689, - 15690, - 15691, - 15692, - 15693, - 15694, - 15695, - 15696, - 15697, - 15698, - 15699, - 15700, - 15701, - 15702, - 15703, - 15704, - 15705, - 15706, - 15707, - 15708, - 15709, - 15710, - 15711, - 15712, - 15713, - 15714, - 15715, - 15716, - 15717, - 15718, - 15719, - 15720, - 15721, - 15722, - 15723, - 15724, - 15725, - 15726, - 15727, - 15728, - 15729, - 15730, - 15731, - 15732, - 15733, - 15734, - 15735, - 15736, - 15737, - 15738, - 15739, - 15740, - 15741, - 15742, - 15743, - 15744, - 15745, - 15746, - 15747, - 15748, - 15749, - 15750, - 15751, - 15752, - 15753, - 15754, - 15755, - 15756, - 15757, - 15758, - 15759, - 15760, - 15761, - 15762, - 15763, - 15764, - 15765, - 15766, - 15767, - 15768, - 15769, - 15770, - 15771, - 15772, - 15773, - 15774, - 15775, - 15776, - 15777, - 15778, - 15779, - 15780, - 15781, - 15782, - 15783, - 15784, - 15785, - 15786, - 15787, - 15788, - 15789, - 15790, - 15791, - 15792, - 15793, - 15794, - 15795, - 15796, - 15797, - 15798, - 15799, - 15800, - 15801, - 15802, - 15803, - 15804, - 15805, - 15806, - 15807, - 15808, - 15809, - 15810, - 15811, - 15812, - 15813, - 15814, - 15815, - 15816, - 15817, - 15818, - 15819, - 15820, - 15821, - 15822, - 15823, - 15824, - 15825, - 15826, - 15827, - 15828, - 15829, - 15830, - 15831, - 15832, - 15833, - 15834, - 15835, - 15836, - 15837, - 15838, - 15839, - 15840, - 15841, - 15842, - 15843, - 15844, - 15845, - 15846, - 15847, - 15848, - 15849, - 15850, - 15851, - 15852, - 15853, - 15854, - 15855, - 15856, - 15857, - 15858, - 15859, - 15860, - 15861, - 15862, - 15863, - 15864, - 15865, - 15866, - 15867, - 15868, - 15869, - 15870, - 15871, - 15872, - 15873, - 15874, - 15875, - 15876, - 15877, - 15878, - 15879, - 15880, - 15881, - 15882, - 15883, - 15884, - 15885, - 15886, - 15887, - 15888, - 15889, - 15890, - 15891, - 15892, - 15893, - 15894, - 15895, - 15896, - 15897, - 15898, - 15899, - 15900, - 15901, - 15902, - 15903, - 15904, - 15905, - 15906, - 15907, - 15908, - 15909, - 15910, - 15911, - 15912, - 15913, - 15914, - 15915, - 15916, - 15917, - 15918, - 15919, - 15920, - 15921, - 15922, - 15923, - 15924, - 15925, - 15926, - 15927, - 15928, - 15929, - 15930, - 15931, - 15932, - 15933, - 15934, - 15935, - 15936, - 15937, - 15938, - 15939, - 15940, - 15941, - 15942, - 15943, - 15944, - 15945, - 15946, - 15947, - 15948, - 15949, - 15950, - 15951, - 15952, - 15953, - 15954, - 15955, - 15956, - 15957, - 15958, - 15959, - 15960, - 15961, - 15962, - 15963, - 15964, - 15965, - 15966, - 15967, - 15968, - 15969, - 15970, - 15971, - 15972, - 15973, - 15974, - 15975, - 15976, - 15977, - 15978, - 15979, - 15980, - 15981, - 15982, - 15983, - 15984, - 15985, - 15986, - 15987, - 15988, - 15989, - 15990, - 15991, - 15992, - 15993, - 15994, - 15995, - 15996, - 15997, - 15998, - 15999, - 16000, - 16001, - 16002, - 16003, - 16004, - 16005, - 16006, - 16007, - 16008, - 16009, - 16010, - 16011, - 16012, - 16013, - 16014, - 16015, - 16016, - 16017, - 16018, - 16019, - 16020, - 16021, - 16022, - 16023, - 16024, - 16025, - 16026, - 16027, - 16028, - 16029, - 16030, - 16031, - 16032, - 16033, - 16034, - 16035, - 16036, - 16037, - 16038, - 16039, - 16040, - 16041, - 16042, - 16043, - 16044, - 16045, - 16046, - 16047, - 16048, - 16049, - 16050, - 16051, - 16052, - 16053, - 16054, - 16055, - 16056, - 16057, - 16058, - 16059, - 16060, - 16061, - 16062, - 16063, - 16064, - 16065, - 16066, - 16067, - 16068, - 16069, - 16070, - 16071, - 16072, - 16073, - 16074, - 16075, - 16076, - 16077, - 16078, - 16079, - 16080, - 16081, - 16082, - 16083, - 16084, - 16085, - 16086, - 16087, - 16088, - 16089, - 16090, - 16091, - 16092, - 16093, - 16094, - 16095, - 16096, - 16097, - 16098, - 16099, - 16100, - 16101, - 16102, - 16103, - 16104, - 16105, - 16106, - 16107, - 16108, - 16109, - 16110, - 16111, - 16112, - 16113, - 16114, - 16115, - 16116, - 16117, - 16118, - 16119, - 16120, - 16121, - 16122, - 16123, - 16124, - 16125, - 16126, - 16127, - 16128, - 16129, - 16130, - 16131, - 16132, - 16133, - 16134, - 16135, - 16136, - 16137, - 16138, - 16139, - 16140, - 16141, - 16142, - 16143, - 16144, - 16145, - 16146, - 16147, - 16148, - 16149, - 16150, - 16151, - 16152, - 16153, - 16154, - 16155, - 16156, - 16157, - 16158, - 16159, - 16160, - 16161, - 16162, - 16163, - 16164, - 16165, - 16166, - 16167, - 16168, - 16169, - 16170, - 16171, - 16172, - 16173, - 16174, - 16175, - 16176, - 16177, - 16178, - 16179, - 16180, - 16181, - 16182, - 16183, - 16184, - 16185, - 16186, - 16187, - 16188, - 16189, - 16190, - 16191, - 16192, - 16193, - 16194, - 16195, - 16196, - 16197, - 16198, - 16199, - 16200, - 16201, - 16202, - 16203, - 16204, - 16205, - 16206, - 16207, - 16208, - 16209, - 16210, - 16211, - 16212, - 16213, - 16214, - 16215, - 16216, - 16217, - 16218, - 16219, - 16220, - 16221, - 16222, - 16223, - 16224, - 16225, - 16226, - 16227, - 16228, - 16229, - 16230, - 16231, - 16232, - 16233, - 16234, - 16235, - 16236, - 16237, - 16238, - 16239, - 16240, - 16241, - 16242, - 16243, - 16244, - 16245, - 16246, - 16247, - 16248, - 16249, - 16250, - 16251, - 16252, - 16253, - 16254, - 16255, - 16256, - 16257, - 16258, - 16259, - 16260, - 16261, - 16262, - 16263, - 16264, - 16265, - 16266, - 16267, - 16268, - 16269, - 16270, - 16271, - 16272, - 16273, - 16274, - 16275, - 16276, - 16277, - 16278, - 16279, - 16280, - 16281, - 16282, - 16283, - 16284, - 16285, - 16286, - 16287, - 16288, - 16289, - 16290, - 16291, - 16292, - 16293, - 16294, - 16295, - 16296, - 16297, - 16298, - 16299, - 16300, - 16301, - 16302, - 16303, - 16304, - 16305, - 16306, - 16307, - 16308, - 16309, - 16310, - 16311, - 16312, - 16313, - 16314, - 16315, - 16316, - 16317, - 16318, - 16319, - 16320, - 16321, - 16322, - 16323, - 16324, - 16325, - 16326, - 16327, - 16328, - 16329, - 16330, - 16331, - 16332, - 16333, - 16334, - 16335, - 16336, - 16337, - 16338, - 16339, - 16340, - 16341, - 16342, - 16343, - 16344, - 16345, - 16346, - 16347, - 16348, - 16349, - 16350, - 16351, - 16352, - 16353, - 16354, - 16355, - 16356, - 16357, - 16358, - 16359, - 16360, - 16361, - 16362, - 16363, - 16364, - 16365, - 16366, - 16367, - 16368, - 16369, - 16370, - 16371, - 16372, - 16373, - 16374, - 16375, - 16376, - 16377, - 16378, - 16379, - 16380, - 16381, - 16382, - 16383, - 16384, - 16385, - 16386, - 16387, - 16388, - 16389, - 16390, - 16391, - 16392, - 16393, - 16394, - 16395, - 16396, - 16397, - 16398, - 16399, - 16400, - 16401, - 16402, - 16403, - 16404, - 16405, - 16406, - 16407, - 16408, - 16409, - 16410, - 16411, - 16412, - 16413, - 16414, - 16415, - 16416, - 16417, - 16418, - 16419, - 16420, - 16421, - 16422, - 16423, - 16424, - 16425, - 16426, - 16427, - 16428, - 16429, - 16430, - 16431, - 16432, - 16433, - 16434, - 16435, - 16436, - 16437, - 16438, - 16439, - 16440, - 16441, - 16442, - 16443, - 16444, - 16445, - 16446, - 16447, - 16448, - 16449, - 16450, - 16451, - 16452, - 16453, - 16454, - 16455, - 16456, - 16457, - 16458, - 16459, - 16460, - 16461, - 16462, - 16463, - 16464, - 16465, - 16466, - 16467, - 16468, - 16469, - 16470, - 16471, - 16472, - 16473, - 16474, - 16475, - 16476, - 16477, - 16478, - 16479, - 16480, - 16481, - 16482, - 16483, - 16484, - 16485, - 16486, - 16487, - 16488, - 16489, - 16490, - 16491, - 16492, - 16493, - 16494, - 16495, - 16496, - 16497, - 16498, - 16499, - 16500, - 16501, - 16502, - 16503, - 16504, - 16505, - 16506, - 16507, - 16508, - 16509, - 16510, - 16511, - 16512, - 16513, - 16514, - 16515, - 16516, - 16517, - 16518, - 16519, - 16520, - 16521, - 16522, - 16523, - 16524, - 16525, - 16526, - 16527, - 16528, - 16529, - 16530, - 16531, - 16532, - 16533, - 16534, - 16535, - 16536, - 16537, - 16538, - 16539, - 16540, - 16541, - 16542, - 16543, - 16544, - 16545, - 16546, - 16547, - 16548, - 16549, - 16550, - 16551, - 16552, - 16553, - 16554, - 16555, - 16556, - 16557, - 16558, - 16559, - 16560, - 16561, - 16562, - 16563, - 16564, - 16565, - 16566, - 16567, - 16568, - 16569, - 16570, - 16571, - 16572, - 16573, - 16574, - 16575, - 16576, - 16577, - 16578, - 16579, - 16580, - 16581, - 16582, - 16583, - 16584, - 16585, - 16586, - 16587, - 16588, - 16589, - 16590, - 16591, - 16592, - 16593, - 16594, - 16595, - 16596, - 16597, - 16598, - 16599, - 16600, - 16601, - 16602, - 16603, - 16604, - 16605, - 16606, - 16607, - 16608, - 16609, - 16610, - 16611, - 16612, - 16613, - 16614, - 16615, - 16616, - 16617, - 16618, - 16619, - 16620, - 16621, - 16622, - 16623, - 16624, - 16625, - 16626, - 16627, - 16628, - 16629, - 16630, - 16631, - 16632, - 16633, - 16634, - 16635, - 16636, - 16637, - 16638, - 16639, - 16640, - 16641, - 16642, - 16643, - 16644, - 16645, - 16646, - 16647, - 16648, - 16649, - 16650, - 16651, - 16652, - 16653, - 16654, - 16655, - 16656, - 16657, - 16658, - 16659, - 16660, - 16661, - 16662, - 16663, - 16664, - 16665, - 16666, - 16667, - 16668, - 16669, - 16670, - 16671, - 16672, - 16673, - 16674, - 16675, - 16676, - 16677, - 16678, - 16679, - 16680, - 16681, - 16682, - 16683, - 16684, - 16685, - 16686, - 16687, - 16688, - 16689, - 16690, - 16691, - 16692, - 16693, - 16694, - 16695, - 16696, - 16697, - 16698, - 16699, - 16700, - 16701, - 16702, - 16703, - 16704, - 16705, - 16706, - 16707, - 16708, - 16709, - 16710, - 16711, - 16712, - 16713, - 16714, - 16715, - 16716, - 16717, - 16718, - 16719, - 16720, - 16721, - 16722, - 16723, - 16724, - 16725, - 16726, - 16727, - 16728, - 16729, - 16730, - 16731, - 16732, - 16733, - 16734, - 16735, - 16736, - 16737, - 16738, - 16739, - 16740, - 16741, - 16742, - 16743, - 16744, - 16745, - 16746, - 16747, - 16748, - 16749, - 16750, - 16751, - 16752, - 16753, - 16754, - 16755, - 16756, - 16757, - 16758, - 16759, - 16760, - 16761, - 16762, - 16763, - 16764, - 16765, - 16766, - 16767, - 16768, - 16769, - 16770, - 16771, - 16772, - 16773, - 16774, - 16775, - 16776, - 16777, - 16778, - 16779, - 16780, - 16781, - 16782, - 16783, - 16784, - 16785, - 16786, - 16787, - 16788, - 16789, - 16790, - 16791, - 16792, - 16793, - 16794, - 16795, - 16796, - 16797, - 16798, - 16799, - 16800, - 16801, - 16802, - 16803, - 16804, - 16805, - 16806, - 16807, - 16808, - 16809, - 16810, - 16811, - 16812, - 16813, - 16814, - 16815, - 16816, - 16817, - 16818, - 16819, - 16820, - 16821, - 16822, - 16823, - 16824, - 16825, - 16826, - 16827, - 16828, - 16829, - 16830, - 16831, - 16832, - 16833, - 16834, - 16835, - 16836, - 16837, - 16838, - 16839, - 16840, - 16841, - 16842, - 16843, - 16844, - 16845, - 16846, - 16847, - 16848, - 16849, - 16850, - 16851, - 16852, - 16853, - 16854, - 16855, - 16856, - 16857, - 16858, - 16859, - 16860, - 16861, - 16862, - 16863, - 16864, - 16865, - 16866, - 16867, - 16868, - 16869, - 16870, - 16871, - 16872, - 16873, - 16874, - 16875, - 16876, - 16877, - 16878, - 16879, - 16880, - 16881, - 16882, - 16883, - 16884, - 16885, - 16886, - 16887, - 16888, - 16889, - 16890, - 16891, - 16892, - 16893, - 16894, - 16895, - 16896, - 16897, - 16898, - 16899, - 16900, - 16901, - 16902, - 16903, - 16904, - 16905, - 16906, - 16907, - 16908, - 16909, - 16910, - 16911, - 16912, - 16913, - 16914, - 16915, - 16916, - 16917, - 16918, - 16919, - 16920, - 16921, - 16922, - 16923, - 16924, - 16925, - 16926, - 16927, - 16928, - 16929, - 16930, - 16931, - 16932, - 16933, - 16934, - 16935, - 16936, - 16937, - 16938, - 16939, - 16940, - 16941, - 16942, - 16943, - 16944, - 16945, - 16946, - 16947, - 16948, - 16949, - 16950, - 16951, - 16952, - 16953, - 16954, - 16955, - 16956, - 16957, - 16958, - 16959, - 16960, - 16961, - 16962, - 16963, - 16964, - 16965, - 16966, - 16967, - 16968, - 16969, - 16970, - 16971, - 16972, - 16973, - 16974, - 16975, - 16976, - 16977, - 16978, - 16979, - 16980, - 16981, - 16982, - 16983, - 16984, - 16985, - 16986, - 16987, - 16988, - 16989, - 16990, - 16991, - 16992, - 16993, - 16994, - 16995, - 16996, - 16997, - 16998, - 16999, - 17000, - 17001, - 17002, - 17003, - 17004, - 17005, - 17006, - 17007, - 17008, - 17009, - 17010, - 17011, - 17012, - 17013, - 17014, - 17015, - 17016, - 17017, - 17018, - 17019, - 17020, - 17021, - 17022, - 17023, - 17024, - 17025, - 17026, - 17027, - 17028, - 17029, - 17030, - 17031, - 17032, - 17033, - 17034, - 17035, - 17036, - 17037, - 17038, - 17039, - 17040, - 17041, - 17042, - 17043, - 17044, - 17045, - 17046, - 17047, - 17048, - 17049, - 17050, - 17051, - 17052, - 17053, - 17054, - 17055, - 17056, - 17057, - 17058, - 17059, - 17060, - 17061, - 17062, - 17063, - 17064, - 17065, - 17066, - 17067, - 17068, - 17069, - 17070, - 17071, - 17072, - 17073, - 17074, - 17075, - 17076, - 17077, - 17078, - 17079, - 17080, - 17081, - 17082, - 17083, - 17084, - 17085, - 17086, - 17087, - 17088, - 17089, - 17090, - 17091, - 17092, - 17093, - 17094, - 17095, - 17096, - 17097, - 17098, - 17099, - 17100, - 17101, - 17102, - 17103, - 17104, - 17105, - 17106, - 17107, - 17108, - 17109, - 17110, - 17111, - 17112, - 17113, - 17114, - 17115, - 17116, - 17117, - 17118, - 17119, - 17120, - 17121, - 17122, - 17123, - 17124, - 17125, - 17126, - 17127, - 17128, - 17129, - 17130, - 17131, - 17132, - 17133, - 17134, - 17135, - 17136, - 17137, - 17138, - 17139, - 17140, - 17141, - 17142, - 17143, - 17144, - 17145, - 17146, - 17147, - 17148, - 17149, - 17150, - 17151, - 17152, - 17153, - 17154, - 17155, - 17156, - 17157, - 17158, - 17159, - 17160, - 17161, - 17162, - 17163, - 17164, - 17165, - 17166, - 17167, - 17168, - 17169, - 17170, - 17171, - 17172, - 17173, - 17174, - 17175, - 17176, - 17177, - 17178, - 17179, - 17180, - 17181, - 17182, - 17183, - 17184, - 17185, - 17186, - 17187, - 17188, - 17189, - 17190, - 17191, - 17192, - 17193, - 17194, - 17195, - 17196, - 17197, - 17198, - 17199, - 17200, - 17201, - 17202, - 17203, - 17204, - 17205, - 17206, - 17207, - 17208, - 17209, - 17210, - 17211, - 17212, - 17213, - 17214, - 17215, - 17216, - 17217, - 17218, - 17219, - 17220, - 17221, - 17222, - 17223, - 17224, - 17225, - 17226, - 17227, - 17228, - 17229, - 17230, - 17231, - 17232, - 17233, - 17234, - 17235, - 17236, - 17237, - 17238, - 17239, - 17240, - 17241, - 17242, - 17243, - 17244, - 17245, - 17246, - 17247, - 17248, - 17249, - 17250, - 17251, - 17252, - 17253, - 17254, - 17255, - 17256, - 17257, - 17258, - 17259, - 17260, - 17261, - 17262, - 17263, - 17264, - 17265, - 17266, - 17267, - 17268, - 17269, - 17270, - 17271, - 17272, - 17273, - 17274, - 17275, - 17276, - 17277, - 17278, - 17279, - 17280, - 17281, - 17282, - 17283, - 17284, - 17285, - 17286, - 17287, - 17288, - 17289, - 17290, - 17291, - 17292, - 17293, - 17294, - 17295, - 17296, - 17297, - 17298, - 17299, - 17300, - 17301, - 17302, - 17303, - 17304, - 17305, - 17306, - 17307, - 17308, - 17309, - 17310, - 17311, - 17312, - 17313, - 17314, - 17315, - 17316, - 17317, - 17318, - 17319, - 17320, - 17321, - 17322, - 17323, - 17324, - 17325, - 17326, - 17327, - 17328, - 17329, - 17330, - 17331, - 17332, - 17333, - 17334, - 17335, - 17336, - 17337, - 17338, - 17339, - 17340, - 17341, - 17342, - 17343, - 17344, - 17345, - 17346, - 17347, - 17348, - 17349, - 17350, - 17351, - 17352, - 17353, - 17354, - 17355, - 17356, - 17357, - 17358, - 17359, - 17360, - 17361, - 17362, - 17363, - 17364, - 17365, - 17366, - 17367, - 17368, - 17369, - 17370, - 17371, - 17372, - 17373, - 17374, - 17375, - 17376, - 17377, - 17378, - 17379, - 17380, - 17381, - 17382, - 17383, - 17384, - 17385, - 17386, - 17387, - 17388, - 17389, - 17390, - 17391, - 17392, - 17393, - 17394, - 17395, - 17396, - 17397, - 17398, - 17399, - 17400, - 17401, - 17402, - 17403, - 17404, - 17405, - 17406, - 17407, - 17408, - 17409, - 17410, - 17411, - 17412, - 17413, - 17414, - 17415, - 17416, - 17417, - 17418, - 17419, - 17420, - 17421, - 17422, - 17423, - 17424, - 17425, - 17426, - 17427, - 17428, - 17429, - 17430, - 17431, - 17432, - 17433, - 17434, - 17435, - 17436, - 17437, - 17438, - 17439, - 17440, - 17441, - 17442, - 17443, - 17444, - 17445, - 17446, - 17447, - 17448, - 17449, - 17450, - 17451, - 17452, - 17453, - 17454, - 17455, - 17456, - 17457, - 17458, - 17459, - 17460, - 17461, - 17462, - 17463, - 17464, - 17465, - 17466, - 17467, - 17468, - 17469, - 17470, - 17471, - 17472, - 17473, - 17474, - 17475, - 17476, - 17477, - 17478, - 17479, - 17480, - 17481, - 17482, - 17483, - 17484, - 17485, - 17486, - 17487, - 17488, - 17489, - 17490, - 17491, - 17492, - 17493, - 17494, - 17495, - 17496, - 17497, - 17498, - 17499, - 17500, - 17501, - 17502, - 17503, - 17504, - 17505, - 17506, - 17507, - 17508, - 17509, - 17510, - 17511, - 17512, - 17513, - 17514, - 17515, - 17516, - 17517, - 17518, - 17519, - 17520, - 17521, - 17522, - 17523, - 17524, - 17525, - 17526, - 17527, - 17528, - 17529, - 17530, - 17531, - 17532, - 17533, - 17534, - 17535, - 17536, - 17537, - 17538, - 17539, - 17540, - 17541, - 17542, - 17543, - 17544, - 17545, - 17546, - 17547, - 17548, - 17549, - 17550, - 17551, - 17552, - 17553, - 17554, - 17555, - 17556, - 17557, - 17558, - 17559, - 17560, - 17561, - 17562, - 17563, - 17564, - 17565, - 17566, - 17567, - 17568, - 17569, - 17570, - 17571, - 17572, - 17573, - 17574, - 17575, - 17576, - 17577, - 17578, - 17579, - 17580, - 17581, - 17582, - 17583, - 17584, - 17585, - 17586, - 17587, - 17588, - 17589, - 17590, - 17591, - 17592, - 17593, - 17594, - 17595, - 17596, - 17597, - 17598, - 17599, - 17600, - 17601, - 17602, - 17603, - 17604, - 17605, - 17606, - 17607, - 17608, - 17609, - 17610, - 17611, - 17612, - 17613, - 17614, - 17615, - 17616, - 17617, - 17618, - 17619, - 17620, - 17621, - 17622, - 17623, - 17624, - 17625, - 17626, - 17627, - 17628, - 17629, - 17630, - 17631, - 17632, - 17633, - 17634, - 17635, - 17636, - 17637, - 17638, - 17639, - 17640, - 17641, - 17642, - 17643, - 17644, - 17645, - 17646, - 17647, - 17648, - 17649, - 17650, - 17651, - 17652, - 17653, - 17654, - 17655, - 17656, - 17657, - 17658, - 17659, - 17660, - 17661, - 17662, - 17663, - 17664, - 17665, - 17666, - 17667, - 17668, - 17669, - 17670, - 17671, - 17672, - 17673, - 17674, - 17675, - 17676, - 17677, - 17678, - 17679, - 17680, - 17681, - 17682, - 17683, - 17684, - 17685, - 17686, - 17687, - 17688, - 17689, - 17690, - 17691, - 17692, - 17693, - 17694, - 17695, - 17696, - 17697, - 17698, - 17699, - 17700, - 17701, - 17702, - 17703, - 17704, - 17705, - 17706, - 17707, - 17708, - 17709, - 17710, - 17711, - 17712, - 17713, - 17714, - 17715, - 17716, - 17717, - 17718, - 17719, - 17720, - 17721, - 17722, - 17723, - 17724, - 17725, - 17726, - 17727, - 17728, - 17729, - 17730, - 17731, - 17732, - 17733, - 17734, - 17735, - 17736, - 17737, - 17738, - 17739, - 17740, - 17741, - 17742, - 17743, - 17744, - 17745, - 17746, - 17747, - 17748, - 17749, - 17750, - 17751, - 17752, - 17753, - 17754, - 17755, - 17756, - 17757, - 17758, - 17759, - 17760, - 17761, - 17762, - 17763, - 17764, - 17765, - 17766, - 17767, - 17768, - 17769, - 17770, - 17771, - 17772, - 17773, - 17774, - 17775, - 17776, - 17777, - 17778, - 17779, - 17780, - 17781, - 17782, - 17783, - 17784, - 17785, - 17786, - 17787, - 17788, - 17789, - 17790, - 17791, - 17792, - 17793, - 17794, - 17795, - 17796, - 17797, - 17798, - 17799, - 17800, - 17801, - 17802, - 17803, - 17804, - 17805, - 17806, - 17807, - 17808, - 17809, - 17810, - 17811, - 17812, - 17813, - 17814, - 17815, - 17816, - 17817, - 17818, - 17819, - 17820, - 17821, - 17822, - 17823, - 17824, - 17825, - 17826, - 17827, - 17828, - 17829, - 17830, - 17831, - 17832, - 17833, - 17834, - 17835, - 17836, - 17837, - 17838, - 17839, - 17840, - 17841, - 17842, - 17843, - 17844, - 17845, - 17846, - 17847, - 17848, - 17849, - 17850, - 17851, - 17852, - 17853, - 17854, - 17855, - 17856, - 17857, - 17858, - 17859, - 17860, - 17861, - 17862, - 17863, - 17864, - 17865, - 17866, - 17867, - 17868, - 17869, - 17870, - 17871, - 17872, - 17873, - 17874, - 17875, - 17876, - 17877, - 17878, - 17879, - 17880, - 17881, - 17882, - 17883, - 17884, - 17885, - 17886, - 17887, - 17888, - 17889, - 17890, - 17891, - 17892, - 17893, - 17894, - 17895, - 17896, - 17897, - 17898, - 17899, - 17900, - 17901, - 17902, - 17903, - 17904, - 17905, - 17906, - 17907, - 17908, - 17909, - 17910, - 17911, - 17912, - 17913, - 17914, - 17915, - 17916, - 17917, - 17918, - 17919, - 17920, - 17921, - 17922, - 17923, - 17924, - 17925, - 17926, - 17927, - 17928, - 17929, - 17930, - 17931, - 17932, - 17933, - 17934, - 17935, - 17936, - 17937, - 17938, - 17939, - 17940, - 17941, - 17942, - 17943, - 17944, - 17945, - 17946, - 17947, - 17948, - 17949, - 17950, - 17951, - 17952, - 17953, - 17954, - 17955, - 17956, - 17957, - 17958, - 17959, - 17960, - 17961, - 17962, - 17963, - 17964, - 17965, - 17966, - 17967, - 17968, - 17969, - 17970, - 17971, - 17972, - 17973, - 17974, - 17975, - 17976, - 17977, - 17978, - 17979, - 17980, - 17981, - 17982, - 17983, - 17984, - 17985, - 17986, - 17987, - 17988, - 17989, - 17990, - 17991, - 17992, - 17993, - 17994, - 17995, - 17996, - 17997, - 17998, - 17999, - 18000, - 18001, - 18002, - 18003, - 18004, - 18005, - 18006, - 18007, - 18008, - 18009, - 18010, - 18011, - 18012, - 18013, - 18014, - 18015, - 18016, - 18017, - 18018, - 18019, - 18020, - 18021, - 18022, - 18023, - 18024, - 18025, - 18026, - 18027, - 18028, - 18029, - 18030, - 18031, - 18032, - 18033, - 18034, - 18035, - 18036, - 18037, - 18038, - 18039, - 18040, - 18041, - 18042, - 18043, - 18044, - 18045, - 18046, - 18047, - 18048, - 18049, - 18050, - 18051, - 18052, - 18053, - 18054, - 18055, - 18056, - 18057, - 18058, - 18059, - 18060, - 18061, - 18062, - 18063, - 18064, - 18065, - 18066, - 18067, - 18068, - 18069, - 18070, - 18071, - 18072, - 18073, - 18074, - 18075, - 18076, - 18077, - 18078, - 18079, - 18080, - 18081, - 18082, - 18083, - 18084, - 18085, - 18086, - 18087, - 18088, - 18089, - 18090, - 18091, - 18092, - 18093, - 18094, - 18095, - 18096, - 18097, - 18098, - 18099, - 18100, - 18101, - 18102, - 18103, - 18104, - 18105, - 18106, - 18107, - 18108, - 18109, - 18110, - 18111, - 18112, - 18113, - 18114, - 18115, - 18116, - 18117, - 18118, - 18119, - 18120, - 18121, - 18122, - 18123, - 18124, - 18125, - 18126, - 18127, - 18128, - 18129, - 18130, - 18131, - 18132, - 18133, - 18134, - 18135, - 18136, - 18137, - 18138, - 18139, - 18140, - 18141, - 18142, - 18143, - 18144, - 18145, - 18146, - 18147, - 18148, - 18149, - 18150, - 18151, - 18152, - 18153, - 18154, - 18155, - 18156, - 18157, - 18158, - 18159, - 18160, - 18161, - 18162, - 18163, - 18164, - 18165, - 18166, - 18167, - 18168, - 18169, - 18170, - 18171, - 18172, - 18173, - 18174, - 18175, - 18176, - 18177, - 18178, - 18179, - 18180, - 18181, - 18182, - 18183, - 18184, - 18185, - 18186, - 18187, - 18188, - 18189, - 18190, - 18191, - 18192, - 18193, - 18194, - 18195, - 18196, - 18197, - 18198, - 18199, - 18200, - 18201, - 18202, - 18203, - 18204, - 18205, - 18206, - 18207, - 18208, - 18209, - 18210, - 18211, - 18212, - 18213, - 18214, - 18215, - 18216, - 18217, - 18218, - 18219, - 18220, - 18221, - 18222, - 18223, - 18224, - 18225, - 18226, - 18227, - 18228, - 18229, - 18230, - 18231, - 18232, - 18233, - 18234, - 18235, - 18236, - 18237, - 18238, - 18239, - 18240, - 18241, - 18242, - 18243, - 18244, - 18245, - 18246, - 18247, - 18248, - 18249, - 18250, - 18251, - 18252, - 18253, - 18254, - 18255, - 18256, - 18257, - 18258, - 18259, - 18260, - 18261, - 18262, - 18263, - 18264, - 18265, - 18266, - 18267, - 18268, - 18269, - 18270, - 18271, - 18272, - 18273, - 18274, - 18275, - 18276, - 18277, - 18278, - 18279, - 18280, - 18281, - 18282, - 18283, - 18284, - 18285, - 18286, - 18287, - 18288, - 18289, - 18290, - 18291, - 18292, - 18293, - 18294, - 18295, - 18296, - 18297, - 18298, - 18299, - 18300, - 18301, - 18302, - 18303, - 18304, - 18305, - 18306, - 18307, - 18308, - 18309, - 18310, - 18311, - 18312, - 18313, - 18314, - 18315, - 18316, - 18317, - 18318, - 18319, - 18320, - 18321, - 18322, - 18323, - 18324, - 18325, - 18326, - 18327, - 18328, - 18329, - 18330, - 18331, - 18332, - 18333, - 18334, - 18335, - 18336, - 18337, - 18338, - 18339, - 18340, - 18341, - 18342, - 18343, - 18344, - 18345, - 18346, - 18347, - 18348, - 18349, - 18350, - 18351, - 18352, - 18353, - 18354, - 18355, - 18356, - 18357, - 18358, - 18359, - 18360, - 18361, - 18362, - 18363, - 18364, - 18365, - 18366, - 18367, - 18368, - 18369, - 18370, - 18371, - 18372, - 18373, - 18374, - 18375, - 18376, - 18377, - 18378, - 18379, - 18380, - 18381, - 18382, - 18383, - 18384, - 18385, - 18386, - 18387, - 18388, - 18389, - 18390, - 18391, - 18392, - 18393, - 18394, - 18395, - 18396, - 18397, - 18398, - 18399, - 18400, - 18401, - 18402, - 18403, - 18404, - 18405, - 18406, - 18407, - 18408, - 18409, - 18410, - 18411, - 18412, - 18413, - 18414, - 18415, - 18416, - 18417, - 18418, - 18419, - 18420, - 18421, - 18422, - 18423, - 18424, - 18425, - 18426, - 18427, - 18428, - 18429, - 18430, - 18431, - 18432, - 18433, - 18434, - 18435, - 18436, - 18437, - 18438, - 18439, - 18440, - 18441, - 18442, - 18443, - 18444, - 18445, - 18446, - 18447, - 18448, - 18449, - 18450, - 18451, - 18452, - 18453, - 18454, - 18455, - 18456, - 18457, - 18458, - 18459, - 18460, - 18461, - 18462, - 18463, - 18464, - 18465, - 18466, - 18467, - 18468, - 18469, - 18470, - 18471, - 18472, - 18473, - 18474, - 18475, - 18476, - 18477, - 18478, - 18479, - 18480, - 18481, - 18482, - 18483, - 18484, - 18485, - 18486, - 18487, - 18488, - 18489, - 18490, - 18491, - 18492, - 18493, - 18494, - 18495, - 18496, - 18497, - 18498, - 18499, - 18500, - 18501, - 18502, - 18503, - 18504, - 18505, - 18506, - 18507, - 18508, - 18509, - 18510, - 18511, - 18512, - 18513, - 18514, - 18515, - 18516, - 18517, - 18518, - 18519, - 18520, - 18521, - 18522, - 18523, - 18524, - 18525, - 18526, - 18527, - 18528, - 18529, - 18530, - 18531, - 18532, - 18533, - 18534, - 18535, - 18536, - 18537, - 18538, - 18539, - 18540, - 18541, - 18542, - 18543, - 18544, - 18545, - 18546, - 18547, - 18548, - 18549, - 18550, - 18551, - 18552, - 18553, - 18554, - 18555, - 18556, - 18557, - 18558, - 18559, - 18560, - 18561, - 18562, - 18563, - 18564, - 18565, - 18566, - 18567, - 18568, - 18569, - 18570, - 18571, - 18572, - 18573, - 18574, - 18575, - 18576, - 18577, - 18578, - 18579, - 18580, - 18581, - 18582, - 18583, - 18584, - 18585, - 18586, - 18587, - 18588, - 18589, - 18590, - 18591, - 18592, - 18593, - 18594, - 18595, - 18596, - 18597, - 18598, - 18599, - 18600, - 18601, - 18602, - 18603, - 18604, - 18605, - 18606, - 18607, - 18608, - 18609, - 18610, - 18611, - 18612, - 18613, - 18614, - 18615, - 18616, - 18617, - 18618, - 18619, - 18620, - 18621, - 18622, - 18623, - 18624, - 18625, - 18626, - 18627, - 18628, - 18629, - 18630, - 18631, - 18632, - 18633, - 18634, - 18635, - 18636, - 18637, - 18638, - 18639, - 18640, - 18641, - 18642, - 18643, - 18644, - 18645, - 18646, - 18647, - 18648, - 18649, - 18650, - 18651, - 18652, - 18653, - 18654, - 18655, - 18656, - 18657, - 18658, - 18659, - 18660, - 18661, - 18662, - 18663, - 18664, - 18665, - 18666, - 18667, - 18668, - 18669, - 18670, - 18671, - 18672, - 18673, - 18674, - 18675, - 18676, - 18677, - 18678, - 18679, - 18680, - 18681, - 18682, - 18683, - 18684, - 18685, - 18686, - 18687, - 18688, - 18689, - 18690, - 18691, - 18692, - 18693, - 18694, - 18695, - 18696, - 18697, - 18698, - 18699, - 18700, - 18701, - 18702, - 18703, - 18704, - 18705, - 18706, - 18707, - 18708, - 18709, - 18710, - 18711, - 18712, - 18713, - 18714, - 18715, - 18716, - 18717, - 18718, - 18719, - 18720, - 18721, - 18722, - 18723, - 18724, - 18725, - 18726, - 18727, - 18728, - 18729, - 18730, - 18731, - 18732, - 18733, - 18734, - 18735, - 18736, - 18737, - 18738, - 18739, - 18740, - 18741, - 18742, - 18743, - 18744, - 18745, - 18746, - 18747, - 18748, - 18749, - 18750, - 18751, - 18752, - 18753, - 18754, - 18755, - 18756, - 18757, - 18758, - 18759, - 18760, - 18761, - 18762, - 18763, - 18764, - 18765, - 18766, - 18767, - 18768, - 18769, - 18770, - 18771, - 18772, - 18773, - 18774, - 18775, - 18776, - 18777, - 18778, - 18779, - 18780, - 18781, - 18782, - 18783, - 18784, - 18785, - 18786, - 18787, - 18788, - 18789, - 18790, - 18791, - 18792, - 18793, - 18794, - 18795, - 18796, - 18797, - 18798, - 18799, - 18800, - 18801, - 18802, - 18803, - 18804, - 18805, - 18806, - 18807, - 18808, - 18809, - 18810, - 18811, - 18812, - 18813, - 18814, - 18815, - 18816, - 18817, - 18818, - 18819, - 18820, - 18821, - 18822, - 18823, - 18824, - 18825, - 18826, - 18827, - 18828, - 18829, - 18830, - 18831, - 18832, - 18833, - 18834, - 18835, - 18836, - 18837, - 18838, - 18839, - 18840, - 18841, - 18842, - 18843, - 18844, - 18845, - 18846, - 18847, - 18848, - 18849, - 18850, - 18851, - 18852, - 18853, - 18854, - 18855, - 18856, - 18857, - 18858, - 18859, - 18860, - 18861, - 18862, - 18863, - 18864, - 18865, - 18866, - 18867, - 18868, - 18869, - 18870, - 18871, - 18872, - 18873, - 18874, - 18875, - 18876, - 18877, - 18878, - 18879, - 18880, - 18881, - 18882, - 18883, - 18884, - 18885, - 18886, - 18887, - 18888, - 18889, - 18890, - 18891, - 18892, - 18893, - 18894, - 18895, - 18896, - 18897, - 18898, - 18899, - 18900, - 18901, - 18902, - 18903, - 18904, - 18905, - 18906, - 18907, - 18908, - 18909, - 18910, - 18911, - 18912, - 18913, - 18914, - 18915, - 18916, - 18917, - 18918, - 18919, - 18920, - 18921, - 18922, - 18923, - 18924, - 18925, - 18926, - 18927, - 18928, - 18929, - 18930, - 18931, - 18932, - 18933, - 18934, - 18935, - 18936, - 18937, - 18938, - 18939, - 18940, - 18941, - 18942, - 18943, - 18944, - 18945, - 18946, - 18947, - 18948, - 18949, - 18950, - 18951, - 18952, - 18953, - 18954, - 18955, - 18956, - 18957, - 18958, - 18959, - 18960, - 18961, - 18962, - 18963, - 18964, - 18965, - 18966, - 18967, - 18968, - 18969, - 18970, - 18971, - 18972, - 18973, - 18974, - 18975, - 18976, - 18977, - 18978, - 18979, - 18980, - 18981, - 18982, - 18983, - 18984, - 18985, - 18986, - 18987, - 18988, - 18989, - 18990, - 18991, - 18992, - 18993, - 18994, - 18995, - 18996, - 18997, - 18998, - 18999, - 19000, - 19001, - 19002, - 19003, - 19004, - 19005, - 19006, - 19007, - 19008, - 19009, - 19010, - 19011, - 19012, - 19013, - 19014, - 19015, - 19016, - 19017, - 19018, - 19019, - 19020, - 19021, - 19022, - 19023, - 19024, - 19025, - 19026, - 19027, - 19028, - 19029, - 19030, - 19031, - 19032, - 19033, - 19034, - 19035, - 19036, - 19037, - 19038, - 19039, - 19040, - 19041, - 19042, - 19043, - 19044, - 19045, - 19046, - 19047, - 19048, - 19049, - 19050, - 19051, - 19052, - 19053, - 19054, - 19055, - 19056, - 19057, - 19058, - 19059, - 19060, - 19061, - 19062, - 19063, - 19064, - 19065, - 19066, - 19067, - 19068, - 19069, - 19070, - 19071, - 19072, - 19073, - 19074, - 19075, - 19076, - 19077, - 19078, - 19079, - 19080, - 19081, - 19082, - 19083, - 19084, - 19085, - 19086, - 19087, - 19088, - 19089, - 19090, - 19091, - 19092, - 19093, - 19094, - 19095, - 19096, - 19097, - 19098, - 19099, - 19100, - 19101, - 19102, - 19103, - 19104, - 19105, - 19106, - 19107, - 19108, - 19109, - 19110, - 19111, - 19112, - 19113, - 19114, - 19115, - 19116, - 19117, - 19118, - 19119, - 19120, - 19121, - 19122, - 19123, - 19124, - 19125, - 19126, - 19127, - 19128, - 19129, - 19130, - 19131, - 19132, - 19133, - 19134, - 19135, - 19136, - 19137, - 19138, - 19139, - 19140, - 19141, - 19142, - 19143, - 19144, - 19145, - 19146, - 19147, - 19148, - 19149, - 19150, - 19151, - 19152, - 19153, - 19154, - 19155, - 19156, - 19157, - 19158, - 19159, - 19160, - 19161, - 19162, - 19163, - 19164, - 19165, - 19166, - 19167, - 19168, - 19169, - 19170, - 19171, - 19172, - 19173, - 19174, - 19175, - 19176, - 19177, - 19178, - 19179, - 19180, - 19181, - 19182, - 19183, - 19184, - 19185, - 19186, - 19187, - 19188, - 19189, - 19190, - 19191, - 19192, - 19193, - 19194, - 19195, - 19196, - 19197, - 19198, - 19199, - 19200, - 19201, - 19202, - 19203, - 19204, - 19205, - 19206, - 19207, - 19208, - 19209, - 19210, - 19211, - 19212, - 19213, - 19214, - 19215, - 19216, - 19217, - 19218, - 19219, - 19220, - 19221, - 19222, - 19223, - 19224, - 19225, - 19226, - 19227, - 19228, - 19229, - 19230, - 19231, - 19232, - 19233, - 19234, - 19235, - 19236, - 19237, - 19238, - 19239, - 19240, - 19241, - 19242, - 19243, - 19244, - 19245, - 19246, - 19247, - 19248, - 19249, - 19250, - 19251, - 19252, - 19253, - 19254, - 19255, - 19256, - 19257, - 19258, - 19259, - 19260, - 19261, - 19262, - 19263, - 19264, - 19265, - 19266, - 19267, - 19268, - 19269, - 19270, - 19271, - 19272, - 19273, - 19274, - 19275, - 19276, - 19277, - 19278, - 19279, - 19280, - 19281, - 19282, - 19283, - 19284, - 19285, - 19286, - 19287, - 19288, - 19289, - 19290, - 19291, - 19292, - 19293, - 19294, - 19295, - 19296, - 19297, - 19298, - 19299, - 19300, - 19301, - 19302, - 19303, - 19304, - 19305, - 19306, - 19307, - 19308, - 19309, - 19310, - 19311, - 19312, - 19313, - 19314, - 19315, - 19316, - 19317, - 19318, - 19319, - 19320, - 19321, - 19322, - 19323, - 19324, - 19325, - 19326, - 19327, - 19328, - 19329, - 19330, - 19331, - 19332, - 19333, - 19334, - 19335, - 19336, - 19337, - 19338, - 19339, - 19340, - 19341, - 19342, - 19343, - 19344, - 19345, - 19346, - 19347, - 19348, - 19349, - 19350, - 19351, - 19352, - 19353, - 19354, - 19355, - 19356, - 19357, - 19358, - 19359, - 19360, - 19361, - 19362, - 19363, - 19364, - 19365, - 19366, - 19367, - 19368, - 19369, - 19370, - 19371, - 19372, - 19373, - 19374, - 19375, - 19376, - 19377, - 19378, - 19379, - 19380, - 19381, - 19382, - 19383, - 19384, - 19385, - 19386, - 19387, - 19388, - 19389, - 19390, - 19391, - 19392, - 19393, - 19394, - 19395, - 19396, - 19397, - 19398, - 19399, - 19400, - 19401, - 19402, - 19403, - 19404, - 19405, - 19406, - 19407, - 19408, - 19409, - 19410, - 19411, - 19412, - 19413, - 19414, - 19415, - 19416, - 19417, - 19418, - 19419, - 19420, - 19421, - 19422, - 19423, - 19424, - 19425, - 19426, - 19427, - 19428, - 19429, - 19430, - 19431, - 19432, - 19433, - 19434, - 19435, - 19436, - 19437, - 19438, - 19439, - 19440, - 19441, - 19442, - 19443, - 19444, - 19445, - 19446, - 19447, - 19448, - 19449, - 19450, - 19451, - 19452, - 19453, - 19454, - 19455, - 19456, - 19457, - 19458, - 19459, - 19460, - 19461, - 19462, - 19463, - 19464, - 19465, - 19466, - 19467, - 19468, - 19469, - 19470, - 19471, - 19472, - 19473, - 19474, - 19475, - 19476, - 19477, - 19478, - 19479, - 19480, - 19481, - 19482, - 19483, - 19484, - 19485, - 19486, - 19487, - 19488, - 19489, - 19490, - 19491, - 19492, - 19493, - 19494, - 19495, - 19496, - 19497, - 19498, - 19499, - 19500, - 19501, - 19502, - 19503, - 19504, - 19505, - 19506, - 19507, - 19508, - 19509, - 19510, - 19511, - 19512, - 19513, - 19514, - 19515, - 19516, - 19517, - 19518, - 19519, - 19520, - 19521, - 19522, - 19523, - 19524, - 19525, - 19526, - 19527, - 19528, - 19529, - 19530, - 19531, - 19532, - 19533, - 19534, - 19535, - 19536, - 19537, - 19538, - 19539, - 19540, - 19541, - 19542, - 19543, - 19544, - 19545, - 19546, - 19547, - 19548, - 19549, - 19550, - 19551, - 19552, - 19553, - 19554, - 19555, - 19556, - 19557, - 19558, - 19559, - 19560, - 19561, - 19562, - 19563, - 19564, - 19565, - 19566, - 19567, - 19568, - 19569, - 19570, - 19571, - 19572, - 19573, - 19574, - 19575, - 19576, - 19577, - 19578, - 19579, - 19580, - 19581, - 19582, - 19583, - 19584, - 19585, - 19586, - 19587, - 19588, - 19589, - 19590, - 19591, - 19592, - 19593, - 19594, - 19595, - 19596, - 19597, - 19598, - 19599, - 19600, - 19601, - 19602, - 19603, - 19604, - 19605, - 19606, - 19607, - 19608, - 19609, - 19610, - 19611, - 19612, - 19613, - 19614, - 19615, - 19616, - 19617, - 19618, - 19619, - 19620, - 19621, - 19622, - 19623, - 19624, - 19625, - 19626, - 19627, - 19628, - 19629, - 19630, - 19631, - 19632, - 19633, - 19634, - 19635, - 19636, - 19637, - 19638, - 19639, - 19640, - 19641, - 19642, - 19643, - 19644, - 19645, - 19646, - 19647, - 19648, - 19649, - 19650, - 19651, - 19652, - 19653, - 19654, - 19655, - 19656, - 19657, - 19658, - 19659, - 19660, - 19661, - 19662, - 19663, - 19664, - 19665, - 19666, - 19667, - 19668, - 19669, - 19670, - 19671, - 19672, - 19673, - 19674, - 19675, - 19676, - 19677, - 19678, - 19679, - 19680, - 19681, - 19682, - 19683, - 19684, - 19685, - 19686, - 19687, - 19688, - 19689, - 19690, - 19691, - 19692, - 19693, - 19694, - 19695, - 19696, - 19697, - 19698, - 19699, - 19700, - 19701, - 19702, - 19703, - 19704, - 19705, - 19706, - 19707, - 19708, - 19709, - 19710, - 19711, - 19712, - 19713, - 19714, - 19715, - 19716, - 19717, - 19718, - 19719, - 19720, - 19721, - 19722, - 19723, - 19724, - 19725, - 19726, - 19727, - 19728, - 19729, - 19730, - 19731, - 19732, - 19733, - 19734, - 19735, - 19736, - 19737, - 19738, - 19739, - 19740, - 19741, - 19742, - 19743, - 19744, - 19745, - 19746, - 19747, - 19748, - 19749, - 19750, - 19751, - 19752, - 19753, - 19754, - 19755, - 19756, - 19757, - 19758, - 19759, - 19760, - 19761, - 19762, - 19763, - 19764, - 19765, - 19766, - 19767, - 19768, - 19769, - 19770, - 19771, - 19772, - 19773, - 19774, - 19775, - 19776, - 19777, - 19778, - 19779, - 19780, - 19781, - 19782, - 19783, - 19784, - 19785, - 19786, - 19787, - 19788, - 19789, - 19790, - 19791, - 19792, - 19793, - 19794, - 19795, - 19796, - 19797, - 19798, - 19799, - 19800, - 19801, - 19802, - 19803, - 19804, - 19805, - 19806, - 19807, - 19808, - 19809, - 19810, - 19811, - 19812, - 19813, - 19814, - 19815, - 19816, - 19817, - 19818, - 19819, - 19820, - 19821, - 19822, - 19823, - 19824, - 19825, - 19826, - 19827, - 19828, - 19829, - 19830, - 19831, - 19832, - 19833, - 19834, - 19835, - 19836, - 19837, - 19838, - 19839, - 19840, - 19841, - 19842, - 19843, - 19844, - 19845, - 19846, - 19847, - 19848, - 19849, - 19850, - 19851, - 19852, - 19853, - 19854, - 19855, - 19856, - 19857, - 19858, - 19859, - 19860, - 19861, - 19862, - 19863, - 19864, - 19865, - 19866, - 19867, - 19868, - 19869, - 19870, - 19871, - 19872, - 19873, - 19874, - 19875, - 19876, - 19877, - 19878, - 19879, - 19880, - 19881, - 19882, - 19883, - 19884, - 19885, - 19886, - 19887, - 19888, - 19889, - 19890, - 19891, - 19892, - 19893, - 19968, - 19969, - 19970, - 19971, - 19972, - 19973, - 19974, - 19975, - 19976, - 19977, - 19978, - 19979, - 19980, - 19981, - 19982, - 19983, - 19984, - 19985, - 19986, - 19987, - 19988, - 19989, - 19990, - 19991, - 19992, - 19993, - 19994, - 19995, - 19996, - 19997, - 19998, - 19999, - 20000, - 20001, - 20002, - 20003, - 20004, - 20005, - 20006, - 20007, - 20008, - 20009, - 20010, - 20011, - 20012, - 20013, - 20014, - 20015, - 20016, - 20017, - 20018, - 20019, - 20020, - 20021, - 20022, - 20023, - 20024, - 20025, - 20026, - 20027, - 20028, - 20029, - 20030, - 20031, - 20032, - 20033, - 20034, - 20035, - 20036, - 20037, - 20038, - 20039, - 20040, - 20041, - 20042, - 20043, - 20044, - 20045, - 20046, - 20047, - 20048, - 20049, - 20050, - 20051, - 20052, - 20053, - 20054, - 20055, - 20056, - 20057, - 20058, - 20059, - 20060, - 20061, - 20062, - 20063, - 20064, - 20065, - 20066, - 20067, - 20068, - 20069, - 20070, - 20071, - 20072, - 20073, - 20074, - 20075, - 20076, - 20077, - 20078, - 20079, - 20080, - 20081, - 20082, - 20083, - 20084, - 20085, - 20086, - 20087, - 20088, - 20089, - 20090, - 20091, - 20092, - 20093, - 20094, - 20095, - 20096, - 20097, - 20098, - 20099, - 20100, - 20101, - 20102, - 20103, - 20104, - 20105, - 20106, - 20107, - 20108, - 20109, - 20110, - 20111, - 20112, - 20113, - 20114, - 20115, - 20116, - 20117, - 20118, - 20119, - 20120, - 20121, - 20122, - 20123, - 20124, - 20125, - 20126, - 20127, - 20128, - 20129, - 20130, - 20131, - 20132, - 20133, - 20134, - 20135, - 20136, - 20137, - 20138, - 20139, - 20140, - 20141, - 20142, - 20143, - 20144, - 20145, - 20146, - 20147, - 20148, - 20149, - 20150, - 20151, - 20152, - 20153, - 20154, - 20155, - 20156, - 20157, - 20158, - 20159, - 20160, - 20161, - 20162, - 20163, - 20164, - 20165, - 20166, - 20167, - 20168, - 20169, - 20170, - 20171, - 20172, - 20173, - 20174, - 20175, - 20176, - 20177, - 20178, - 20179, - 20180, - 20181, - 20182, - 20183, - 20184, - 20185, - 20186, - 20187, - 20188, - 20189, - 20190, - 20191, - 20192, - 20193, - 20194, - 20195, - 20196, - 20197, - 20198, - 20199, - 20200, - 20201, - 20202, - 20203, - 20204, - 20205, - 20206, - 20207, - 20208, - 20209, - 20210, - 20211, - 20212, - 20213, - 20214, - 20215, - 20216, - 20217, - 20218, - 20219, - 20220, - 20221, - 20222, - 20223, - 20224, - 20225, - 20226, - 20227, - 20228, - 20229, - 20230, - 20231, - 20232, - 20233, - 20234, - 20235, - 20236, - 20237, - 20238, - 20239, - 20240, - 20241, - 20242, - 20243, - 20244, - 20245, - 20246, - 20247, - 20248, - 20249, - 20250, - 20251, - 20252, - 20253, - 20254, - 20255, - 20256, - 20257, - 20258, - 20259, - 20260, - 20261, - 20262, - 20263, - 20264, - 20265, - 20266, - 20267, - 20268, - 20269, - 20270, - 20271, - 20272, - 20273, - 20274, - 20275, - 20276, - 20277, - 20278, - 20279, - 20280, - 20281, - 20282, - 20283, - 20284, - 20285, - 20286, - 20287, - 20288, - 20289, - 20290, - 20291, - 20292, - 20293, - 20294, - 20295, - 20296, - 20297, - 20298, - 20299, - 20300, - 20301, - 20302, - 20303, - 20304, - 20305, - 20306, - 20307, - 20308, - 20309, - 20310, - 20311, - 20312, - 20313, - 20314, - 20315, - 20316, - 20317, - 20318, - 20319, - 20320, - 20321, - 20322, - 20323, - 20324, - 20325, - 20326, - 20327, - 20328, - 20329, - 20330, - 20331, - 20332, - 20333, - 20334, - 20335, - 20336, - 20337, - 20338, - 20339, - 20340, - 20341, - 20342, - 20343, - 20344, - 20345, - 20346, - 20347, - 20348, - 20349, - 20350, - 20351, - 20352, - 20353, - 20354, - 20355, - 20356, - 20357, - 20358, - 20359, - 20360, - 20361, - 20362, - 20363, - 20364, - 20365, - 20366, - 20367, - 20368, - 20369, - 20370, - 20371, - 20372, - 20373, - 20374, - 20375, - 20376, - 20377, - 20378, - 20379, - 20380, - 20381, - 20382, - 20383, - 20384, - 20385, - 20386, - 20387, - 20388, - 20389, - 20390, - 20391, - 20392, - 20393, - 20394, - 20395, - 20396, - 20397, - 20398, - 20399, - 20400, - 20401, - 20402, - 20403, - 20404, - 20405, - 20406, - 20407, - 20408, - 20409, - 20410, - 20411, - 20412, - 20413, - 20414, - 20415, - 20416, - 20417, - 20418, - 20419, - 20420, - 20421, - 20422, - 20423, - 20424, - 20425, - 20426, - 20427, - 20428, - 20429, - 20430, - 20431, - 20432, - 20433, - 20434, - 20435, - 20436, - 20437, - 20438, - 20439, - 20440, - 20441, - 20442, - 20443, - 20444, - 20445, - 20446, - 20447, - 20448, - 20449, - 20450, - 20451, - 20452, - 20453, - 20454, - 20455, - 20456, - 20457, - 20458, - 20459, - 20460, - 20461, - 20462, - 20463, - 20464, - 20465, - 20466, - 20467, - 20468, - 20469, - 20470, - 20471, - 20472, - 20473, - 20474, - 20475, - 20476, - 20477, - 20478, - 20479, - 20480, - 20481, - 20482, - 20483, - 20484, - 20485, - 20486, - 20487, - 20488, - 20489, - 20490, - 20491, - 20492, - 20493, - 20494, - 20495, - 20496, - 20497, - 20498, - 20499, - 20500, - 20501, - 20502, - 20503, - 20504, - 20505, - 20506, - 20507, - 20508, - 20509, - 20510, - 20511, - 20512, - 20513, - 20514, - 20515, - 20516, - 20517, - 20518, - 20519, - 20520, - 20521, - 20522, - 20523, - 20524, - 20525, - 20526, - 20527, - 20528, - 20529, - 20530, - 20531, - 20532, - 20533, - 20534, - 20535, - 20536, - 20537, - 20538, - 20539, - 20540, - 20541, - 20542, - 20543, - 20544, - 20545, - 20546, - 20547, - 20548, - 20549, - 20550, - 20551, - 20552, - 20553, - 20554, - 20555, - 20556, - 20557, - 20558, - 20559, - 20560, - 20561, - 20562, - 20563, - 20564, - 20565, - 20566, - 20567, - 20568, - 20569, - 20570, - 20571, - 20572, - 20573, - 20574, - 20575, - 20576, - 20577, - 20578, - 20579, - 20580, - 20581, - 20582, - 20583, - 20584, - 20585, - 20586, - 20587, - 20588, - 20589, - 20590, - 20591, - 20592, - 20593, - 20594, - 20595, - 20596, - 20597, - 20598, - 20599, - 20600, - 20601, - 20602, - 20603, - 20604, - 20605, - 20606, - 20607, - 20608, - 20609, - 20610, - 20611, - 20612, - 20613, - 20614, - 20615, - 20616, - 20617, - 20618, - 20619, - 20620, - 20621, - 20622, - 20623, - 20624, - 20625, - 20626, - 20627, - 20628, - 20629, - 20630, - 20631, - 20632, - 20633, - 20634, - 20635, - 20636, - 20637, - 20638, - 20639, - 20640, - 20641, - 20642, - 20643, - 20644, - 20645, - 20646, - 20647, - 20648, - 20649, - 20650, - 20651, - 20652, - 20653, - 20654, - 20655, - 20656, - 20657, - 20658, - 20659, - 20660, - 20661, - 20662, - 20663, - 20664, - 20665, - 20666, - 20667, - 20668, - 20669, - 20670, - 20671, - 20672, - 20673, - 20674, - 20675, - 20676, - 20677, - 20678, - 20679, - 20680, - 20681, - 20682, - 20683, - 20684, - 20685, - 20686, - 20687, - 20688, - 20689, - 20690, - 20691, - 20692, - 20693, - 20694, - 20695, - 20696, - 20697, - 20698, - 20699, - 20700, - 20701, - 20702, - 20703, - 20704, - 20705, - 20706, - 20707, - 20708, - 20709, - 20710, - 20711, - 20712, - 20713, - 20714, - 20715, - 20716, - 20717, - 20718, - 20719, - 20720, - 20721, - 20722, - 20723, - 20724, - 20725, - 20726, - 20727, - 20728, - 20729, - 20730, - 20731, - 20732, - 20733, - 20734, - 20735, - 20736, - 20737, - 20738, - 20739, - 20740, - 20741, - 20742, - 20743, - 20744, - 20745, - 20746, - 20747, - 20748, - 20749, - 20750, - 20751, - 20752, - 20753, - 20754, - 20755, - 20756, - 20757, - 20758, - 20759, - 20760, - 20761, - 20762, - 20763, - 20764, - 20765, - 20766, - 20767, - 20768, - 20769, - 20770, - 20771, - 20772, - 20773, - 20774, - 20775, - 20776, - 20777, - 20778, - 20779, - 20780, - 20781, - 20782, - 20783, - 20784, - 20785, - 20786, - 20787, - 20788, - 20789, - 20790, - 20791, - 20792, - 20793, - 20794, - 20795, - 20796, - 20797, - 20798, - 20799, - 20800, - 20801, - 20802, - 20803, - 20804, - 20805, - 20806, - 20807, - 20808, - 20809, - 20810, - 20811, - 20812, - 20813, - 20814, - 20815, - 20816, - 20817, - 20818, - 20819, - 20820, - 20821, - 20822, - 20823, - 20824, - 20825, - 20826, - 20827, - 20828, - 20829, - 20830, - 20831, - 20832, - 20833, - 20834, - 20835, - 20836, - 20837, - 20838, - 20839, - 20840, - 20841, - 20842, - 20843, - 20844, - 20845, - 20846, - 20847, - 20848, - 20849, - 20850, - 20851, - 20852, - 20853, - 20854, - 20855, - 20856, - 20857, - 20858, - 20859, - 20860, - 20861, - 20862, - 20863, - 20864, - 20865, - 20866, - 20867, - 20868, - 20869, - 20870, - 20871, - 20872, - 20873, - 20874, - 20875, - 20876, - 20877, - 20878, - 20879, - 20880, - 20881, - 20882, - 20883, - 20884, - 20885, - 20886, - 20887, - 20888, - 20889, - 20890, - 20891, - 20892, - 20893, - 20894, - 20895, - 20896, - 20897, - 20898, - 20899, - 20900, - 20901, - 20902, - 20903, - 20904, - 20905, - 20906, - 20907, - 20908, - 20909, - 20910, - 20911, - 20912, - 20913, - 20914, - 20915, - 20916, - 20917, - 20918, - 20919, - 20920, - 20921, - 20922, - 20923, - 20924, - 20925, - 20926, - 20927, - 20928, - 20929, - 20930, - 20931, - 20932, - 20933, - 20934, - 20935, - 20936, - 20937, - 20938, - 20939, - 20940, - 20941, - 20942, - 20943, - 20944, - 20945, - 20946, - 20947, - 20948, - 20949, - 20950, - 20951, - 20952, - 20953, - 20954, - 20955, - 20956, - 20957, - 20958, - 20959, - 20960, - 20961, - 20962, - 20963, - 20964, - 20965, - 20966, - 20967, - 20968, - 20969, - 20970, - 20971, - 20972, - 20973, - 20974, - 20975, - 20976, - 20977, - 20978, - 20979, - 20980, - 20981, - 20982, - 20983, - 20984, - 20985, - 20986, - 20987, - 20988, - 20989, - 20990, - 20991, - 20992, - 20993, - 20994, - 20995, - 20996, - 20997, - 20998, - 20999, - 21000, - 21001, - 21002, - 21003, - 21004, - 21005, - 21006, - 21007, - 21008, - 21009, - 21010, - 21011, - 21012, - 21013, - 21014, - 21015, - 21016, - 21017, - 21018, - 21019, - 21020, - 21021, - 21022, - 21023, - 21024, - 21025, - 21026, - 21027, - 21028, - 21029, - 21030, - 21031, - 21032, - 21033, - 21034, - 21035, - 21036, - 21037, - 21038, - 21039, - 21040, - 21041, - 21042, - 21043, - 21044, - 21045, - 21046, - 21047, - 21048, - 21049, - 21050, - 21051, - 21052, - 21053, - 21054, - 21055, - 21056, - 21057, - 21058, - 21059, - 21060, - 21061, - 21062, - 21063, - 21064, - 21065, - 21066, - 21067, - 21068, - 21069, - 21070, - 21071, - 21072, - 21073, - 21074, - 21075, - 21076, - 21077, - 21078, - 21079, - 21080, - 21081, - 21082, - 21083, - 21084, - 21085, - 21086, - 21087, - 21088, - 21089, - 21090, - 21091, - 21092, - 21093, - 21094, - 21095, - 21096, - 21097, - 21098, - 21099, - 21100, - 21101, - 21102, - 21103, - 21104, - 21105, - 21106, - 21107, - 21108, - 21109, - 21110, - 21111, - 21112, - 21113, - 21114, - 21115, - 21116, - 21117, - 21118, - 21119, - 21120, - 21121, - 21122, - 21123, - 21124, - 21125, - 21126, - 21127, - 21128, - 21129, - 21130, - 21131, - 21132, - 21133, - 21134, - 21135, - 21136, - 21137, - 21138, - 21139, - 21140, - 21141, - 21142, - 21143, - 21144, - 21145, - 21146, - 21147, - 21148, - 21149, - 21150, - 21151, - 21152, - 21153, - 21154, - 21155, - 21156, - 21157, - 21158, - 21159, - 21160, - 21161, - 21162, - 21163, - 21164, - 21165, - 21166, - 21167, - 21168, - 21169, - 21170, - 21171, - 21172, - 21173, - 21174, - 21175, - 21176, - 21177, - 21178, - 21179, - 21180, - 21181, - 21182, - 21183, - 21184, - 21185, - 21186, - 21187, - 21188, - 21189, - 21190, - 21191, - 21192, - 21193, - 21194, - 21195, - 21196, - 21197, - 21198, - 21199, - 21200, - 21201, - 21202, - 21203, - 21204, - 21205, - 21206, - 21207, - 21208, - 21209, - 21210, - 21211, - 21212, - 21213, - 21214, - 21215, - 21216, - 21217, - 21218, - 21219, - 21220, - 21221, - 21222, - 21223, - 21224, - 21225, - 21226, - 21227, - 21228, - 21229, - 21230, - 21231, - 21232, - 21233, - 21234, - 21235, - 21236, - 21237, - 21238, - 21239, - 21240, - 21241, - 21242, - 21243, - 21244, - 21245, - 21246, - 21247, - 21248, - 21249, - 21250, - 21251, - 21252, - 21253, - 21254, - 21255, - 21256, - 21257, - 21258, - 21259, - 21260, - 21261, - 21262, - 21263, - 21264, - 21265, - 21266, - 21267, - 21268, - 21269, - 21270, - 21271, - 21272, - 21273, - 21274, - 21275, - 21276, - 21277, - 21278, - 21279, - 21280, - 21281, - 21282, - 21283, - 21284, - 21285, - 21286, - 21287, - 21288, - 21289, - 21290, - 21291, - 21292, - 21293, - 21294, - 21295, - 21296, - 21297, - 21298, - 21299, - 21300, - 21301, - 21302, - 21303, - 21304, - 21305, - 21306, - 21307, - 21308, - 21309, - 21310, - 21311, - 21312, - 21313, - 21314, - 21315, - 21316, - 21317, - 21318, - 21319, - 21320, - 21321, - 21322, - 21323, - 21324, - 21325, - 21326, - 21327, - 21328, - 21329, - 21330, - 21331, - 21332, - 21333, - 21334, - 21335, - 21336, - 21337, - 21338, - 21339, - 21340, - 21341, - 21342, - 21343, - 21344, - 21345, - 21346, - 21347, - 21348, - 21349, - 21350, - 21351, - 21352, - 21353, - 21354, - 21355, - 21356, - 21357, - 21358, - 21359, - 21360, - 21361, - 21362, - 21363, - 21364, - 21365, - 21366, - 21367, - 21368, - 21369, - 21370, - 21371, - 21372, - 21373, - 21374, - 21375, - 21376, - 21377, - 21378, - 21379, - 21380, - 21381, - 21382, - 21383, - 21384, - 21385, - 21386, - 21387, - 21388, - 21389, - 21390, - 21391, - 21392, - 21393, - 21394, - 21395, - 21396, - 21397, - 21398, - 21399, - 21400, - 21401, - 21402, - 21403, - 21404, - 21405, - 21406, - 21407, - 21408, - 21409, - 21410, - 21411, - 21412, - 21413, - 21414, - 21415, - 21416, - 21417, - 21418, - 21419, - 21420, - 21421, - 21422, - 21423, - 21424, - 21425, - 21426, - 21427, - 21428, - 21429, - 21430, - 21431, - 21432, - 21433, - 21434, - 21435, - 21436, - 21437, - 21438, - 21439, - 21440, - 21441, - 21442, - 21443, - 21444, - 21445, - 21446, - 21447, - 21448, - 21449, - 21450, - 21451, - 21452, - 21453, - 21454, - 21455, - 21456, - 21457, - 21458, - 21459, - 21460, - 21461, - 21462, - 21463, - 21464, - 21465, - 21466, - 21467, - 21468, - 21469, - 21470, - 21471, - 21472, - 21473, - 21474, - 21475, - 21476, - 21477, - 21478, - 21479, - 21480, - 21481, - 21482, - 21483, - 21484, - 21485, - 21486, - 21487, - 21488, - 21489, - 21490, - 21491, - 21492, - 21493, - 21494, - 21495, - 21496, - 21497, - 21498, - 21499, - 21500, - 21501, - 21502, - 21503, - 21504, - 21505, - 21506, - 21507, - 21508, - 21509, - 21510, - 21511, - 21512, - 21513, - 21514, - 21515, - 21516, - 21517, - 21518, - 21519, - 21520, - 21521, - 21522, - 21523, - 21524, - 21525, - 21526, - 21527, - 21528, - 21529, - 21530, - 21531, - 21532, - 21533, - 21534, - 21535, - 21536, - 21537, - 21538, - 21539, - 21540, - 21541, - 21542, - 21543, - 21544, - 21545, - 21546, - 21547, - 21548, - 21549, - 21550, - 21551, - 21552, - 21553, - 21554, - 21555, - 21556, - 21557, - 21558, - 21559, - 21560, - 21561, - 21562, - 21563, - 21564, - 21565, - 21566, - 21567, - 21568, - 21569, - 21570, - 21571, - 21572, - 21573, - 21574, - 21575, - 21576, - 21577, - 21578, - 21579, - 21580, - 21581, - 21582, - 21583, - 21584, - 21585, - 21586, - 21587, - 21588, - 21589, - 21590, - 21591, - 21592, - 21593, - 21594, - 21595, - 21596, - 21597, - 21598, - 21599, - 21600, - 21601, - 21602, - 21603, - 21604, - 21605, - 21606, - 21607, - 21608, - 21609, - 21610, - 21611, - 21612, - 21613, - 21614, - 21615, - 21616, - 21617, - 21618, - 21619, - 21620, - 21621, - 21622, - 21623, - 21624, - 21625, - 21626, - 21627, - 21628, - 21629, - 21630, - 21631, - 21632, - 21633, - 21634, - 21635, - 21636, - 21637, - 21638, - 21639, - 21640, - 21641, - 21642, - 21643, - 21644, - 21645, - 21646, - 21647, - 21648, - 21649, - 21650, - 21651, - 21652, - 21653, - 21654, - 21655, - 21656, - 21657, - 21658, - 21659, - 21660, - 21661, - 21662, - 21663, - 21664, - 21665, - 21666, - 21667, - 21668, - 21669, - 21670, - 21671, - 21672, - 21673, - 21674, - 21675, - 21676, - 21677, - 21678, - 21679, - 21680, - 21681, - 21682, - 21683, - 21684, - 21685, - 21686, - 21687, - 21688, - 21689, - 21690, - 21691, - 21692, - 21693, - 21694, - 21695, - 21696, - 21697, - 21698, - 21699, - 21700, - 21701, - 21702, - 21703, - 21704, - 21705, - 21706, - 21707, - 21708, - 21709, - 21710, - 21711, - 21712, - 21713, - 21714, - 21715, - 21716, - 21717, - 21718, - 21719, - 21720, - 21721, - 21722, - 21723, - 21724, - 21725, - 21726, - 21727, - 21728, - 21729, - 21730, - 21731, - 21732, - 21733, - 21734, - 21735, - 21736, - 21737, - 21738, - 21739, - 21740, - 21741, - 21742, - 21743, - 21744, - 21745, - 21746, - 21747, - 21748, - 21749, - 21750, - 21751, - 21752, - 21753, - 21754, - 21755, - 21756, - 21757, - 21758, - 21759, - 21760, - 21761, - 21762, - 21763, - 21764, - 21765, - 21766, - 21767, - 21768, - 21769, - 21770, - 21771, - 21772, - 21773, - 21774, - 21775, - 21776, - 21777, - 21778, - 21779, - 21780, - 21781, - 21782, - 21783, - 21784, - 21785, - 21786, - 21787, - 21788, - 21789, - 21790, - 21791, - 21792, - 21793, - 21794, - 21795, - 21796, - 21797, - 21798, - 21799, - 21800, - 21801, - 21802, - 21803, - 21804, - 21805, - 21806, - 21807, - 21808, - 21809, - 21810, - 21811, - 21812, - 21813, - 21814, - 21815, - 21816, - 21817, - 21818, - 21819, - 21820, - 21821, - 21822, - 21823, - 21824, - 21825, - 21826, - 21827, - 21828, - 21829, - 21830, - 21831, - 21832, - 21833, - 21834, - 21835, - 21836, - 21837, - 21838, - 21839, - 21840, - 21841, - 21842, - 21843, - 21844, - 21845, - 21846, - 21847, - 21848, - 21849, - 21850, - 21851, - 21852, - 21853, - 21854, - 21855, - 21856, - 21857, - 21858, - 21859, - 21860, - 21861, - 21862, - 21863, - 21864, - 21865, - 21866, - 21867, - 21868, - 21869, - 21870, - 21871, - 21872, - 21873, - 21874, - 21875, - 21876, - 21877, - 21878, - 21879, - 21880, - 21881, - 21882, - 21883, - 21884, - 21885, - 21886, - 21887, - 21888, - 21889, - 21890, - 21891, - 21892, - 21893, - 21894, - 21895, - 21896, - 21897, - 21898, - 21899, - 21900, - 21901, - 21902, - 21903, - 21904, - 21905, - 21906, - 21907, - 21908, - 21909, - 21910, - 21911, - 21912, - 21913, - 21914, - 21915, - 21916, - 21917, - 21918, - 21919, - 21920, - 21921, - 21922, - 21923, - 21924, - 21925, - 21926, - 21927, - 21928, - 21929, - 21930, - 21931, - 21932, - 21933, - 21934, - 21935, - 21936, - 21937, - 21938, - 21939, - 21940, - 21941, - 21942, - 21943, - 21944, - 21945, - 21946, - 21947, - 21948, - 21949, - 21950, - 21951, - 21952, - 21953, - 21954, - 21955, - 21956, - 21957, - 21958, - 21959, - 21960, - 21961, - 21962, - 21963, - 21964, - 21965, - 21966, - 21967, - 21968, - 21969, - 21970, - 21971, - 21972, - 21973, - 21974, - 21975, - 21976, - 21977, - 21978, - 21979, - 21980, - 21981, - 21982, - 21983, - 21984, - 21985, - 21986, - 21987, - 21988, - 21989, - 21990, - 21991, - 21992, - 21993, - 21994, - 21995, - 21996, - 21997, - 21998, - 21999, - 22000, - 22001, - 22002, - 22003, - 22004, - 22005, - 22006, - 22007, - 22008, - 22009, - 22010, - 22011, - 22012, - 22013, - 22014, - 22015, - 22016, - 22017, - 22018, - 22019, - 22020, - 22021, - 22022, - 22023, - 22024, - 22025, - 22026, - 22027, - 22028, - 22029, - 22030, - 22031, - 22032, - 22033, - 22034, - 22035, - 22036, - 22037, - 22038, - 22039, - 22040, - 22041, - 22042, - 22043, - 22044, - 22045, - 22046, - 22047, - 22048, - 22049, - 22050, - 22051, - 22052, - 22053, - 22054, - 22055, - 22056, - 22057, - 22058, - 22059, - 22060, - 22061, - 22062, - 22063, - 22064, - 22065, - 22066, - 22067, - 22068, - 22069, - 22070, - 22071, - 22072, - 22073, - 22074, - 22075, - 22076, - 22077, - 22078, - 22079, - 22080, - 22081, - 22082, - 22083, - 22084, - 22085, - 22086, - 22087, - 22088, - 22089, - 22090, - 22091, - 22092, - 22093, - 22094, - 22095, - 22096, - 22097, - 22098, - 22099, - 22100, - 22101, - 22102, - 22103, - 22104, - 22105, - 22106, - 22107, - 22108, - 22109, - 22110, - 22111, - 22112, - 22113, - 22114, - 22115, - 22116, - 22117, - 22118, - 22119, - 22120, - 22121, - 22122, - 22123, - 22124, - 22125, - 22126, - 22127, - 22128, - 22129, - 22130, - 22131, - 22132, - 22133, - 22134, - 22135, - 22136, - 22137, - 22138, - 22139, - 22140, - 22141, - 22142, - 22143, - 22144, - 22145, - 22146, - 22147, - 22148, - 22149, - 22150, - 22151, - 22152, - 22153, - 22154, - 22155, - 22156, - 22157, - 22158, - 22159, - 22160, - 22161, - 22162, - 22163, - 22164, - 22165, - 22166, - 22167, - 22168, - 22169, - 22170, - 22171, - 22172, - 22173, - 22174, - 22175, - 22176, - 22177, - 22178, - 22179, - 22180, - 22181, - 22182, - 22183, - 22184, - 22185, - 22186, - 22187, - 22188, - 22189, - 22190, - 22191, - 22192, - 22193, - 22194, - 22195, - 22196, - 22197, - 22198, - 22199, - 22200, - 22201, - 22202, - 22203, - 22204, - 22205, - 22206, - 22207, - 22208, - 22209, - 22210, - 22211, - 22212, - 22213, - 22214, - 22215, - 22216, - 22217, - 22218, - 22219, - 22220, - 22221, - 22222, - 22223, - 22224, - 22225, - 22226, - 22227, - 22228, - 22229, - 22230, - 22231, - 22232, - 22233, - 22234, - 22235, - 22236, - 22237, - 22238, - 22239, - 22240, - 22241, - 22242, - 22243, - 22244, - 22245, - 22246, - 22247, - 22248, - 22249, - 22250, - 22251, - 22252, - 22253, - 22254, - 22255, - 22256, - 22257, - 22258, - 22259, - 22260, - 22261, - 22262, - 22263, - 22264, - 22265, - 22266, - 22267, - 22268, - 22269, - 22270, - 22271, - 22272, - 22273, - 22274, - 22275, - 22276, - 22277, - 22278, - 22279, - 22280, - 22281, - 22282, - 22283, - 22284, - 22285, - 22286, - 22287, - 22288, - 22289, - 22290, - 22291, - 22292, - 22293, - 22294, - 22295, - 22296, - 22297, - 22298, - 22299, - 22300, - 22301, - 22302, - 22303, - 22304, - 22305, - 22306, - 22307, - 22308, - 22309, - 22310, - 22311, - 22312, - 22313, - 22314, - 22315, - 22316, - 22317, - 22318, - 22319, - 22320, - 22321, - 22322, - 22323, - 22324, - 22325, - 22326, - 22327, - 22328, - 22329, - 22330, - 22331, - 22332, - 22333, - 22334, - 22335, - 22336, - 22337, - 22338, - 22339, - 22340, - 22341, - 22342, - 22343, - 22344, - 22345, - 22346, - 22347, - 22348, - 22349, - 22350, - 22351, - 22352, - 22353, - 22354, - 22355, - 22356, - 22357, - 22358, - 22359, - 22360, - 22361, - 22362, - 22363, - 22364, - 22365, - 22366, - 22367, - 22368, - 22369, - 22370, - 22371, - 22372, - 22373, - 22374, - 22375, - 22376, - 22377, - 22378, - 22379, - 22380, - 22381, - 22382, - 22383, - 22384, - 22385, - 22386, - 22387, - 22388, - 22389, - 22390, - 22391, - 22392, - 22393, - 22394, - 22395, - 22396, - 22397, - 22398, - 22399, - 22400, - 22401, - 22402, - 22403, - 22404, - 22405, - 22406, - 22407, - 22408, - 22409, - 22410, - 22411, - 22412, - 22413, - 22414, - 22415, - 22416, - 22417, - 22418, - 22419, - 22420, - 22421, - 22422, - 22423, - 22424, - 22425, - 22426, - 22427, - 22428, - 22429, - 22430, - 22431, - 22432, - 22433, - 22434, - 22435, - 22436, - 22437, - 22438, - 22439, - 22440, - 22441, - 22442, - 22443, - 22444, - 22445, - 22446, - 22447, - 22448, - 22449, - 22450, - 22451, - 22452, - 22453, - 22454, - 22455, - 22456, - 22457, - 22458, - 22459, - 22460, - 22461, - 22462, - 22463, - 22464, - 22465, - 22466, - 22467, - 22468, - 22469, - 22470, - 22471, - 22472, - 22473, - 22474, - 22475, - 22476, - 22477, - 22478, - 22479, - 22480, - 22481, - 22482, - 22483, - 22484, - 22485, - 22486, - 22487, - 22488, - 22489, - 22490, - 22491, - 22492, - 22493, - 22494, - 22495, - 22496, - 22497, - 22498, - 22499, - 22500, - 22501, - 22502, - 22503, - 22504, - 22505, - 22506, - 22507, - 22508, - 22509, - 22510, - 22511, - 22512, - 22513, - 22514, - 22515, - 22516, - 22517, - 22518, - 22519, - 22520, - 22521, - 22522, - 22523, - 22524, - 22525, - 22526, - 22527, - 22528, - 22529, - 22530, - 22531, - 22532, - 22533, - 22534, - 22535, - 22536, - 22537, - 22538, - 22539, - 22540, - 22541, - 22542, - 22543, - 22544, - 22545, - 22546, - 22547, - 22548, - 22549, - 22550, - 22551, - 22552, - 22553, - 22554, - 22555, - 22556, - 22557, - 22558, - 22559, - 22560, - 22561, - 22562, - 22563, - 22564, - 22565, - 22566, - 22567, - 22568, - 22569, - 22570, - 22571, - 22572, - 22573, - 22574, - 22575, - 22576, - 22577, - 22578, - 22579, - 22580, - 22581, - 22582, - 22583, - 22584, - 22585, - 22586, - 22587, - 22588, - 22589, - 22590, - 22591, - 22592, - 22593, - 22594, - 22595, - 22596, - 22597, - 22598, - 22599, - 22600, - 22601, - 22602, - 22603, - 22604, - 22605, - 22606, - 22607, - 22608, - 22609, - 22610, - 22611, - 22612, - 22613, - 22614, - 22615, - 22616, - 22617, - 22618, - 22619, - 22620, - 22621, - 22622, - 22623, - 22624, - 22625, - 22626, - 22627, - 22628, - 22629, - 22630, - 22631, - 22632, - 22633, - 22634, - 22635, - 22636, - 22637, - 22638, - 22639, - 22640, - 22641, - 22642, - 22643, - 22644, - 22645, - 22646, - 22647, - 22648, - 22649, - 22650, - 22651, - 22652, - 22653, - 22654, - 22655, - 22656, - 22657, - 22658, - 22659, - 22660, - 22661, - 22662, - 22663, - 22664, - 22665, - 22666, - 22667, - 22668, - 22669, - 22670, - 22671, - 22672, - 22673, - 22674, - 22675, - 22676, - 22677, - 22678, - 22679, - 22680, - 22681, - 22682, - 22683, - 22684, - 22685, - 22686, - 22687, - 22688, - 22689, - 22690, - 22691, - 22692, - 22693, - 22694, - 22695, - 22696, - 22697, - 22698, - 22699, - 22700, - 22701, - 22702, - 22703, - 22704, - 22705, - 22706, - 22707, - 22708, - 22709, - 22710, - 22711, - 22712, - 22713, - 22714, - 22715, - 22716, - 22717, - 22718, - 22719, - 22720, - 22721, - 22722, - 22723, - 22724, - 22725, - 22726, - 22727, - 22728, - 22729, - 22730, - 22731, - 22732, - 22733, - 22734, - 22735, - 22736, - 22737, - 22738, - 22739, - 22740, - 22741, - 22742, - 22743, - 22744, - 22745, - 22746, - 22747, - 22748, - 22749, - 22750, - 22751, - 22752, - 22753, - 22754, - 22755, - 22756, - 22757, - 22758, - 22759, - 22760, - 22761, - 22762, - 22763, - 22764, - 22765, - 22766, - 22767, - 22768, - 22769, - 22770, - 22771, - 22772, - 22773, - 22774, - 22775, - 22776, - 22777, - 22778, - 22779, - 22780, - 22781, - 22782, - 22783, - 22784, - 22785, - 22786, - 22787, - 22788, - 22789, - 22790, - 22791, - 22792, - 22793, - 22794, - 22795, - 22796, - 22797, - 22798, - 22799, - 22800, - 22801, - 22802, - 22803, - 22804, - 22805, - 22806, - 22807, - 22808, - 22809, - 22810, - 22811, - 22812, - 22813, - 22814, - 22815, - 22816, - 22817, - 22818, - 22819, - 22820, - 22821, - 22822, - 22823, - 22824, - 22825, - 22826, - 22827, - 22828, - 22829, - 22830, - 22831, - 22832, - 22833, - 22834, - 22835, - 22836, - 22837, - 22838, - 22839, - 22840, - 22841, - 22842, - 22843, - 22844, - 22845, - 22846, - 22847, - 22848, - 22849, - 22850, - 22851, - 22852, - 22853, - 22854, - 22855, - 22856, - 22857, - 22858, - 22859, - 22860, - 22861, - 22862, - 22863, - 22864, - 22865, - 22866, - 22867, - 22868, - 22869, - 22870, - 22871, - 22872, - 22873, - 22874, - 22875, - 22876, - 22877, - 22878, - 22879, - 22880, - 22881, - 22882, - 22883, - 22884, - 22885, - 22886, - 22887, - 22888, - 22889, - 22890, - 22891, - 22892, - 22893, - 22894, - 22895, - 22896, - 22897, - 22898, - 22899, - 22900, - 22901, - 22902, - 22903, - 22904, - 22905, - 22906, - 22907, - 22908, - 22909, - 22910, - 22911, - 22912, - 22913, - 22914, - 22915, - 22916, - 22917, - 22918, - 22919, - 22920, - 22921, - 22922, - 22923, - 22924, - 22925, - 22926, - 22927, - 22928, - 22929, - 22930, - 22931, - 22932, - 22933, - 22934, - 22935, - 22936, - 22937, - 22938, - 22939, - 22940, - 22941, - 22942, - 22943, - 22944, - 22945, - 22946, - 22947, - 22948, - 22949, - 22950, - 22951, - 22952, - 22953, - 22954, - 22955, - 22956, - 22957, - 22958, - 22959, - 22960, - 22961, - 22962, - 22963, - 22964, - 22965, - 22966, - 22967, - 22968, - 22969, - 22970, - 22971, - 22972, - 22973, - 22974, - 22975, - 22976, - 22977, - 22978, - 22979, - 22980, - 22981, - 22982, - 22983, - 22984, - 22985, - 22986, - 22987, - 22988, - 22989, - 22990, - 22991, - 22992, - 22993, - 22994, - 22995, - 22996, - 22997, - 22998, - 22999, - 23000, - 23001, - 23002, - 23003, - 23004, - 23005, - 23006, - 23007, - 23008, - 23009, - 23010, - 23011, - 23012, - 23013, - 23014, - 23015, - 23016, - 23017, - 23018, - 23019, - 23020, - 23021, - 23022, - 23023, - 23024, - 23025, - 23026, - 23027, - 23028, - 23029, - 23030, - 23031, - 23032, - 23033, - 23034, - 23035, - 23036, - 23037, - 23038, - 23039, - 23040, - 23041, - 23042, - 23043, - 23044, - 23045, - 23046, - 23047, - 23048, - 23049, - 23050, - 23051, - 23052, - 23053, - 23054, - 23055, - 23056, - 23057, - 23058, - 23059, - 23060, - 23061, - 23062, - 23063, - 23064, - 23065, - 23066, - 23067, - 23068, - 23069, - 23070, - 23071, - 23072, - 23073, - 23074, - 23075, - 23076, - 23077, - 23078, - 23079, - 23080, - 23081, - 23082, - 23083, - 23084, - 23085, - 23086, - 23087, - 23088, - 23089, - 23090, - 23091, - 23092, - 23093, - 23094, - 23095, - 23096, - 23097, - 23098, - 23099, - 23100, - 23101, - 23102, - 23103, - 23104, - 23105, - 23106, - 23107, - 23108, - 23109, - 23110, - 23111, - 23112, - 23113, - 23114, - 23115, - 23116, - 23117, - 23118, - 23119, - 23120, - 23121, - 23122, - 23123, - 23124, - 23125, - 23126, - 23127, - 23128, - 23129, - 23130, - 23131, - 23132, - 23133, - 23134, - 23135, - 23136, - 23137, - 23138, - 23139, - 23140, - 23141, - 23142, - 23143, - 23144, - 23145, - 23146, - 23147, - 23148, - 23149, - 23150, - 23151, - 23152, - 23153, - 23154, - 23155, - 23156, - 23157, - 23158, - 23159, - 23160, - 23161, - 23162, - 23163, - 23164, - 23165, - 23166, - 23167, - 23168, - 23169, - 23170, - 23171, - 23172, - 23173, - 23174, - 23175, - 23176, - 23177, - 23178, - 23179, - 23180, - 23181, - 23182, - 23183, - 23184, - 23185, - 23186, - 23187, - 23188, - 23189, - 23190, - 23191, - 23192, - 23193, - 23194, - 23195, - 23196, - 23197, - 23198, - 23199, - 23200, - 23201, - 23202, - 23203, - 23204, - 23205, - 23206, - 23207, - 23208, - 23209, - 23210, - 23211, - 23212, - 23213, - 23214, - 23215, - 23216, - 23217, - 23218, - 23219, - 23220, - 23221, - 23222, - 23223, - 23224, - 23225, - 23226, - 23227, - 23228, - 23229, - 23230, - 23231, - 23232, - 23233, - 23234, - 23235, - 23236, - 23237, - 23238, - 23239, - 23240, - 23241, - 23242, - 23243, - 23244, - 23245, - 23246, - 23247, - 23248, - 23249, - 23250, - 23251, - 23252, - 23253, - 23254, - 23255, - 23256, - 23257, - 23258, - 23259, - 23260, - 23261, - 23262, - 23263, - 23264, - 23265, - 23266, - 23267, - 23268, - 23269, - 23270, - 23271, - 23272, - 23273, - 23274, - 23275, - 23276, - 23277, - 23278, - 23279, - 23280, - 23281, - 23282, - 23283, - 23284, - 23285, - 23286, - 23287, - 23288, - 23289, - 23290, - 23291, - 23292, - 23293, - 23294, - 23295, - 23296, - 23297, - 23298, - 23299, - 23300, - 23301, - 23302, - 23303, - 23304, - 23305, - 23306, - 23307, - 23308, - 23309, - 23310, - 23311, - 23312, - 23313, - 23314, - 23315, - 23316, - 23317, - 23318, - 23319, - 23320, - 23321, - 23322, - 23323, - 23324, - 23325, - 23326, - 23327, - 23328, - 23329, - 23330, - 23331, - 23332, - 23333, - 23334, - 23335, - 23336, - 23337, - 23338, - 23339, - 23340, - 23341, - 23342, - 23343, - 23344, - 23345, - 23346, - 23347, - 23348, - 23349, - 23350, - 23351, - 23352, - 23353, - 23354, - 23355, - 23356, - 23357, - 23358, - 23359, - 23360, - 23361, - 23362, - 23363, - 23364, - 23365, - 23366, - 23367, - 23368, - 23369, - 23370, - 23371, - 23372, - 23373, - 23374, - 23375, - 23376, - 23377, - 23378, - 23379, - 23380, - 23381, - 23382, - 23383, - 23384, - 23385, - 23386, - 23387, - 23388, - 23389, - 23390, - 23391, - 23392, - 23393, - 23394, - 23395, - 23396, - 23397, - 23398, - 23399, - 23400, - 23401, - 23402, - 23403, - 23404, - 23405, - 23406, - 23407, - 23408, - 23409, - 23410, - 23411, - 23412, - 23413, - 23414, - 23415, - 23416, - 23417, - 23418, - 23419, - 23420, - 23421, - 23422, - 23423, - 23424, - 23425, - 23426, - 23427, - 23428, - 23429, - 23430, - 23431, - 23432, - 23433, - 23434, - 23435, - 23436, - 23437, - 23438, - 23439, - 23440, - 23441, - 23442, - 23443, - 23444, - 23445, - 23446, - 23447, - 23448, - 23449, - 23450, - 23451, - 23452, - 23453, - 23454, - 23455, - 23456, - 23457, - 23458, - 23459, - 23460, - 23461, - 23462, - 23463, - 23464, - 23465, - 23466, - 23467, - 23468, - 23469, - 23470, - 23471, - 23472, - 23473, - 23474, - 23475, - 23476, - 23477, - 23478, - 23479, - 23480, - 23481, - 23482, - 23483, - 23484, - 23485, - 23486, - 23487, - 23488, - 23489, - 23490, - 23491, - 23492, - 23493, - 23494, - 23495, - 23496, - 23497, - 23498, - 23499, - 23500, - 23501, - 23502, - 23503, - 23504, - 23505, - 23506, - 23507, - 23508, - 23509, - 23510, - 23511, - 23512, - 23513, - 23514, - 23515, - 23516, - 23517, - 23518, - 23519, - 23520, - 23521, - 23522, - 23523, - 23524, - 23525, - 23526, - 23527, - 23528, - 23529, - 23530, - 23531, - 23532, - 23533, - 23534, - 23535, - 23536, - 23537, - 23538, - 23539, - 23540, - 23541, - 23542, - 23543, - 23544, - 23545, - 23546, - 23547, - 23548, - 23549, - 23550, - 23551, - 23552, - 23553, - 23554, - 23555, - 23556, - 23557, - 23558, - 23559, - 23560, - 23561, - 23562, - 23563, - 23564, - 23565, - 23566, - 23567, - 23568, - 23569, - 23570, - 23571, - 23572, - 23573, - 23574, - 23575, - 23576, - 23577, - 23578, - 23579, - 23580, - 23581, - 23582, - 23583, - 23584, - 23585, - 23586, - 23587, - 23588, - 23589, - 23590, - 23591, - 23592, - 23593, - 23594, - 23595, - 23596, - 23597, - 23598, - 23599, - 23600, - 23601, - 23602, - 23603, - 23604, - 23605, - 23606, - 23607, - 23608, - 23609, - 23610, - 23611, - 23612, - 23613, - 23614, - 23615, - 23616, - 23617, - 23618, - 23619, - 23620, - 23621, - 23622, - 23623, - 23624, - 23625, - 23626, - 23627, - 23628, - 23629, - 23630, - 23631, - 23632, - 23633, - 23634, - 23635, - 23636, - 23637, - 23638, - 23639, - 23640, - 23641, - 23642, - 23643, - 23644, - 23645, - 23646, - 23647, - 23648, - 23649, - 23650, - 23651, - 23652, - 23653, - 23654, - 23655, - 23656, - 23657, - 23658, - 23659, - 23660, - 23661, - 23662, - 23663, - 23664, - 23665, - 23666, - 23667, - 23668, - 23669, - 23670, - 23671, - 23672, - 23673, - 23674, - 23675, - 23676, - 23677, - 23678, - 23679, - 23680, - 23681, - 23682, - 23683, - 23684, - 23685, - 23686, - 23687, - 23688, - 23689, - 23690, - 23691, - 23692, - 23693, - 23694, - 23695, - 23696, - 23697, - 23698, - 23699, - 23700, - 23701, - 23702, - 23703, - 23704, - 23705, - 23706, - 23707, - 23708, - 23709, - 23710, - 23711, - 23712, - 23713, - 23714, - 23715, - 23716, - 23717, - 23718, - 23719, - 23720, - 23721, - 23722, - 23723, - 23724, - 23725, - 23726, - 23727, - 23728, - 23729, - 23730, - 23731, - 23732, - 23733, - 23734, - 23735, - 23736, - 23737, - 23738, - 23739, - 23740, - 23741, - 23742, - 23743, - 23744, - 23745, - 23746, - 23747, - 23748, - 23749, - 23750, - 23751, - 23752, - 23753, - 23754, - 23755, - 23756, - 23757, - 23758, - 23759, - 23760, - 23761, - 23762, - 23763, - 23764, - 23765, - 23766, - 23767, - 23768, - 23769, - 23770, - 23771, - 23772, - 23773, - 23774, - 23775, - 23776, - 23777, - 23778, - 23779, - 23780, - 23781, - 23782, - 23783, - 23784, - 23785, - 23786, - 23787, - 23788, - 23789, - 23790, - 23791, - 23792, - 23793, - 23794, - 23795, - 23796, - 23797, - 23798, - 23799, - 23800, - 23801, - 23802, - 23803, - 23804, - 23805, - 23806, - 23807, - 23808, - 23809, - 23810, - 23811, - 23812, - 23813, - 23814, - 23815, - 23816, - 23817, - 23818, - 23819, - 23820, - 23821, - 23822, - 23823, - 23824, - 23825, - 23826, - 23827, - 23828, - 23829, - 23830, - 23831, - 23832, - 23833, - 23834, - 23835, - 23836, - 23837, - 23838, - 23839, - 23840, - 23841, - 23842, - 23843, - 23844, - 23845, - 23846, - 23847, - 23848, - 23849, - 23850, - 23851, - 23852, - 23853, - 23854, - 23855, - 23856, - 23857, - 23858, - 23859, - 23860, - 23861, - 23862, - 23863, - 23864, - 23865, - 23866, - 23867, - 23868, - 23869, - 23870, - 23871, - 23872, - 23873, - 23874, - 23875, - 23876, - 23877, - 23878, - 23879, - 23880, - 23881, - 23882, - 23883, - 23884, - 23885, - 23886, - 23887, - 23888, - 23889, - 23890, - 23891, - 23892, - 23893, - 23894, - 23895, - 23896, - 23897, - 23898, - 23899, - 23900, - 23901, - 23902, - 23903, - 23904, - 23905, - 23906, - 23907, - 23908, - 23909, - 23910, - 23911, - 23912, - 23913, - 23914, - 23915, - 23916, - 23917, - 23918, - 23919, - 23920, - 23921, - 23922, - 23923, - 23924, - 23925, - 23926, - 23927, - 23928, - 23929, - 23930, - 23931, - 23932, - 23933, - 23934, - 23935, - 23936, - 23937, - 23938, - 23939, - 23940, - 23941, - 23942, - 23943, - 23944, - 23945, - 23946, - 23947, - 23948, - 23949, - 23950, - 23951, - 23952, - 23953, - 23954, - 23955, - 23956, - 23957, - 23958, - 23959, - 23960, - 23961, - 23962, - 23963, - 23964, - 23965, - 23966, - 23967, - 23968, - 23969, - 23970, - 23971, - 23972, - 23973, - 23974, - 23975, - 23976, - 23977, - 23978, - 23979, - 23980, - 23981, - 23982, - 23983, - 23984, - 23985, - 23986, - 23987, - 23988, - 23989, - 23990, - 23991, - 23992, - 23993, - 23994, - 23995, - 23996, - 23997, - 23998, - 23999, - 24000, - 24001, - 24002, - 24003, - 24004, - 24005, - 24006, - 24007, - 24008, - 24009, - 24010, - 24011, - 24012, - 24013, - 24014, - 24015, - 24016, - 24017, - 24018, - 24019, - 24020, - 24021, - 24022, - 24023, - 24024, - 24025, - 24026, - 24027, - 24028, - 24029, - 24030, - 24031, - 24032, - 24033, - 24034, - 24035, - 24036, - 24037, - 24038, - 24039, - 24040, - 24041, - 24042, - 24043, - 24044, - 24045, - 24046, - 24047, - 24048, - 24049, - 24050, - 24051, - 24052, - 24053, - 24054, - 24055, - 24056, - 24057, - 24058, - 24059, - 24060, - 24061, - 24062, - 24063, - 24064, - 24065, - 24066, - 24067, - 24068, - 24069, - 24070, - 24071, - 24072, - 24073, - 24074, - 24075, - 24076, - 24077, - 24078, - 24079, - 24080, - 24081, - 24082, - 24083, - 24084, - 24085, - 24086, - 24087, - 24088, - 24089, - 24090, - 24091, - 24092, - 24093, - 24094, - 24095, - 24096, - 24097, - 24098, - 24099, - 24100, - 24101, - 24102, - 24103, - 24104, - 24105, - 24106, - 24107, - 24108, - 24109, - 24110, - 24111, - 24112, - 24113, - 24114, - 24115, - 24116, - 24117, - 24118, - 24119, - 24120, - 24121, - 24122, - 24123, - 24124, - 24125, - 24126, - 24127, - 24128, - 24129, - 24130, - 24131, - 24132, - 24133, - 24134, - 24135, - 24136, - 24137, - 24138, - 24139, - 24140, - 24141, - 24142, - 24143, - 24144, - 24145, - 24146, - 24147, - 24148, - 24149, - 24150, - 24151, - 24152, - 24153, - 24154, - 24155, - 24156, - 24157, - 24158, - 24159, - 24160, - 24161, - 24162, - 24163, - 24164, - 24165, - 24166, - 24167, - 24168, - 24169, - 24170, - 24171, - 24172, - 24173, - 24174, - 24175, - 24176, - 24177, - 24178, - 24179, - 24180, - 24181, - 24182, - 24183, - 24184, - 24185, - 24186, - 24187, - 24188, - 24189, - 24190, - 24191, - 24192, - 24193, - 24194, - 24195, - 24196, - 24197, - 24198, - 24199, - 24200, - 24201, - 24202, - 24203, - 24204, - 24205, - 24206, - 24207, - 24208, - 24209, - 24210, - 24211, - 24212, - 24213, - 24214, - 24215, - 24216, - 24217, - 24218, - 24219, - 24220, - 24221, - 24222, - 24223, - 24224, - 24225, - 24226, - 24227, - 24228, - 24229, - 24230, - 24231, - 24232, - 24233, - 24234, - 24235, - 24236, - 24237, - 24238, - 24239, - 24240, - 24241, - 24242, - 24243, - 24244, - 24245, - 24246, - 24247, - 24248, - 24249, - 24250, - 24251, - 24252, - 24253, - 24254, - 24255, - 24256, - 24257, - 24258, - 24259, - 24260, - 24261, - 24262, - 24263, - 24264, - 24265, - 24266, - 24267, - 24268, - 24269, - 24270, - 24271, - 24272, - 24273, - 24274, - 24275, - 24276, - 24277, - 24278, - 24279, - 24280, - 24281, - 24282, - 24283, - 24284, - 24285, - 24286, - 24287, - 24288, - 24289, - 24290, - 24291, - 24292, - 24293, - 24294, - 24295, - 24296, - 24297, - 24298, - 24299, - 24300, - 24301, - 24302, - 24303, - 24304, - 24305, - 24306, - 24307, - 24308, - 24309, - 24310, - 24311, - 24312, - 24313, - 24314, - 24315, - 24316, - 24317, - 24318, - 24319, - 24320, - 24321, - 24322, - 24323, - 24324, - 24325, - 24326, - 24327, - 24328, - 24329, - 24330, - 24331, - 24332, - 24333, - 24334, - 24335, - 24336, - 24337, - 24338, - 24339, - 24340, - 24341, - 24342, - 24343, - 24344, - 24345, - 24346, - 24347, - 24348, - 24349, - 24350, - 24351, - 24352, - 24353, - 24354, - 24355, - 24356, - 24357, - 24358, - 24359, - 24360, - 24361, - 24362, - 24363, - 24364, - 24365, - 24366, - 24367, - 24368, - 24369, - 24370, - 24371, - 24372, - 24373, - 24374, - 24375, - 24376, - 24377, - 24378, - 24379, - 24380, - 24381, - 24382, - 24383, - 24384, - 24385, - 24386, - 24387, - 24388, - 24389, - 24390, - 24391, - 24392, - 24393, - 24394, - 24395, - 24396, - 24397, - 24398, - 24399, - 24400, - 24401, - 24402, - 24403, - 24404, - 24405, - 24406, - 24407, - 24408, - 24409, - 24410, - 24411, - 24412, - 24413, - 24414, - 24415, - 24416, - 24417, - 24418, - 24419, - 24420, - 24421, - 24422, - 24423, - 24424, - 24425, - 24426, - 24427, - 24428, - 24429, - 24430, - 24431, - 24432, - 24433, - 24434, - 24435, - 24436, - 24437, - 24438, - 24439, - 24440, - 24441, - 24442, - 24443, - 24444, - 24445, - 24446, - 24447, - 24448, - 24449, - 24450, - 24451, - 24452, - 24453, - 24454, - 24455, - 24456, - 24457, - 24458, - 24459, - 24460, - 24461, - 24462, - 24463, - 24464, - 24465, - 24466, - 24467, - 24468, - 24469, - 24470, - 24471, - 24472, - 24473, - 24474, - 24475, - 24476, - 24477, - 24478, - 24479, - 24480, - 24481, - 24482, - 24483, - 24484, - 24485, - 24486, - 24487, - 24488, - 24489, - 24490, - 24491, - 24492, - 24493, - 24494, - 24495, - 24496, - 24497, - 24498, - 24499, - 24500, - 24501, - 24502, - 24503, - 24504, - 24505, - 24506, - 24507, - 24508, - 24509, - 24510, - 24511, - 24512, - 24513, - 24514, - 24515, - 24516, - 24517, - 24518, - 24519, - 24520, - 24521, - 24522, - 24523, - 24524, - 24525, - 24526, - 24527, - 24528, - 24529, - 24530, - 24531, - 24532, - 24533, - 24534, - 24535, - 24536, - 24537, - 24538, - 24539, - 24540, - 24541, - 24542, - 24543, - 24544, - 24545, - 24546, - 24547, - 24548, - 24549, - 24550, - 24551, - 24552, - 24553, - 24554, - 24555, - 24556, - 24557, - 24558, - 24559, - 24560, - 24561, - 24562, - 24563, - 24564, - 24565, - 24566, - 24567, - 24568, - 24569, - 24570, - 24571, - 24572, - 24573, - 24574, - 24575, - 24576, - 24577, - 24578, - 24579, - 24580, - 24581, - 24582, - 24583, - 24584, - 24585, - 24586, - 24587, - 24588, - 24589, - 24590, - 24591, - 24592, - 24593, - 24594, - 24595, - 24596, - 24597, - 24598, - 24599, - 24600, - 24601, - 24602, - 24603, - 24604, - 24605, - 24606, - 24607, - 24608, - 24609, - 24610, - 24611, - 24612, - 24613, - 24614, - 24615, - 24616, - 24617, - 24618, - 24619, - 24620, - 24621, - 24622, - 24623, - 24624, - 24625, - 24626, - 24627, - 24628, - 24629, - 24630, - 24631, - 24632, - 24633, - 24634, - 24635, - 24636, - 24637, - 24638, - 24639, - 24640, - 24641, - 24642, - 24643, - 24644, - 24645, - 24646, - 24647, - 24648, - 24649, - 24650, - 24651, - 24652, - 24653, - 24654, - 24655, - 24656, - 24657, - 24658, - 24659, - 24660, - 24661, - 24662, - 24663, - 24664, - 24665, - 24666, - 24667, - 24668, - 24669, - 24670, - 24671, - 24672, - 24673, - 24674, - 24675, - 24676, - 24677, - 24678, - 24679, - 24680, - 24681, - 24682, - 24683, - 24684, - 24685, - 24686, - 24687, - 24688, - 24689, - 24690, - 24691, - 24692, - 24693, - 24694, - 24695, - 24696, - 24697, - 24698, - 24699, - 24700, - 24701, - 24702, - 24703, - 24704, - 24705, - 24706, - 24707, - 24708, - 24709, - 24710, - 24711, - 24712, - 24713, - 24714, - 24715, - 24716, - 24717, - 24718, - 24719, - 24720, - 24721, - 24722, - 24723, - 24724, - 24725, - 24726, - 24727, - 24728, - 24729, - 24730, - 24731, - 24732, - 24733, - 24734, - 24735, - 24736, - 24737, - 24738, - 24739, - 24740, - 24741, - 24742, - 24743, - 24744, - 24745, - 24746, - 24747, - 24748, - 24749, - 24750, - 24751, - 24752, - 24753, - 24754, - 24755, - 24756, - 24757, - 24758, - 24759, - 24760, - 24761, - 24762, - 24763, - 24764, - 24765, - 24766, - 24767, - 24768, - 24769, - 24770, - 24771, - 24772, - 24773, - 24774, - 24775, - 24776, - 24777, - 24778, - 24779, - 24780, - 24781, - 24782, - 24783, - 24784, - 24785, - 24786, - 24787, - 24788, - 24789, - 24790, - 24791, - 24792, - 24793, - 24794, - 24795, - 24796, - 24797, - 24798, - 24799, - 24800, - 24801, - 24802, - 24803, - 24804, - 24805, - 24806, - 24807, - 24808, - 24809, - 24810, - 24811, - 24812, - 24813, - 24814, - 24815, - 24816, - 24817, - 24818, - 24819, - 24820, - 24821, - 24822, - 24823, - 24824, - 24825, - 24826, - 24827, - 24828, - 24829, - 24830, - 24831, - 24832, - 24833, - 24834, - 24835, - 24836, - 24837, - 24838, - 24839, - 24840, - 24841, - 24842, - 24843, - 24844, - 24845, - 24846, - 24847, - 24848, - 24849, - 24850, - 24851, - 24852, - 24853, - 24854, - 24855, - 24856, - 24857, - 24858, - 24859, - 24860, - 24861, - 24862, - 24863, - 24864, - 24865, - 24866, - 24867, - 24868, - 24869, - 24870, - 24871, - 24872, - 24873, - 24874, - 24875, - 24876, - 24877, - 24878, - 24879, - 24880, - 24881, - 24882, - 24883, - 24884, - 24885, - 24886, - 24887, - 24888, - 24889, - 24890, - 24891, - 24892, - 24893, - 24894, - 24895, - 24896, - 24897, - 24898, - 24899, - 24900, - 24901, - 24902, - 24903, - 24904, - 24905, - 24906, - 24907, - 24908, - 24909, - 24910, - 24911, - 24912, - 24913, - 24914, - 24915, - 24916, - 24917, - 24918, - 24919, - 24920, - 24921, - 24922, - 24923, - 24924, - 24925, - 24926, - 24927, - 24928, - 24929, - 24930, - 24931, - 24932, - 24933, - 24934, - 24935, - 24936, - 24937, - 24938, - 24939, - 24940, - 24941, - 24942, - 24943, - 24944, - 24945, - 24946, - 24947, - 24948, - 24949, - 24950, - 24951, - 24952, - 24953, - 24954, - 24955, - 24956, - 24957, - 24958, - 24959, - 24960, - 24961, - 24962, - 24963, - 24964, - 24965, - 24966, - 24967, - 24968, - 24969, - 24970, - 24971, - 24972, - 24973, - 24974, - 24975, - 24976, - 24977, - 24978, - 24979, - 24980, - 24981, - 24982, - 24983, - 24984, - 24985, - 24986, - 24987, - 24988, - 24989, - 24990, - 24991, - 24992, - 24993, - 24994, - 24995, - 24996, - 24997, - 24998, - 24999, - 25000, - 25001, - 25002, - 25003, - 25004, - 25005, - 25006, - 25007, - 25008, - 25009, - 25010, - 25011, - 25012, - 25013, - 25014, - 25015, - 25016, - 25017, - 25018, - 25019, - 25020, - 25021, - 25022, - 25023, - 25024, - 25025, - 25026, - 25027, - 25028, - 25029, - 25030, - 25031, - 25032, - 25033, - 25034, - 25035, - 25036, - 25037, - 25038, - 25039, - 25040, - 25041, - 25042, - 25043, - 25044, - 25045, - 25046, - 25047, - 25048, - 25049, - 25050, - 25051, - 25052, - 25053, - 25054, - 25055, - 25056, - 25057, - 25058, - 25059, - 25060, - 25061, - 25062, - 25063, - 25064, - 25065, - 25066, - 25067, - 25068, - 25069, - 25070, - 25071, - 25072, - 25073, - 25074, - 25075, - 25076, - 25077, - 25078, - 25079, - 25080, - 25081, - 25082, - 25083, - 25084, - 25085, - 25086, - 25087, - 25088, - 25089, - 25090, - 25091, - 25092, - 25093, - 25094, - 25095, - 25096, - 25097, - 25098, - 25099, - 25100, - 25101, - 25102, - 25103, - 25104, - 25105, - 25106, - 25107, - 25108, - 25109, - 25110, - 25111, - 25112, - 25113, - 25114, - 25115, - 25116, - 25117, - 25118, - 25119, - 25120, - 25121, - 25122, - 25123, - 25124, - 25125, - 25126, - 25127, - 25128, - 25129, - 25130, - 25131, - 25132, - 25133, - 25134, - 25135, - 25136, - 25137, - 25138, - 25139, - 25140, - 25141, - 25142, - 25143, - 25144, - 25145, - 25146, - 25147, - 25148, - 25149, - 25150, - 25151, - 25152, - 25153, - 25154, - 25155, - 25156, - 25157, - 25158, - 25159, - 25160, - 25161, - 25162, - 25163, - 25164, - 25165, - 25166, - 25167, - 25168, - 25169, - 25170, - 25171, - 25172, - 25173, - 25174, - 25175, - 25176, - 25177, - 25178, - 25179, - 25180, - 25181, - 25182, - 25183, - 25184, - 25185, - 25186, - 25187, - 25188, - 25189, - 25190, - 25191, - 25192, - 25193, - 25194, - 25195, - 25196, - 25197, - 25198, - 25199, - 25200, - 25201, - 25202, - 25203, - 25204, - 25205, - 25206, - 25207, - 25208, - 25209, - 25210, - 25211, - 25212, - 25213, - 25214, - 25215, - 25216, - 25217, - 25218, - 25219, - 25220, - 25221, - 25222, - 25223, - 25224, - 25225, - 25226, - 25227, - 25228, - 25229, - 25230, - 25231, - 25232, - 25233, - 25234, - 25235, - 25236, - 25237, - 25238, - 25239, - 25240, - 25241, - 25242, - 25243, - 25244, - 25245, - 25246, - 25247, - 25248, - 25249, - 25250, - 25251, - 25252, - 25253, - 25254, - 25255, - 25256, - 25257, - 25258, - 25259, - 25260, - 25261, - 25262, - 25263, - 25264, - 25265, - 25266, - 25267, - 25268, - 25269, - 25270, - 25271, - 25272, - 25273, - 25274, - 25275, - 25276, - 25277, - 25278, - 25279, - 25280, - 25281, - 25282, - 25283, - 25284, - 25285, - 25286, - 25287, - 25288, - 25289, - 25290, - 25291, - 25292, - 25293, - 25294, - 25295, - 25296, - 25297, - 25298, - 25299, - 25300, - 25301, - 25302, - 25303, - 25304, - 25305, - 25306, - 25307, - 25308, - 25309, - 25310, - 25311, - 25312, - 25313, - 25314, - 25315, - 25316, - 25317, - 25318, - 25319, - 25320, - 25321, - 25322, - 25323, - 25324, - 25325, - 25326, - 25327, - 25328, - 25329, - 25330, - 25331, - 25332, - 25333, - 25334, - 25335, - 25336, - 25337, - 25338, - 25339, - 25340, - 25341, - 25342, - 25343, - 25344, - 25345, - 25346, - 25347, - 25348, - 25349, - 25350, - 25351, - 25352, - 25353, - 25354, - 25355, - 25356, - 25357, - 25358, - 25359, - 25360, - 25361, - 25362, - 25363, - 25364, - 25365, - 25366, - 25367, - 25368, - 25369, - 25370, - 25371, - 25372, - 25373, - 25374, - 25375, - 25376, - 25377, - 25378, - 25379, - 25380, - 25381, - 25382, - 25383, - 25384, - 25385, - 25386, - 25387, - 25388, - 25389, - 25390, - 25391, - 25392, - 25393, - 25394, - 25395, - 25396, - 25397, - 25398, - 25399, - 25400, - 25401, - 25402, - 25403, - 25404, - 25405, - 25406, - 25407, - 25408, - 25409, - 25410, - 25411, - 25412, - 25413, - 25414, - 25415, - 25416, - 25417, - 25418, - 25419, - 25420, - 25421, - 25422, - 25423, - 25424, - 25425, - 25426, - 25427, - 25428, - 25429, - 25430, - 25431, - 25432, - 25433, - 25434, - 25435, - 25436, - 25437, - 25438, - 25439, - 25440, - 25441, - 25442, - 25443, - 25444, - 25445, - 25446, - 25447, - 25448, - 25449, - 25450, - 25451, - 25452, - 25453, - 25454, - 25455, - 25456, - 25457, - 25458, - 25459, - 25460, - 25461, - 25462, - 25463, - 25464, - 25465, - 25466, - 25467, - 25468, - 25469, - 25470, - 25471, - 25472, - 25473, - 25474, - 25475, - 25476, - 25477, - 25478, - 25479, - 25480, - 25481, - 25482, - 25483, - 25484, - 25485, - 25486, - 25487, - 25488, - 25489, - 25490, - 25491, - 25492, - 25493, - 25494, - 25495, - 25496, - 25497, - 25498, - 25499, - 25500, - 25501, - 25502, - 25503, - 25504, - 25505, - 25506, - 25507, - 25508, - 25509, - 25510, - 25511, - 25512, - 25513, - 25514, - 25515, - 25516, - 25517, - 25518, - 25519, - 25520, - 25521, - 25522, - 25523, - 25524, - 25525, - 25526, - 25527, - 25528, - 25529, - 25530, - 25531, - 25532, - 25533, - 25534, - 25535, - 25536, - 25537, - 25538, - 25539, - 25540, - 25541, - 25542, - 25543, - 25544, - 25545, - 25546, - 25547, - 25548, - 25549, - 25550, - 25551, - 25552, - 25553, - 25554, - 25555, - 25556, - 25557, - 25558, - 25559, - 25560, - 25561, - 25562, - 25563, - 25564, - 25565, - 25566, - 25567, - 25568, - 25569, - 25570, - 25571, - 25572, - 25573, - 25574, - 25575, - 25576, - 25577, - 25578, - 25579, - 25580, - 25581, - 25582, - 25583, - 25584, - 25585, - 25586, - 25587, - 25588, - 25589, - 25590, - 25591, - 25592, - 25593, - 25594, - 25595, - 25596, - 25597, - 25598, - 25599, - 25600, - 25601, - 25602, - 25603, - 25604, - 25605, - 25606, - 25607, - 25608, - 25609, - 25610, - 25611, - 25612, - 25613, - 25614, - 25615, - 25616, - 25617, - 25618, - 25619, - 25620, - 25621, - 25622, - 25623, - 25624, - 25625, - 25626, - 25627, - 25628, - 25629, - 25630, - 25631, - 25632, - 25633, - 25634, - 25635, - 25636, - 25637, - 25638, - 25639, - 25640, - 25641, - 25642, - 25643, - 25644, - 25645, - 25646, - 25647, - 25648, - 25649, - 25650, - 25651, - 25652, - 25653, - 25654, - 25655, - 25656, - 25657, - 25658, - 25659, - 25660, - 25661, - 25662, - 25663, - 25664, - 25665, - 25666, - 25667, - 25668, - 25669, - 25670, - 25671, - 25672, - 25673, - 25674, - 25675, - 25676, - 25677, - 25678, - 25679, - 25680, - 25681, - 25682, - 25683, - 25684, - 25685, - 25686, - 25687, - 25688, - 25689, - 25690, - 25691, - 25692, - 25693, - 25694, - 25695, - 25696, - 25697, - 25698, - 25699, - 25700, - 25701, - 25702, - 25703, - 25704, - 25705, - 25706, - 25707, - 25708, - 25709, - 25710, - 25711, - 25712, - 25713, - 25714, - 25715, - 25716, - 25717, - 25718, - 25719, - 25720, - 25721, - 25722, - 25723, - 25724, - 25725, - 25726, - 25727, - 25728, - 25729, - 25730, - 25731, - 25732, - 25733, - 25734, - 25735, - 25736, - 25737, - 25738, - 25739, - 25740, - 25741, - 25742, - 25743, - 25744, - 25745, - 25746, - 25747, - 25748, - 25749, - 25750, - 25751, - 25752, - 25753, - 25754, - 25755, - 25756, - 25757, - 25758, - 25759, - 25760, - 25761, - 25762, - 25763, - 25764, - 25765, - 25766, - 25767, - 25768, - 25769, - 25770, - 25771, - 25772, - 25773, - 25774, - 25775, - 25776, - 25777, - 25778, - 25779, - 25780, - 25781, - 25782, - 25783, - 25784, - 25785, - 25786, - 25787, - 25788, - 25789, - 25790, - 25791, - 25792, - 25793, - 25794, - 25795, - 25796, - 25797, - 25798, - 25799, - 25800, - 25801, - 25802, - 25803, - 25804, - 25805, - 25806, - 25807, - 25808, - 25809, - 25810, - 25811, - 25812, - 25813, - 25814, - 25815, - 25816, - 25817, - 25818, - 25819, - 25820, - 25821, - 25822, - 25823, - 25824, - 25825, - 25826, - 25827, - 25828, - 25829, - 25830, - 25831, - 25832, - 25833, - 25834, - 25835, - 25836, - 25837, - 25838, - 25839, - 25840, - 25841, - 25842, - 25843, - 25844, - 25845, - 25846, - 25847, - 25848, - 25849, - 25850, - 25851, - 25852, - 25853, - 25854, - 25855, - 25856, - 25857, - 25858, - 25859, - 25860, - 25861, - 25862, - 25863, - 25864, - 25865, - 25866, - 25867, - 25868, - 25869, - 25870, - 25871, - 25872, - 25873, - 25874, - 25875, - 25876, - 25877, - 25878, - 25879, - 25880, - 25881, - 25882, - 25883, - 25884, - 25885, - 25886, - 25887, - 25888, - 25889, - 25890, - 25891, - 25892, - 25893, - 25894, - 25895, - 25896, - 25897, - 25898, - 25899, - 25900, - 25901, - 25902, - 25903, - 25904, - 25905, - 25906, - 25907, - 25908, - 25909, - 25910, - 25911, - 25912, - 25913, - 25914, - 25915, - 25916, - 25917, - 25918, - 25919, - 25920, - 25921, - 25922, - 25923, - 25924, - 25925, - 25926, - 25927, - 25928, - 25929, - 25930, - 25931, - 25932, - 25933, - 25934, - 25935, - 25936, - 25937, - 25938, - 25939, - 25940, - 25941, - 25942, - 25943, - 25944, - 25945, - 25946, - 25947, - 25948, - 25949, - 25950, - 25951, - 25952, - 25953, - 25954, - 25955, - 25956, - 25957, - 25958, - 25959, - 25960, - 25961, - 25962, - 25963, - 25964, - 25965, - 25966, - 25967, - 25968, - 25969, - 25970, - 25971, - 25972, - 25973, - 25974, - 25975, - 25976, - 25977, - 25978, - 25979, - 25980, - 25981, - 25982, - 25983, - 25984, - 25985, - 25986, - 25987, - 25988, - 25989, - 25990, - 25991, - 25992, - 25993, - 25994, - 25995, - 25996, - 25997, - 25998, - 25999, - 26000, - 26001, - 26002, - 26003, - 26004, - 26005, - 26006, - 26007, - 26008, - 26009, - 26010, - 26011, - 26012, - 26013, - 26014, - 26015, - 26016, - 26017, - 26018, - 26019, - 26020, - 26021, - 26022, - 26023, - 26024, - 26025, - 26026, - 26027, - 26028, - 26029, - 26030, - 26031, - 26032, - 26033, - 26034, - 26035, - 26036, - 26037, - 26038, - 26039, - 26040, - 26041, - 26042, - 26043, - 26044, - 26045, - 26046, - 26047, - 26048, - 26049, - 26050, - 26051, - 26052, - 26053, - 26054, - 26055, - 26056, - 26057, - 26058, - 26059, - 26060, - 26061, - 26062, - 26063, - 26064, - 26065, - 26066, - 26067, - 26068, - 26069, - 26070, - 26071, - 26072, - 26073, - 26074, - 26075, - 26076, - 26077, - 26078, - 26079, - 26080, - 26081, - 26082, - 26083, - 26084, - 26085, - 26086, - 26087, - 26088, - 26089, - 26090, - 26091, - 26092, - 26093, - 26094, - 26095, - 26096, - 26097, - 26098, - 26099, - 26100, - 26101, - 26102, - 26103, - 26104, - 26105, - 26106, - 26107, - 26108, - 26109, - 26110, - 26111, - 26112, - 26113, - 26114, - 26115, - 26116, - 26117, - 26118, - 26119, - 26120, - 26121, - 26122, - 26123, - 26124, - 26125, - 26126, - 26127, - 26128, - 26129, - 26130, - 26131, - 26132, - 26133, - 26134, - 26135, - 26136, - 26137, - 26138, - 26139, - 26140, - 26141, - 26142, - 26143, - 26144, - 26145, - 26146, - 26147, - 26148, - 26149, - 26150, - 26151, - 26152, - 26153, - 26154, - 26155, - 26156, - 26157, - 26158, - 26159, - 26160, - 26161, - 26162, - 26163, - 26164, - 26165, - 26166, - 26167, - 26168, - 26169, - 26170, - 26171, - 26172, - 26173, - 26174, - 26175, - 26176, - 26177, - 26178, - 26179, - 26180, - 26181, - 26182, - 26183, - 26184, - 26185, - 26186, - 26187, - 26188, - 26189, - 26190, - 26191, - 26192, - 26193, - 26194, - 26195, - 26196, - 26197, - 26198, - 26199, - 26200, - 26201, - 26202, - 26203, - 26204, - 26205, - 26206, - 26207, - 26208, - 26209, - 26210, - 26211, - 26212, - 26213, - 26214, - 26215, - 26216, - 26217, - 26218, - 26219, - 26220, - 26221, - 26222, - 26223, - 26224, - 26225, - 26226, - 26227, - 26228, - 26229, - 26230, - 26231, - 26232, - 26233, - 26234, - 26235, - 26236, - 26237, - 26238, - 26239, - 26240, - 26241, - 26242, - 26243, - 26244, - 26245, - 26246, - 26247, - 26248, - 26249, - 26250, - 26251, - 26252, - 26253, - 26254, - 26255, - 26256, - 26257, - 26258, - 26259, - 26260, - 26261, - 26262, - 26263, - 26264, - 26265, - 26266, - 26267, - 26268, - 26269, - 26270, - 26271, - 26272, - 26273, - 26274, - 26275, - 26276, - 26277, - 26278, - 26279, - 26280, - 26281, - 26282, - 26283, - 26284, - 26285, - 26286, - 26287, - 26288, - 26289, - 26290, - 26291, - 26292, - 26293, - 26294, - 26295, - 26296, - 26297, - 26298, - 26299, - 26300, - 26301, - 26302, - 26303, - 26304, - 26305, - 26306, - 26307, - 26308, - 26309, - 26310, - 26311, - 26312, - 26313, - 26314, - 26315, - 26316, - 26317, - 26318, - 26319, - 26320, - 26321, - 26322, - 26323, - 26324, - 26325, - 26326, - 26327, - 26328, - 26329, - 26330, - 26331, - 26332, - 26333, - 26334, - 26335, - 26336, - 26337, - 26338, - 26339, - 26340, - 26341, - 26342, - 26343, - 26344, - 26345, - 26346, - 26347, - 26348, - 26349, - 26350, - 26351, - 26352, - 26353, - 26354, - 26355, - 26356, - 26357, - 26358, - 26359, - 26360, - 26361, - 26362, - 26363, - 26364, - 26365, - 26366, - 26367, - 26368, - 26369, - 26370, - 26371, - 26372, - 26373, - 26374, - 26375, - 26376, - 26377, - 26378, - 26379, - 26380, - 26381, - 26382, - 26383, - 26384, - 26385, - 26386, - 26387, - 26388, - 26389, - 26390, - 26391, - 26392, - 26393, - 26394, - 26395, - 26396, - 26397, - 26398, - 26399, - 26400, - 26401, - 26402, - 26403, - 26404, - 26405, - 26406, - 26407, - 26408, - 26409, - 26410, - 26411, - 26412, - 26413, - 26414, - 26415, - 26416, - 26417, - 26418, - 26419, - 26420, - 26421, - 26422, - 26423, - 26424, - 26425, - 26426, - 26427, - 26428, - 26429, - 26430, - 26431, - 26432, - 26433, - 26434, - 26435, - 26436, - 26437, - 26438, - 26439, - 26440, - 26441, - 26442, - 26443, - 26444, - 26445, - 26446, - 26447, - 26448, - 26449, - 26450, - 26451, - 26452, - 26453, - 26454, - 26455, - 26456, - 26457, - 26458, - 26459, - 26460, - 26461, - 26462, - 26463, - 26464, - 26465, - 26466, - 26467, - 26468, - 26469, - 26470, - 26471, - 26472, - 26473, - 26474, - 26475, - 26476, - 26477, - 26478, - 26479, - 26480, - 26481, - 26482, - 26483, - 26484, - 26485, - 26486, - 26487, - 26488, - 26489, - 26490, - 26491, - 26492, - 26493, - 26494, - 26495, - 26496, - 26497, - 26498, - 26499, - 26500, - 26501, - 26502, - 26503, - 26504, - 26505, - 26506, - 26507, - 26508, - 26509, - 26510, - 26511, - 26512, - 26513, - 26514, - 26515, - 26516, - 26517, - 26518, - 26519, - 26520, - 26521, - 26522, - 26523, - 26524, - 26525, - 26526, - 26527, - 26528, - 26529, - 26530, - 26531, - 26532, - 26533, - 26534, - 26535, - 26536, - 26537, - 26538, - 26539, - 26540, - 26541, - 26542, - 26543, - 26544, - 26545, - 26546, - 26547, - 26548, - 26549, - 26550, - 26551, - 26552, - 26553, - 26554, - 26555, - 26556, - 26557, - 26558, - 26559, - 26560, - 26561, - 26562, - 26563, - 26564, - 26565, - 26566, - 26567, - 26568, - 26569, - 26570, - 26571, - 26572, - 26573, - 26574, - 26575, - 26576, - 26577, - 26578, - 26579, - 26580, - 26581, - 26582, - 26583, - 26584, - 26585, - 26586, - 26587, - 26588, - 26589, - 26590, - 26591, - 26592, - 26593, - 26594, - 26595, - 26596, - 26597, - 26598, - 26599, - 26600, - 26601, - 26602, - 26603, - 26604, - 26605, - 26606, - 26607, - 26608, - 26609, - 26610, - 26611, - 26612, - 26613, - 26614, - 26615, - 26616, - 26617, - 26618, - 26619, - 26620, - 26621, - 26622, - 26623, - 26624, - 26625, - 26626, - 26627, - 26628, - 26629, - 26630, - 26631, - 26632, - 26633, - 26634, - 26635, - 26636, - 26637, - 26638, - 26639, - 26640, - 26641, - 26642, - 26643, - 26644, - 26645, - 26646, - 26647, - 26648, - 26649, - 26650, - 26651, - 26652, - 26653, - 26654, - 26655, - 26656, - 26657, - 26658, - 26659, - 26660, - 26661, - 26662, - 26663, - 26664, - 26665, - 26666, - 26667, - 26668, - 26669, - 26670, - 26671, - 26672, - 26673, - 26674, - 26675, - 26676, - 26677, - 26678, - 26679, - 26680, - 26681, - 26682, - 26683, - 26684, - 26685, - 26686, - 26687, - 26688, - 26689, - 26690, - 26691, - 26692, - 26693, - 26694, - 26695, - 26696, - 26697, - 26698, - 26699, - 26700, - 26701, - 26702, - 26703, - 26704, - 26705, - 26706, - 26707, - 26708, - 26709, - 26710, - 26711, - 26712, - 26713, - 26714, - 26715, - 26716, - 26717, - 26718, - 26719, - 26720, - 26721, - 26722, - 26723, - 26724, - 26725, - 26726, - 26727, - 26728, - 26729, - 26730, - 26731, - 26732, - 26733, - 26734, - 26735, - 26736, - 26737, - 26738, - 26739, - 26740, - 26741, - 26742, - 26743, - 26744, - 26745, - 26746, - 26747, - 26748, - 26749, - 26750, - 26751, - 26752, - 26753, - 26754, - 26755, - 26756, - 26757, - 26758, - 26759, - 26760, - 26761, - 26762, - 26763, - 26764, - 26765, - 26766, - 26767, - 26768, - 26769, - 26770, - 26771, - 26772, - 26773, - 26774, - 26775, - 26776, - 26777, - 26778, - 26779, - 26780, - 26781, - 26782, - 26783, - 26784, - 26785, - 26786, - 26787, - 26788, - 26789, - 26790, - 26791, - 26792, - 26793, - 26794, - 26795, - 26796, - 26797, - 26798, - 26799, - 26800, - 26801, - 26802, - 26803, - 26804, - 26805, - 26806, - 26807, - 26808, - 26809, - 26810, - 26811, - 26812, - 26813, - 26814, - 26815, - 26816, - 26817, - 26818, - 26819, - 26820, - 26821, - 26822, - 26823, - 26824, - 26825, - 26826, - 26827, - 26828, - 26829, - 26830, - 26831, - 26832, - 26833, - 26834, - 26835, - 26836, - 26837, - 26838, - 26839, - 26840, - 26841, - 26842, - 26843, - 26844, - 26845, - 26846, - 26847, - 26848, - 26849, - 26850, - 26851, - 26852, - 26853, - 26854, - 26855, - 26856, - 26857, - 26858, - 26859, - 26860, - 26861, - 26862, - 26863, - 26864, - 26865, - 26866, - 26867, - 26868, - 26869, - 26870, - 26871, - 26872, - 26873, - 26874, - 26875, - 26876, - 26877, - 26878, - 26879, - 26880, - 26881, - 26882, - 26883, - 26884, - 26885, - 26886, - 26887, - 26888, - 26889, - 26890, - 26891, - 26892, - 26893, - 26894, - 26895, - 26896, - 26897, - 26898, - 26899, - 26900, - 26901, - 26902, - 26903, - 26904, - 26905, - 26906, - 26907, - 26908, - 26909, - 26910, - 26911, - 26912, - 26913, - 26914, - 26915, - 26916, - 26917, - 26918, - 26919, - 26920, - 26921, - 26922, - 26923, - 26924, - 26925, - 26926, - 26927, - 26928, - 26929, - 26930, - 26931, - 26932, - 26933, - 26934, - 26935, - 26936, - 26937, - 26938, - 26939, - 26940, - 26941, - 26942, - 26943, - 26944, - 26945, - 26946, - 26947, - 26948, - 26949, - 26950, - 26951, - 26952, - 26953, - 26954, - 26955, - 26956, - 26957, - 26958, - 26959, - 26960, - 26961, - 26962, - 26963, - 26964, - 26965, - 26966, - 26967, - 26968, - 26969, - 26970, - 26971, - 26972, - 26973, - 26974, - 26975, - 26976, - 26977, - 26978, - 26979, - 26980, - 26981, - 26982, - 26983, - 26984, - 26985, - 26986, - 26987, - 26988, - 26989, - 26990, - 26991, - 26992, - 26993, - 26994, - 26995, - 26996, - 26997, - 26998, - 26999, - 27000, - 27001, - 27002, - 27003, - 27004, - 27005, - 27006, - 27007, - 27008, - 27009, - 27010, - 27011, - 27012, - 27013, - 27014, - 27015, - 27016, - 27017, - 27018, - 27019, - 27020, - 27021, - 27022, - 27023, - 27024, - 27025, - 27026, - 27027, - 27028, - 27029, - 27030, - 27031, - 27032, - 27033, - 27034, - 27035, - 27036, - 27037, - 27038, - 27039, - 27040, - 27041, - 27042, - 27043, - 27044, - 27045, - 27046, - 27047, - 27048, - 27049, - 27050, - 27051, - 27052, - 27053, - 27054, - 27055, - 27056, - 27057, - 27058, - 27059, - 27060, - 27061, - 27062, - 27063, - 27064, - 27065, - 27066, - 27067, - 27068, - 27069, - 27070, - 27071, - 27072, - 27073, - 27074, - 27075, - 27076, - 27077, - 27078, - 27079, - 27080, - 27081, - 27082, - 27083, - 27084, - 27085, - 27086, - 27087, - 27088, - 27089, - 27090, - 27091, - 27092, - 27093, - 27094, - 27095, - 27096, - 27097, - 27098, - 27099, - 27100, - 27101, - 27102, - 27103, - 27104, - 27105, - 27106, - 27107, - 27108, - 27109, - 27110, - 27111, - 27112, - 27113, - 27114, - 27115, - 27116, - 27117, - 27118, - 27119, - 27120, - 27121, - 27122, - 27123, - 27124, - 27125, - 27126, - 27127, - 27128, - 27129, - 27130, - 27131, - 27132, - 27133, - 27134, - 27135, - 27136, - 27137, - 27138, - 27139, - 27140, - 27141, - 27142, - 27143, - 27144, - 27145, - 27146, - 27147, - 27148, - 27149, - 27150, - 27151, - 27152, - 27153, - 27154, - 27155, - 27156, - 27157, - 27158, - 27159, - 27160, - 27161, - 27162, - 27163, - 27164, - 27165, - 27166, - 27167, - 27168, - 27169, - 27170, - 27171, - 27172, - 27173, - 27174, - 27175, - 27176, - 27177, - 27178, - 27179, - 27180, - 27181, - 27182, - 27183, - 27184, - 27185, - 27186, - 27187, - 27188, - 27189, - 27190, - 27191, - 27192, - 27193, - 27194, - 27195, - 27196, - 27197, - 27198, - 27199, - 27200, - 27201, - 27202, - 27203, - 27204, - 27205, - 27206, - 27207, - 27208, - 27209, - 27210, - 27211, - 27212, - 27213, - 27214, - 27215, - 27216, - 27217, - 27218, - 27219, - 27220, - 27221, - 27222, - 27223, - 27224, - 27225, - 27226, - 27227, - 27228, - 27229, - 27230, - 27231, - 27232, - 27233, - 27234, - 27235, - 27236, - 27237, - 27238, - 27239, - 27240, - 27241, - 27242, - 27243, - 27244, - 27245, - 27246, - 27247, - 27248, - 27249, - 27250, - 27251, - 27252, - 27253, - 27254, - 27255, - 27256, - 27257, - 27258, - 27259, - 27260, - 27261, - 27262, - 27263, - 27264, - 27265, - 27266, - 27267, - 27268, - 27269, - 27270, - 27271, - 27272, - 27273, - 27274, - 27275, - 27276, - 27277, - 27278, - 27279, - 27280, - 27281, - 27282, - 27283, - 27284, - 27285, - 27286, - 27287, - 27288, - 27289, - 27290, - 27291, - 27292, - 27293, - 27294, - 27295, - 27296, - 27297, - 27298, - 27299, - 27300, - 27301, - 27302, - 27303, - 27304, - 27305, - 27306, - 27307, - 27308, - 27309, - 27310, - 27311, - 27312, - 27313, - 27314, - 27315, - 27316, - 27317, - 27318, - 27319, - 27320, - 27321, - 27322, - 27323, - 27324, - 27325, - 27326, - 27327, - 27328, - 27329, - 27330, - 27331, - 27332, - 27333, - 27334, - 27335, - 27336, - 27337, - 27338, - 27339, - 27340, - 27341, - 27342, - 27343, - 27344, - 27345, - 27346, - 27347, - 27348, - 27349, - 27350, - 27351, - 27352, - 27353, - 27354, - 27355, - 27356, - 27357, - 27358, - 27359, - 27360, - 27361, - 27362, - 27363, - 27364, - 27365, - 27366, - 27367, - 27368, - 27369, - 27370, - 27371, - 27372, - 27373, - 27374, - 27375, - 27376, - 27377, - 27378, - 27379, - 27380, - 27381, - 27382, - 27383, - 27384, - 27385, - 27386, - 27387, - 27388, - 27389, - 27390, - 27391, - 27392, - 27393, - 27394, - 27395, - 27396, - 27397, - 27398, - 27399, - 27400, - 27401, - 27402, - 27403, - 27404, - 27405, - 27406, - 27407, - 27408, - 27409, - 27410, - 27411, - 27412, - 27413, - 27414, - 27415, - 27416, - 27417, - 27418, - 27419, - 27420, - 27421, - 27422, - 27423, - 27424, - 27425, - 27426, - 27427, - 27428, - 27429, - 27430, - 27431, - 27432, - 27433, - 27434, - 27435, - 27436, - 27437, - 27438, - 27439, - 27440, - 27441, - 27442, - 27443, - 27444, - 27445, - 27446, - 27447, - 27448, - 27449, - 27450, - 27451, - 27452, - 27453, - 27454, - 27455, - 27456, - 27457, - 27458, - 27459, - 27460, - 27461, - 27462, - 27463, - 27464, - 27465, - 27466, - 27467, - 27468, - 27469, - 27470, - 27471, - 27472, - 27473, - 27474, - 27475, - 27476, - 27477, - 27478, - 27479, - 27480, - 27481, - 27482, - 27483, - 27484, - 27485, - 27486, - 27487, - 27488, - 27489, - 27490, - 27491, - 27492, - 27493, - 27494, - 27495, - 27496, - 27497, - 27498, - 27499, - 27500, - 27501, - 27502, - 27503, - 27504, - 27505, - 27506, - 27507, - 27508, - 27509, - 27510, - 27511, - 27512, - 27513, - 27514, - 27515, - 27516, - 27517, - 27518, - 27519, - 27520, - 27521, - 27522, - 27523, - 27524, - 27525, - 27526, - 27527, - 27528, - 27529, - 27530, - 27531, - 27532, - 27533, - 27534, - 27535, - 27536, - 27537, - 27538, - 27539, - 27540, - 27541, - 27542, - 27543, - 27544, - 27545, - 27546, - 27547, - 27548, - 27549, - 27550, - 27551, - 27552, - 27553, - 27554, - 27555, - 27556, - 27557, - 27558, - 27559, - 27560, - 27561, - 27562, - 27563, - 27564, - 27565, - 27566, - 27567, - 27568, - 27569, - 27570, - 27571, - 27572, - 27573, - 27574, - 27575, - 27576, - 27577, - 27578, - 27579, - 27580, - 27581, - 27582, - 27583, - 27584, - 27585, - 27586, - 27587, - 27588, - 27589, - 27590, - 27591, - 27592, - 27593, - 27594, - 27595, - 27596, - 27597, - 27598, - 27599, - 27600, - 27601, - 27602, - 27603, - 27604, - 27605, - 27606, - 27607, - 27608, - 27609, - 27610, - 27611, - 27612, - 27613, - 27614, - 27615, - 27616, - 27617, - 27618, - 27619, - 27620, - 27621, - 27622, - 27623, - 27624, - 27625, - 27626, - 27627, - 27628, - 27629, - 27630, - 27631, - 27632, - 27633, - 27634, - 27635, - 27636, - 27637, - 27638, - 27639, - 27640, - 27641, - 27642, - 27643, - 27644, - 27645, - 27646, - 27647, - 27648, - 27649, - 27650, - 27651, - 27652, - 27653, - 27654, - 27655, - 27656, - 27657, - 27658, - 27659, - 27660, - 27661, - 27662, - 27663, - 27664, - 27665, - 27666, - 27667, - 27668, - 27669, - 27670, - 27671, - 27672, - 27673, - 27674, - 27675, - 27676, - 27677, - 27678, - 27679, - 27680, - 27681, - 27682, - 27683, - 27684, - 27685, - 27686, - 27687, - 27688, - 27689, - 27690, - 27691, - 27692, - 27693, - 27694, - 27695, - 27696, - 27697, - 27698, - 27699, - 27700, - 27701, - 27702, - 27703, - 27704, - 27705, - 27706, - 27707, - 27708, - 27709, - 27710, - 27711, - 27712, - 27713, - 27714, - 27715, - 27716, - 27717, - 27718, - 27719, - 27720, - 27721, - 27722, - 27723, - 27724, - 27725, - 27726, - 27727, - 27728, - 27729, - 27730, - 27731, - 27732, - 27733, - 27734, - 27735, - 27736, - 27737, - 27738, - 27739, - 27740, - 27741, - 27742, - 27743, - 27744, - 27745, - 27746, - 27747, - 27748, - 27749, - 27750, - 27751, - 27752, - 27753, - 27754, - 27755, - 27756, - 27757, - 27758, - 27759, - 27760, - 27761, - 27762, - 27763, - 27764, - 27765, - 27766, - 27767, - 27768, - 27769, - 27770, - 27771, - 27772, - 27773, - 27774, - 27775, - 27776, - 27777, - 27778, - 27779, - 27780, - 27781, - 27782, - 27783, - 27784, - 27785, - 27786, - 27787, - 27788, - 27789, - 27790, - 27791, - 27792, - 27793, - 27794, - 27795, - 27796, - 27797, - 27798, - 27799, - 27800, - 27801, - 27802, - 27803, - 27804, - 27805, - 27806, - 27807, - 27808, - 27809, - 27810, - 27811, - 27812, - 27813, - 27814, - 27815, - 27816, - 27817, - 27818, - 27819, - 27820, - 27821, - 27822, - 27823, - 27824, - 27825, - 27826, - 27827, - 27828, - 27829, - 27830, - 27831, - 27832, - 27833, - 27834, - 27835, - 27836, - 27837, - 27838, - 27839, - 27840, - 27841, - 27842, - 27843, - 27844, - 27845, - 27846, - 27847, - 27848, - 27849, - 27850, - 27851, - 27852, - 27853, - 27854, - 27855, - 27856, - 27857, - 27858, - 27859, - 27860, - 27861, - 27862, - 27863, - 27864, - 27865, - 27866, - 27867, - 27868, - 27869, - 27870, - 27871, - 27872, - 27873, - 27874, - 27875, - 27876, - 27877, - 27878, - 27879, - 27880, - 27881, - 27882, - 27883, - 27884, - 27885, - 27886, - 27887, - 27888, - 27889, - 27890, - 27891, - 27892, - 27893, - 27894, - 27895, - 27896, - 27897, - 27898, - 27899, - 27900, - 27901, - 27902, - 27903, - 27904, - 27905, - 27906, - 27907, - 27908, - 27909, - 27910, - 27911, - 27912, - 27913, - 27914, - 27915, - 27916, - 27917, - 27918, - 27919, - 27920, - 27921, - 27922, - 27923, - 27924, - 27925, - 27926, - 27927, - 27928, - 27929, - 27930, - 27931, - 27932, - 27933, - 27934, - 27935, - 27936, - 27937, - 27938, - 27939, - 27940, - 27941, - 27942, - 27943, - 27944, - 27945, - 27946, - 27947, - 27948, - 27949, - 27950, - 27951, - 27952, - 27953, - 27954, - 27955, - 27956, - 27957, - 27958, - 27959, - 27960, - 27961, - 27962, - 27963, - 27964, - 27965, - 27966, - 27967, - 27968, - 27969, - 27970, - 27971, - 27972, - 27973, - 27974, - 27975, - 27976, - 27977, - 27978, - 27979, - 27980, - 27981, - 27982, - 27983, - 27984, - 27985, - 27986, - 27987, - 27988, - 27989, - 27990, - 27991, - 27992, - 27993, - 27994, - 27995, - 27996, - 27997, - 27998, - 27999, - 28000, - 28001, - 28002, - 28003, - 28004, - 28005, - 28006, - 28007, - 28008, - 28009, - 28010, - 28011, - 28012, - 28013, - 28014, - 28015, - 28016, - 28017, - 28018, - 28019, - 28020, - 28021, - 28022, - 28023, - 28024, - 28025, - 28026, - 28027, - 28028, - 28029, - 28030, - 28031, - 28032, - 28033, - 28034, - 28035, - 28036, - 28037, - 28038, - 28039, - 28040, - 28041, - 28042, - 28043, - 28044, - 28045, - 28046, - 28047, - 28048, - 28049, - 28050, - 28051, - 28052, - 28053, - 28054, - 28055, - 28056, - 28057, - 28058, - 28059, - 28060, - 28061, - 28062, - 28063, - 28064, - 28065, - 28066, - 28067, - 28068, - 28069, - 28070, - 28071, - 28072, - 28073, - 28074, - 28075, - 28076, - 28077, - 28078, - 28079, - 28080, - 28081, - 28082, - 28083, - 28084, - 28085, - 28086, - 28087, - 28088, - 28089, - 28090, - 28091, - 28092, - 28093, - 28094, - 28095, - 28096, - 28097, - 28098, - 28099, - 28100, - 28101, - 28102, - 28103, - 28104, - 28105, - 28106, - 28107, - 28108, - 28109, - 28110, - 28111, - 28112, - 28113, - 28114, - 28115, - 28116, - 28117, - 28118, - 28119, - 28120, - 28121, - 28122, - 28123, - 28124, - 28125, - 28126, - 28127, - 28128, - 28129, - 28130, - 28131, - 28132, - 28133, - 28134, - 28135, - 28136, - 28137, - 28138, - 28139, - 28140, - 28141, - 28142, - 28143, - 28144, - 28145, - 28146, - 28147, - 28148, - 28149, - 28150, - 28151, - 28152, - 28153, - 28154, - 28155, - 28156, - 28157, - 28158, - 28159, - 28160, - 28161, - 28162, - 28163, - 28164, - 28165, - 28166, - 28167, - 28168, - 28169, - 28170, - 28171, - 28172, - 28173, - 28174, - 28175, - 28176, - 28177, - 28178, - 28179, - 28180, - 28181, - 28182, - 28183, - 28184, - 28185, - 28186, - 28187, - 28188, - 28189, - 28190, - 28191, - 28192, - 28193, - 28194, - 28195, - 28196, - 28197, - 28198, - 28199, - 28200, - 28201, - 28202, - 28203, - 28204, - 28205, - 28206, - 28207, - 28208, - 28209, - 28210, - 28211, - 28212, - 28213, - 28214, - 28215, - 28216, - 28217, - 28218, - 28219, - 28220, - 28221, - 28222, - 28223, - 28224, - 28225, - 28226, - 28227, - 28228, - 28229, - 28230, - 28231, - 28232, - 28233, - 28234, - 28235, - 28236, - 28237, - 28238, - 28239, - 28240, - 28241, - 28242, - 28243, - 28244, - 28245, - 28246, - 28247, - 28248, - 28249, - 28250, - 28251, - 28252, - 28253, - 28254, - 28255, - 28256, - 28257, - 28258, - 28259, - 28260, - 28261, - 28262, - 28263, - 28264, - 28265, - 28266, - 28267, - 28268, - 28269, - 28270, - 28271, - 28272, - 28273, - 28274, - 28275, - 28276, - 28277, - 28278, - 28279, - 28280, - 28281, - 28282, - 28283, - 28284, - 28285, - 28286, - 28287, - 28288, - 28289, - 28290, - 28291, - 28292, - 28293, - 28294, - 28295, - 28296, - 28297, - 28298, - 28299, - 28300, - 28301, - 28302, - 28303, - 28304, - 28305, - 28306, - 28307, - 28308, - 28309, - 28310, - 28311, - 28312, - 28313, - 28314, - 28315, - 28316, - 28317, - 28318, - 28319, - 28320, - 28321, - 28322, - 28323, - 28324, - 28325, - 28326, - 28327, - 28328, - 28329, - 28330, - 28331, - 28332, - 28333, - 28334, - 28335, - 28336, - 28337, - 28338, - 28339, - 28340, - 28341, - 28342, - 28343, - 28344, - 28345, - 28346, - 28347, - 28348, - 28349, - 28350, - 28351, - 28352, - 28353, - 28354, - 28355, - 28356, - 28357, - 28358, - 28359, - 28360, - 28361, - 28362, - 28363, - 28364, - 28365, - 28366, - 28367, - 28368, - 28369, - 28370, - 28371, - 28372, - 28373, - 28374, - 28375, - 28376, - 28377, - 28378, - 28379, - 28380, - 28381, - 28382, - 28383, - 28384, - 28385, - 28386, - 28387, - 28388, - 28389, - 28390, - 28391, - 28392, - 28393, - 28394, - 28395, - 28396, - 28397, - 28398, - 28399, - 28400, - 28401, - 28402, - 28403, - 28404, - 28405, - 28406, - 28407, - 28408, - 28409, - 28410, - 28411, - 28412, - 28413, - 28414, - 28415, - 28416, - 28417, - 28418, - 28419, - 28420, - 28421, - 28422, - 28423, - 28424, - 28425, - 28426, - 28427, - 28428, - 28429, - 28430, - 28431, - 28432, - 28433, - 28434, - 28435, - 28436, - 28437, - 28438, - 28439, - 28440, - 28441, - 28442, - 28443, - 28444, - 28445, - 28446, - 28447, - 28448, - 28449, - 28450, - 28451, - 28452, - 28453, - 28454, - 28455, - 28456, - 28457, - 28458, - 28459, - 28460, - 28461, - 28462, - 28463, - 28464, - 28465, - 28466, - 28467, - 28468, - 28469, - 28470, - 28471, - 28472, - 28473, - 28474, - 28475, - 28476, - 28477, - 28478, - 28479, - 28480, - 28481, - 28482, - 28483, - 28484, - 28485, - 28486, - 28487, - 28488, - 28489, - 28490, - 28491, - 28492, - 28493, - 28494, - 28495, - 28496, - 28497, - 28498, - 28499, - 28500, - 28501, - 28502, - 28503, - 28504, - 28505, - 28506, - 28507, - 28508, - 28509, - 28510, - 28511, - 28512, - 28513, - 28514, - 28515, - 28516, - 28517, - 28518, - 28519, - 28520, - 28521, - 28522, - 28523, - 28524, - 28525, - 28526, - 28527, - 28528, - 28529, - 28530, - 28531, - 28532, - 28533, - 28534, - 28535, - 28536, - 28537, - 28538, - 28539, - 28540, - 28541, - 28542, - 28543, - 28544, - 28545, - 28546, - 28547, - 28548, - 28549, - 28550, - 28551, - 28552, - 28553, - 28554, - 28555, - 28556, - 28557, - 28558, - 28559, - 28560, - 28561, - 28562, - 28563, - 28564, - 28565, - 28566, - 28567, - 28568, - 28569, - 28570, - 28571, - 28572, - 28573, - 28574, - 28575, - 28576, - 28577, - 28578, - 28579, - 28580, - 28581, - 28582, - 28583, - 28584, - 28585, - 28586, - 28587, - 28588, - 28589, - 28590, - 28591, - 28592, - 28593, - 28594, - 28595, - 28596, - 28597, - 28598, - 28599, - 28600, - 28601, - 28602, - 28603, - 28604, - 28605, - 28606, - 28607, - 28608, - 28609, - 28610, - 28611, - 28612, - 28613, - 28614, - 28615, - 28616, - 28617, - 28618, - 28619, - 28620, - 28621, - 28622, - 28623, - 28624, - 28625, - 28626, - 28627, - 28628, - 28629, - 28630, - 28631, - 28632, - 28633, - 28634, - 28635, - 28636, - 28637, - 28638, - 28639, - 28640, - 28641, - 28642, - 28643, - 28644, - 28645, - 28646, - 28647, - 28648, - 28649, - 28650, - 28651, - 28652, - 28653, - 28654, - 28655, - 28656, - 28657, - 28658, - 28659, - 28660, - 28661, - 28662, - 28663, - 28664, - 28665, - 28666, - 28667, - 28668, - 28669, - 28670, - 28671, - 28672, - 28673, - 28674, - 28675, - 28676, - 28677, - 28678, - 28679, - 28680, - 28681, - 28682, - 28683, - 28684, - 28685, - 28686, - 28687, - 28688, - 28689, - 28690, - 28691, - 28692, - 28693, - 28694, - 28695, - 28696, - 28697, - 28698, - 28699, - 28700, - 28701, - 28702, - 28703, - 28704, - 28705, - 28706, - 28707, - 28708, - 28709, - 28710, - 28711, - 28712, - 28713, - 28714, - 28715, - 28716, - 28717, - 28718, - 28719, - 28720, - 28721, - 28722, - 28723, - 28724, - 28725, - 28726, - 28727, - 28728, - 28729, - 28730, - 28731, - 28732, - 28733, - 28734, - 28735, - 28736, - 28737, - 28738, - 28739, - 28740, - 28741, - 28742, - 28743, - 28744, - 28745, - 28746, - 28747, - 28748, - 28749, - 28750, - 28751, - 28752, - 28753, - 28754, - 28755, - 28756, - 28757, - 28758, - 28759, - 28760, - 28761, - 28762, - 28763, - 28764, - 28765, - 28766, - 28767, - 28768, - 28769, - 28770, - 28771, - 28772, - 28773, - 28774, - 28775, - 28776, - 28777, - 28778, - 28779, - 28780, - 28781, - 28782, - 28783, - 28784, - 28785, - 28786, - 28787, - 28788, - 28789, - 28790, - 28791, - 28792, - 28793, - 28794, - 28795, - 28796, - 28797, - 28798, - 28799, - 28800, - 28801, - 28802, - 28803, - 28804, - 28805, - 28806, - 28807, - 28808, - 28809, - 28810, - 28811, - 28812, - 28813, - 28814, - 28815, - 28816, - 28817, - 28818, - 28819, - 28820, - 28821, - 28822, - 28823, - 28824, - 28825, - 28826, - 28827, - 28828, - 28829, - 28830, - 28831, - 28832, - 28833, - 28834, - 28835, - 28836, - 28837, - 28838, - 28839, - 28840, - 28841, - 28842, - 28843, - 28844, - 28845, - 28846, - 28847, - 28848, - 28849, - 28850, - 28851, - 28852, - 28853, - 28854, - 28855, - 28856, - 28857, - 28858, - 28859, - 28860, - 28861, - 28862, - 28863, - 28864, - 28865, - 28866, - 28867, - 28868, - 28869, - 28870, - 28871, - 28872, - 28873, - 28874, - 28875, - 28876, - 28877, - 28878, - 28879, - 28880, - 28881, - 28882, - 28883, - 28884, - 28885, - 28886, - 28887, - 28888, - 28889, - 28890, - 28891, - 28892, - 28893, - 28894, - 28895, - 28896, - 28897, - 28898, - 28899, - 28900, - 28901, - 28902, - 28903, - 28904, - 28905, - 28906, - 28907, - 28908, - 28909, - 28910, - 28911, - 28912, - 28913, - 28914, - 28915, - 28916, - 28917, - 28918, - 28919, - 28920, - 28921, - 28922, - 28923, - 28924, - 28925, - 28926, - 28927, - 28928, - 28929, - 28930, - 28931, - 28932, - 28933, - 28934, - 28935, - 28936, - 28937, - 28938, - 28939, - 28940, - 28941, - 28942, - 28943, - 28944, - 28945, - 28946, - 28947, - 28948, - 28949, - 28950, - 28951, - 28952, - 28953, - 28954, - 28955, - 28956, - 28957, - 28958, - 28959, - 28960, - 28961, - 28962, - 28963, - 28964, - 28965, - 28966, - 28967, - 28968, - 28969, - 28970, - 28971, - 28972, - 28973, - 28974, - 28975, - 28976, - 28977, - 28978, - 28979, - 28980, - 28981, - 28982, - 28983, - 28984, - 28985, - 28986, - 28987, - 28988, - 28989, - 28990, - 28991, - 28992, - 28993, - 28994, - 28995, - 28996, - 28997, - 28998, - 28999, - 29000, - 29001, - 29002, - 29003, - 29004, - 29005, - 29006, - 29007, - 29008, - 29009, - 29010, - 29011, - 29012, - 29013, - 29014, - 29015, - 29016, - 29017, - 29018, - 29019, - 29020, - 29021, - 29022, - 29023, - 29024, - 29025, - 29026, - 29027, - 29028, - 29029, - 29030, - 29031, - 29032, - 29033, - 29034, - 29035, - 29036, - 29037, - 29038, - 29039, - 29040, - 29041, - 29042, - 29043, - 29044, - 29045, - 29046, - 29047, - 29048, - 29049, - 29050, - 29051, - 29052, - 29053, - 29054, - 29055, - 29056, - 29057, - 29058, - 29059, - 29060, - 29061, - 29062, - 29063, - 29064, - 29065, - 29066, - 29067, - 29068, - 29069, - 29070, - 29071, - 29072, - 29073, - 29074, - 29075, - 29076, - 29077, - 29078, - 29079, - 29080, - 29081, - 29082, - 29083, - 29084, - 29085, - 29086, - 29087, - 29088, - 29089, - 29090, - 29091, - 29092, - 29093, - 29094, - 29095, - 29096, - 29097, - 29098, - 29099, - 29100, - 29101, - 29102, - 29103, - 29104, - 29105, - 29106, - 29107, - 29108, - 29109, - 29110, - 29111, - 29112, - 29113, - 29114, - 29115, - 29116, - 29117, - 29118, - 29119, - 29120, - 29121, - 29122, - 29123, - 29124, - 29125, - 29126, - 29127, - 29128, - 29129, - 29130, - 29131, - 29132, - 29133, - 29134, - 29135, - 29136, - 29137, - 29138, - 29139, - 29140, - 29141, - 29142, - 29143, - 29144, - 29145, - 29146, - 29147, - 29148, - 29149, - 29150, - 29151, - 29152, - 29153, - 29154, - 29155, - 29156, - 29157, - 29158, - 29159, - 29160, - 29161, - 29162, - 29163, - 29164, - 29165, - 29166, - 29167, - 29168, - 29169, - 29170, - 29171, - 29172, - 29173, - 29174, - 29175, - 29176, - 29177, - 29178, - 29179, - 29180, - 29181, - 29182, - 29183, - 29184, - 29185, - 29186, - 29187, - 29188, - 29189, - 29190, - 29191, - 29192, - 29193, - 29194, - 29195, - 29196, - 29197, - 29198, - 29199, - 29200, - 29201, - 29202, - 29203, - 29204, - 29205, - 29206, - 29207, - 29208, - 29209, - 29210, - 29211, - 29212, - 29213, - 29214, - 29215, - 29216, - 29217, - 29218, - 29219, - 29220, - 29221, - 29222, - 29223, - 29224, - 29225, - 29226, - 29227, - 29228, - 29229, - 29230, - 29231, - 29232, - 29233, - 29234, - 29235, - 29236, - 29237, - 29238, - 29239, - 29240, - 29241, - 29242, - 29243, - 29244, - 29245, - 29246, - 29247, - 29248, - 29249, - 29250, - 29251, - 29252, - 29253, - 29254, - 29255, - 29256, - 29257, - 29258, - 29259, - 29260, - 29261, - 29262, - 29263, - 29264, - 29265, - 29266, - 29267, - 29268, - 29269, - 29270, - 29271, - 29272, - 29273, - 29274, - 29275, - 29276, - 29277, - 29278, - 29279, - 29280, - 29281, - 29282, - 29283, - 29284, - 29285, - 29286, - 29287, - 29288, - 29289, - 29290, - 29291, - 29292, - 29293, - 29294, - 29295, - 29296, - 29297, - 29298, - 29299, - 29300, - 29301, - 29302, - 29303, - 29304, - 29305, - 29306, - 29307, - 29308, - 29309, - 29310, - 29311, - 29312, - 29313, - 29314, - 29315, - 29316, - 29317, - 29318, - 29319, - 29320, - 29321, - 29322, - 29323, - 29324, - 29325, - 29326, - 29327, - 29328, - 29329, - 29330, - 29331, - 29332, - 29333, - 29334, - 29335, - 29336, - 29337, - 29338, - 29339, - 29340, - 29341, - 29342, - 29343, - 29344, - 29345, - 29346, - 29347, - 29348, - 29349, - 29350, - 29351, - 29352, - 29353, - 29354, - 29355, - 29356, - 29357, - 29358, - 29359, - 29360, - 29361, - 29362, - 29363, - 29364, - 29365, - 29366, - 29367, - 29368, - 29369, - 29370, - 29371, - 29372, - 29373, - 29374, - 29375, - 29376, - 29377, - 29378, - 29379, - 29380, - 29381, - 29382, - 29383, - 29384, - 29385, - 29386, - 29387, - 29388, - 29389, - 29390, - 29391, - 29392, - 29393, - 29394, - 29395, - 29396, - 29397, - 29398, - 29399, - 29400, - 29401, - 29402, - 29403, - 29404, - 29405, - 29406, - 29407, - 29408, - 29409, - 29410, - 29411, - 29412, - 29413, - 29414, - 29415, - 29416, - 29417, - 29418, - 29419, - 29420, - 29421, - 29422, - 29423, - 29424, - 29425, - 29426, - 29427, - 29428, - 29429, - 29430, - 29431, - 29432, - 29433, - 29434, - 29435, - 29436, - 29437, - 29438, - 29439, - 29440, - 29441, - 29442, - 29443, - 29444, - 29445, - 29446, - 29447, - 29448, - 29449, - 29450, - 29451, - 29452, - 29453, - 29454, - 29455, - 29456, - 29457, - 29458, - 29459, - 29460, - 29461, - 29462, - 29463, - 29464, - 29465, - 29466, - 29467, - 29468, - 29469, - 29470, - 29471, - 29472, - 29473, - 29474, - 29475, - 29476, - 29477, - 29478, - 29479, - 29480, - 29481, - 29482, - 29483, - 29484, - 29485, - 29486, - 29487, - 29488, - 29489, - 29490, - 29491, - 29492, - 29493, - 29494, - 29495, - 29496, - 29497, - 29498, - 29499, - 29500, - 29501, - 29502, - 29503, - 29504, - 29505, - 29506, - 29507, - 29508, - 29509, - 29510, - 29511, - 29512, - 29513, - 29514, - 29515, - 29516, - 29517, - 29518, - 29519, - 29520, - 29521, - 29522, - 29523, - 29524, - 29525, - 29526, - 29527, - 29528, - 29529, - 29530, - 29531, - 29532, - 29533, - 29534, - 29535, - 29536, - 29537, - 29538, - 29539, - 29540, - 29541, - 29542, - 29543, - 29544, - 29545, - 29546, - 29547, - 29548, - 29549, - 29550, - 29551, - 29552, - 29553, - 29554, - 29555, - 29556, - 29557, - 29558, - 29559, - 29560, - 29561, - 29562, - 29563, - 29564, - 29565, - 29566, - 29567, - 29568, - 29569, - 29570, - 29571, - 29572, - 29573, - 29574, - 29575, - 29576, - 29577, - 29578, - 29579, - 29580, - 29581, - 29582, - 29583, - 29584, - 29585, - 29586, - 29587, - 29588, - 29589, - 29590, - 29591, - 29592, - 29593, - 29594, - 29595, - 29596, - 29597, - 29598, - 29599, - 29600, - 29601, - 29602, - 29603, - 29604, - 29605, - 29606, - 29607, - 29608, - 29609, - 29610, - 29611, - 29612, - 29613, - 29614, - 29615, - 29616, - 29617, - 29618, - 29619, - 29620, - 29621, - 29622, - 29623, - 29624, - 29625, - 29626, - 29627, - 29628, - 29629, - 29630, - 29631, - 29632, - 29633, - 29634, - 29635, - 29636, - 29637, - 29638, - 29639, - 29640, - 29641, - 29642, - 29643, - 29644, - 29645, - 29646, - 29647, - 29648, - 29649, - 29650, - 29651, - 29652, - 29653, - 29654, - 29655, - 29656, - 29657, - 29658, - 29659, - 29660, - 29661, - 29662, - 29663, - 29664, - 29665, - 29666, - 29667, - 29668, - 29669, - 29670, - 29671, - 29672, - 29673, - 29674, - 29675, - 29676, - 29677, - 29678, - 29679, - 29680, - 29681, - 29682, - 29683, - 29684, - 29685, - 29686, - 29687, - 29688, - 29689, - 29690, - 29691, - 29692, - 29693, - 29694, - 29695, - 29696, - 29697, - 29698, - 29699, - 29700, - 29701, - 29702, - 29703, - 29704, - 29705, - 29706, - 29707, - 29708, - 29709, - 29710, - 29711, - 29712, - 29713, - 29714, - 29715, - 29716, - 29717, - 29718, - 29719, - 29720, - 29721, - 29722, - 29723, - 29724, - 29725, - 29726, - 29727, - 29728, - 29729, - 29730, - 29731, - 29732, - 29733, - 29734, - 29735, - 29736, - 29737, - 29738, - 29739, - 29740, - 29741, - 29742, - 29743, - 29744, - 29745, - 29746, - 29747, - 29748, - 29749, - 29750, - 29751, - 29752, - 29753, - 29754, - 29755, - 29756, - 29757, - 29758, - 29759, - 29760, - 29761, - 29762, - 29763, - 29764, - 29765, - 29766, - 29767, - 29768, - 29769, - 29770, - 29771, - 29772, - 29773, - 29774, - 29775, - 29776, - 29777, - 29778, - 29779, - 29780, - 29781, - 29782, - 29783, - 29784, - 29785, - 29786, - 29787, - 29788, - 29789, - 29790, - 29791, - 29792, - 29793, - 29794, - 29795, - 29796, - 29797, - 29798, - 29799, - 29800, - 29801, - 29802, - 29803, - 29804, - 29805, - 29806, - 29807, - 29808, - 29809, - 29810, - 29811, - 29812, - 29813, - 29814, - 29815, - 29816, - 29817, - 29818, - 29819, - 29820, - 29821, - 29822, - 29823, - 29824, - 29825, - 29826, - 29827, - 29828, - 29829, - 29830, - 29831, - 29832, - 29833, - 29834, - 29835, - 29836, - 29837, - 29838, - 29839, - 29840, - 29841, - 29842, - 29843, - 29844, - 29845, - 29846, - 29847, - 29848, - 29849, - 29850, - 29851, - 29852, - 29853, - 29854, - 29855, - 29856, - 29857, - 29858, - 29859, - 29860, - 29861, - 29862, - 29863, - 29864, - 29865, - 29866, - 29867, - 29868, - 29869, - 29870, - 29871, - 29872, - 29873, - 29874, - 29875, - 29876, - 29877, - 29878, - 29879, - 29880, - 29881, - 29882, - 29883, - 29884, - 29885, - 29886, - 29887, - 29888, - 29889, - 29890, - 29891, - 29892, - 29893, - 29894, - 29895, - 29896, - 29897, - 29898, - 29899, - 29900, - 29901, - 29902, - 29903, - 29904, - 29905, - 29906, - 29907, - 29908, - 29909, - 29910, - 29911, - 29912, - 29913, - 29914, - 29915, - 29916, - 29917, - 29918, - 29919, - 29920, - 29921, - 29922, - 29923, - 29924, - 29925, - 29926, - 29927, - 29928, - 29929, - 29930, - 29931, - 29932, - 29933, - 29934, - 29935, - 29936, - 29937, - 29938, - 29939, - 29940, - 29941, - 29942, - 29943, - 29944, - 29945, - 29946, - 29947, - 29948, - 29949, - 29950, - 29951, - 29952, - 29953, - 29954, - 29955, - 29956, - 29957, - 29958, - 29959, - 29960, - 29961, - 29962, - 29963, - 29964, - 29965, - 29966, - 29967, - 29968, - 29969, - 29970, - 29971, - 29972, - 29973, - 29974, - 29975, - 29976, - 29977, - 29978, - 29979, - 29980, - 29981, - 29982, - 29983, - 29984, - 29985, - 29986, - 29987, - 29988, - 29989, - 29990, - 29991, - 29992, - 29993, - 29994, - 29995, - 29996, - 29997, - 29998, - 29999, - 30000, - 30001, - 30002, - 30003, - 30004, - 30005, - 30006, - 30007, - 30008, - 30009, - 30010, - 30011, - 30012, - 30013, - 30014, - 30015, - 30016, - 30017, - 30018, - 30019, - 30020, - 30021, - 30022, - 30023, - 30024, - 30025, - 30026, - 30027, - 30028, - 30029, - 30030, - 30031, - 30032, - 30033, - 30034, - 30035, - 30036, - 30037, - 30038, - 30039, - 30040, - 30041, - 30042, - 30043, - 30044, - 30045, - 30046, - 30047, - 30048, - 30049, - 30050, - 30051, - 30052, - 30053, - 30054, - 30055, - 30056, - 30057, - 30058, - 30059, - 30060, - 30061, - 30062, - 30063, - 30064, - 30065, - 30066, - 30067, - 30068, - 30069, - 30070, - 30071, - 30072, - 30073, - 30074, - 30075, - 30076, - 30077, - 30078, - 30079, - 30080, - 30081, - 30082, - 30083, - 30084, - 30085, - 30086, - 30087, - 30088, - 30089, - 30090, - 30091, - 30092, - 30093, - 30094, - 30095, - 30096, - 30097, - 30098, - 30099, - 30100, - 30101, - 30102, - 30103, - 30104, - 30105, - 30106, - 30107, - 30108, - 30109, - 30110, - 30111, - 30112, - 30113, - 30114, - 30115, - 30116, - 30117, - 30118, - 30119, - 30120, - 30121, - 30122, - 30123, - 30124, - 30125, - 30126, - 30127, - 30128, - 30129, - 30130, - 30131, - 30132, - 30133, - 30134, - 30135, - 30136, - 30137, - 30138, - 30139, - 30140, - 30141, - 30142, - 30143, - 30144, - 30145, - 30146, - 30147, - 30148, - 30149, - 30150, - 30151, - 30152, - 30153, - 30154, - 30155, - 30156, - 30157, - 30158, - 30159, - 30160, - 30161, - 30162, - 30163, - 30164, - 30165, - 30166, - 30167, - 30168, - 30169, - 30170, - 30171, - 30172, - 30173, - 30174, - 30175, - 30176, - 30177, - 30178, - 30179, - 30180, - 30181, - 30182, - 30183, - 30184, - 30185, - 30186, - 30187, - 30188, - 30189, - 30190, - 30191, - 30192, - 30193, - 30194, - 30195, - 30196, - 30197, - 30198, - 30199, - 30200, - 30201, - 30202, - 30203, - 30204, - 30205, - 30206, - 30207, - 30208, - 30209, - 30210, - 30211, - 30212, - 30213, - 30214, - 30215, - 30216, - 30217, - 30218, - 30219, - 30220, - 30221, - 30222, - 30223, - 30224, - 30225, - 30226, - 30227, - 30228, - 30229, - 30230, - 30231, - 30232, - 30233, - 30234, - 30235, - 30236, - 30237, - 30238, - 30239, - 30240, - 30241, - 30242, - 30243, - 30244, - 30245, - 30246, - 30247, - 30248, - 30249, - 30250, - 30251, - 30252, - 30253, - 30254, - 30255, - 30256, - 30257, - 30258, - 30259, - 30260, - 30261, - 30262, - 30263, - 30264, - 30265, - 30266, - 30267, - 30268, - 30269, - 30270, - 30271, - 30272, - 30273, - 30274, - 30275, - 30276, - 30277, - 30278, - 30279, - 30280, - 30281, - 30282, - 30283, - 30284, - 30285, - 30286, - 30287, - 30288, - 30289, - 30290, - 30291, - 30292, - 30293, - 30294, - 30295, - 30296, - 30297, - 30298, - 30299, - 30300, - 30301, - 30302, - 30303, - 30304, - 30305, - 30306, - 30307, - 30308, - 30309, - 30310, - 30311, - 30312, - 30313, - 30314, - 30315, - 30316, - 30317, - 30318, - 30319, - 30320, - 30321, - 30322, - 30323, - 30324, - 30325, - 30326, - 30327, - 30328, - 30329, - 30330, - 30331, - 30332, - 30333, - 30334, - 30335, - 30336, - 30337, - 30338, - 30339, - 30340, - 30341, - 30342, - 30343, - 30344, - 30345, - 30346, - 30347, - 30348, - 30349, - 30350, - 30351, - 30352, - 30353, - 30354, - 30355, - 30356, - 30357, - 30358, - 30359, - 30360, - 30361, - 30362, - 30363, - 30364, - 30365, - 30366, - 30367, - 30368, - 30369, - 30370, - 30371, - 30372, - 30373, - 30374, - 30375, - 30376, - 30377, - 30378, - 30379, - 30380, - 30381, - 30382, - 30383, - 30384, - 30385, - 30386, - 30387, - 30388, - 30389, - 30390, - 30391, - 30392, - 30393, - 30394, - 30395, - 30396, - 30397, - 30398, - 30399, - 30400, - 30401, - 30402, - 30403, - 30404, - 30405, - 30406, - 30407, - 30408, - 30409, - 30410, - 30411, - 30412, - 30413, - 30414, - 30415, - 30416, - 30417, - 30418, - 30419, - 30420, - 30421, - 30422, - 30423, - 30424, - 30425, - 30426, - 30427, - 30428, - 30429, - 30430, - 30431, - 30432, - 30433, - 30434, - 30435, - 30436, - 30437, - 30438, - 30439, - 30440, - 30441, - 30442, - 30443, - 30444, - 30445, - 30446, - 30447, - 30448, - 30449, - 30450, - 30451, - 30452, - 30453, - 30454, - 30455, - 30456, - 30457, - 30458, - 30459, - 30460, - 30461, - 30462, - 30463, - 30464, - 30465, - 30466, - 30467, - 30468, - 30469, - 30470, - 30471, - 30472, - 30473, - 30474, - 30475, - 30476, - 30477, - 30478, - 30479, - 30480, - 30481, - 30482, - 30483, - 30484, - 30485, - 30486, - 30487, - 30488, - 30489, - 30490, - 30491, - 30492, - 30493, - 30494, - 30495, - 30496, - 30497, - 30498, - 30499, - 30500, - 30501, - 30502, - 30503, - 30504, - 30505, - 30506, - 30507, - 30508, - 30509, - 30510, - 30511, - 30512, - 30513, - 30514, - 30515, - 30516, - 30517, - 30518, - 30519, - 30520, - 30521, - 30522, - 30523, - 30524, - 30525, - 30526, - 30527, - 30528, - 30529, - 30530, - 30531, - 30532, - 30533, - 30534, - 30535, - 30536, - 30537, - 30538, - 30539, - 30540, - 30541, - 30542, - 30543, - 30544, - 30545, - 30546, - 30547, - 30548, - 30549, - 30550, - 30551, - 30552, - 30553, - 30554, - 30555, - 30556, - 30557, - 30558, - 30559, - 30560, - 30561, - 30562, - 30563, - 30564, - 30565, - 30566, - 30567, - 30568, - 30569, - 30570, - 30571, - 30572, - 30573, - 30574, - 30575, - 30576, - 30577, - 30578, - 30579, - 30580, - 30581, - 30582, - 30583, - 30584, - 30585, - 30586, - 30587, - 30588, - 30589, - 30590, - 30591, - 30592, - 30593, - 30594, - 30595, - 30596, - 30597, - 30598, - 30599, - 30600, - 30601, - 30602, - 30603, - 30604, - 30605, - 30606, - 30607, - 30608, - 30609, - 30610, - 30611, - 30612, - 30613, - 30614, - 30615, - 30616, - 30617, - 30618, - 30619, - 30620, - 30621, - 30622, - 30623, - 30624, - 30625, - 30626, - 30627, - 30628, - 30629, - 30630, - 30631, - 30632, - 30633, - 30634, - 30635, - 30636, - 30637, - 30638, - 30639, - 30640, - 30641, - 30642, - 30643, - 30644, - 30645, - 30646, - 30647, - 30648, - 30649, - 30650, - 30651, - 30652, - 30653, - 30654, - 30655, - 30656, - 30657, - 30658, - 30659, - 30660, - 30661, - 30662, - 30663, - 30664, - 30665, - 30666, - 30667, - 30668, - 30669, - 30670, - 30671, - 30672, - 30673, - 30674, - 30675, - 30676, - 30677, - 30678, - 30679, - 30680, - 30681, - 30682, - 30683, - 30684, - 30685, - 30686, - 30687, - 30688, - 30689, - 30690, - 30691, - 30692, - 30693, - 30694, - 30695, - 30696, - 30697, - 30698, - 30699, - 30700, - 30701, - 30702, - 30703, - 30704, - 30705, - 30706, - 30707, - 30708, - 30709, - 30710, - 30711, - 30712, - 30713, - 30714, - 30715, - 30716, - 30717, - 30718, - 30719, - 30720, - 30721, - 30722, - 30723, - 30724, - 30725, - 30726, - 30727, - 30728, - 30729, - 30730, - 30731, - 30732, - 30733, - 30734, - 30735, - 30736, - 30737, - 30738, - 30739, - 30740, - 30741, - 30742, - 30743, - 30744, - 30745, - 30746, - 30747, - 30748, - 30749, - 30750, - 30751, - 30752, - 30753, - 30754, - 30755, - 30756, - 30757, - 30758, - 30759, - 30760, - 30761, - 30762, - 30763, - 30764, - 30765, - 30766, - 30767, - 30768, - 30769, - 30770, - 30771, - 30772, - 30773, - 30774, - 30775, - 30776, - 30777, - 30778, - 30779, - 30780, - 30781, - 30782, - 30783, - 30784, - 30785, - 30786, - 30787, - 30788, - 30789, - 30790, - 30791, - 30792, - 30793, - 30794, - 30795, - 30796, - 30797, - 30798, - 30799, - 30800, - 30801, - 30802, - 30803, - 30804, - 30805, - 30806, - 30807, - 30808, - 30809, - 30810, - 30811, - 30812, - 30813, - 30814, - 30815, - 30816, - 30817, - 30818, - 30819, - 30820, - 30821, - 30822, - 30823, - 30824, - 30825, - 30826, - 30827, - 30828, - 30829, - 30830, - 30831, - 30832, - 30833, - 30834, - 30835, - 30836, - 30837, - 30838, - 30839, - 30840, - 30841, - 30842, - 30843, - 30844, - 30845, - 30846, - 30847, - 30848, - 30849, - 30850, - 30851, - 30852, - 30853, - 30854, - 30855, - 30856, - 30857, - 30858, - 30859, - 30860, - 30861, - 30862, - 30863, - 30864, - 30865, - 30866, - 30867, - 30868, - 30869, - 30870, - 30871, - 30872, - 30873, - 30874, - 30875, - 30876, - 30877, - 30878, - 30879, - 30880, - 30881, - 30882, - 30883, - 30884, - 30885, - 30886, - 30887, - 30888, - 30889, - 30890, - 30891, - 30892, - 30893, - 30894, - 30895, - 30896, - 30897, - 30898, - 30899, - 30900, - 30901, - 30902, - 30903, - 30904, - 30905, - 30906, - 30907, - 30908, - 30909, - 30910, - 30911, - 30912, - 30913, - 30914, - 30915, - 30916, - 30917, - 30918, - 30919, - 30920, - 30921, - 30922, - 30923, - 30924, - 30925, - 30926, - 30927, - 30928, - 30929, - 30930, - 30931, - 30932, - 30933, - 30934, - 30935, - 30936, - 30937, - 30938, - 30939, - 30940, - 30941, - 30942, - 30943, - 30944, - 30945, - 30946, - 30947, - 30948, - 30949, - 30950, - 30951, - 30952, - 30953, - 30954, - 30955, - 30956, - 30957, - 30958, - 30959, - 30960, - 30961, - 30962, - 30963, - 30964, - 30965, - 30966, - 30967, - 30968, - 30969, - 30970, - 30971, - 30972, - 30973, - 30974, - 30975, - 30976, - 30977, - 30978, - 30979, - 30980, - 30981, - 30982, - 30983, - 30984, - 30985, - 30986, - 30987, - 30988, - 30989, - 30990, - 30991, - 30992, - 30993, - 30994, - 30995, - 30996, - 30997, - 30998, - 30999, - 31000, - 31001, - 31002, - 31003, - 31004, - 31005, - 31006, - 31007, - 31008, - 31009, - 31010, - 31011, - 31012, - 31013, - 31014, - 31015, - 31016, - 31017, - 31018, - 31019, - 31020, - 31021, - 31022, - 31023, - 31024, - 31025, - 31026, - 31027, - 31028, - 31029, - 31030, - 31031, - 31032, - 31033, - 31034, - 31035, - 31036, - 31037, - 31038, - 31039, - 31040, - 31041, - 31042, - 31043, - 31044, - 31045, - 31046, - 31047, - 31048, - 31049, - 31050, - 31051, - 31052, - 31053, - 31054, - 31055, - 31056, - 31057, - 31058, - 31059, - 31060, - 31061, - 31062, - 31063, - 31064, - 31065, - 31066, - 31067, - 31068, - 31069, - 31070, - 31071, - 31072, - 31073, - 31074, - 31075, - 31076, - 31077, - 31078, - 31079, - 31080, - 31081, - 31082, - 31083, - 31084, - 31085, - 31086, - 31087, - 31088, - 31089, - 31090, - 31091, - 31092, - 31093, - 31094, - 31095, - 31096, - 31097, - 31098, - 31099, - 31100, - 31101, - 31102, - 31103, - 31104, - 31105, - 31106, - 31107, - 31108, - 31109, - 31110, - 31111, - 31112, - 31113, - 31114, - 31115, - 31116, - 31117, - 31118, - 31119, - 31120, - 31121, - 31122, - 31123, - 31124, - 31125, - 31126, - 31127, - 31128, - 31129, - 31130, - 31131, - 31132, - 31133, - 31134, - 31135, - 31136, - 31137, - 31138, - 31139, - 31140, - 31141, - 31142, - 31143, - 31144, - 31145, - 31146, - 31147, - 31148, - 31149, - 31150, - 31151, - 31152, - 31153, - 31154, - 31155, - 31156, - 31157, - 31158, - 31159, - 31160, - 31161, - 31162, - 31163, - 31164, - 31165, - 31166, - 31167, - 31168, - 31169, - 31170, - 31171, - 31172, - 31173, - 31174, - 31175, - 31176, - 31177, - 31178, - 31179, - 31180, - 31181, - 31182, - 31183, - 31184, - 31185, - 31186, - 31187, - 31188, - 31189, - 31190, - 31191, - 31192, - 31193, - 31194, - 31195, - 31196, - 31197, - 31198, - 31199, - 31200, - 31201, - 31202, - 31203, - 31204, - 31205, - 31206, - 31207, - 31208, - 31209, - 31210, - 31211, - 31212, - 31213, - 31214, - 31215, - 31216, - 31217, - 31218, - 31219, - 31220, - 31221, - 31222, - 31223, - 31224, - 31225, - 31226, - 31227, - 31228, - 31229, - 31230, - 31231, - 31232, - 31233, - 31234, - 31235, - 31236, - 31237, - 31238, - 31239, - 31240, - 31241, - 31242, - 31243, - 31244, - 31245, - 31246, - 31247, - 31248, - 31249, - 31250, - 31251, - 31252, - 31253, - 31254, - 31255, - 31256, - 31257, - 31258, - 31259, - 31260, - 31261, - 31262, - 31263, - 31264, - 31265, - 31266, - 31267, - 31268, - 31269, - 31270, - 31271, - 31272, - 31273, - 31274, - 31275, - 31276, - 31277, - 31278, - 31279, - 31280, - 31281, - 31282, - 31283, - 31284, - 31285, - 31286, - 31287, - 31288, - 31289, - 31290, - 31291, - 31292, - 31293, - 31294, - 31295, - 31296, - 31297, - 31298, - 31299, - 31300, - 31301, - 31302, - 31303, - 31304, - 31305, - 31306, - 31307, - 31308, - 31309, - 31310, - 31311, - 31312, - 31313, - 31314, - 31315, - 31316, - 31317, - 31318, - 31319, - 31320, - 31321, - 31322, - 31323, - 31324, - 31325, - 31326, - 31327, - 31328, - 31329, - 31330, - 31331, - 31332, - 31333, - 31334, - 31335, - 31336, - 31337, - 31338, - 31339, - 31340, - 31341, - 31342, - 31343, - 31344, - 31345, - 31346, - 31347, - 31348, - 31349, - 31350, - 31351, - 31352, - 31353, - 31354, - 31355, - 31356, - 31357, - 31358, - 31359, - 31360, - 31361, - 31362, - 31363, - 31364, - 31365, - 31366, - 31367, - 31368, - 31369, - 31370, - 31371, - 31372, - 31373, - 31374, - 31375, - 31376, - 31377, - 31378, - 31379, - 31380, - 31381, - 31382, - 31383, - 31384, - 31385, - 31386, - 31387, - 31388, - 31389, - 31390, - 31391, - 31392, - 31393, - 31394, - 31395, - 31396, - 31397, - 31398, - 31399, - 31400, - 31401, - 31402, - 31403, - 31404, - 31405, - 31406, - 31407, - 31408, - 31409, - 31410, - 31411, - 31412, - 31413, - 31414, - 31415, - 31416, - 31417, - 31418, - 31419, - 31420, - 31421, - 31422, - 31423, - 31424, - 31425, - 31426, - 31427, - 31428, - 31429, - 31430, - 31431, - 31432, - 31433, - 31434, - 31435, - 31436, - 31437, - 31438, - 31439, - 31440, - 31441, - 31442, - 31443, - 31444, - 31445, - 31446, - 31447, - 31448, - 31449, - 31450, - 31451, - 31452, - 31453, - 31454, - 31455, - 31456, - 31457, - 31458, - 31459, - 31460, - 31461, - 31462, - 31463, - 31464, - 31465, - 31466, - 31467, - 31468, - 31469, - 31470, - 31471, - 31472, - 31473, - 31474, - 31475, - 31476, - 31477, - 31478, - 31479, - 31480, - 31481, - 31482, - 31483, - 31484, - 31485, - 31486, - 31487, - 31488, - 31489, - 31490, - 31491, - 31492, - 31493, - 31494, - 31495, - 31496, - 31497, - 31498, - 31499, - 31500, - 31501, - 31502, - 31503, - 31504, - 31505, - 31506, - 31507, - 31508, - 31509, - 31510, - 31511, - 31512, - 31513, - 31514, - 31515, - 31516, - 31517, - 31518, - 31519, - 31520, - 31521, - 31522, - 31523, - 31524, - 31525, - 31526, - 31527, - 31528, - 31529, - 31530, - 31531, - 31532, - 31533, - 31534, - 31535, - 31536, - 31537, - 31538, - 31539, - 31540, - 31541, - 31542, - 31543, - 31544, - 31545, - 31546, - 31547, - 31548, - 31549, - 31550, - 31551, - 31552, - 31553, - 31554, - 31555, - 31556, - 31557, - 31558, - 31559, - 31560, - 31561, - 31562, - 31563, - 31564, - 31565, - 31566, - 31567, - 31568, - 31569, - 31570, - 31571, - 31572, - 31573, - 31574, - 31575, - 31576, - 31577, - 31578, - 31579, - 31580, - 31581, - 31582, - 31583, - 31584, - 31585, - 31586, - 31587, - 31588, - 31589, - 31590, - 31591, - 31592, - 31593, - 31594, - 31595, - 31596, - 31597, - 31598, - 31599, - 31600, - 31601, - 31602, - 31603, - 31604, - 31605, - 31606, - 31607, - 31608, - 31609, - 31610, - 31611, - 31612, - 31613, - 31614, - 31615, - 31616, - 31617, - 31618, - 31619, - 31620, - 31621, - 31622, - 31623, - 31624, - 31625, - 31626, - 31627, - 31628, - 31629, - 31630, - 31631, - 31632, - 31633, - 31634, - 31635, - 31636, - 31637, - 31638, - 31639, - 31640, - 31641, - 31642, - 31643, - 31644, - 31645, - 31646, - 31647, - 31648, - 31649, - 31650, - 31651, - 31652, - 31653, - 31654, - 31655, - 31656, - 31657, - 31658, - 31659, - 31660, - 31661, - 31662, - 31663, - 31664, - 31665, - 31666, - 31667, - 31668, - 31669, - 31670, - 31671, - 31672, - 31673, - 31674, - 31675, - 31676, - 31677, - 31678, - 31679, - 31680, - 31681, - 31682, - 31683, - 31684, - 31685, - 31686, - 31687, - 31688, - 31689, - 31690, - 31691, - 31692, - 31693, - 31694, - 31695, - 31696, - 31697, - 31698, - 31699, - 31700, - 31701, - 31702, - 31703, - 31704, - 31705, - 31706, - 31707, - 31708, - 31709, - 31710, - 31711, - 31712, - 31713, - 31714, - 31715, - 31716, - 31717, - 31718, - 31719, - 31720, - 31721, - 31722, - 31723, - 31724, - 31725, - 31726, - 31727, - 31728, - 31729, - 31730, - 31731, - 31732, - 31733, - 31734, - 31735, - 31736, - 31737, - 31738, - 31739, - 31740, - 31741, - 31742, - 31743, - 31744, - 31745, - 31746, - 31747, - 31748, - 31749, - 31750, - 31751, - 31752, - 31753, - 31754, - 31755, - 31756, - 31757, - 31758, - 31759, - 31760, - 31761, - 31762, - 31763, - 31764, - 31765, - 31766, - 31767, - 31768, - 31769, - 31770, - 31771, - 31772, - 31773, - 31774, - 31775, - 31776, - 31777, - 31778, - 31779, - 31780, - 31781, - 31782, - 31783, - 31784, - 31785, - 31786, - 31787, - 31788, - 31789, - 31790, - 31791, - 31792, - 31793, - 31794, - 31795, - 31796, - 31797, - 31798, - 31799, - 31800, - 31801, - 31802, - 31803, - 31804, - 31805, - 31806, - 31807, - 31808, - 31809, - 31810, - 31811, - 31812, - 31813, - 31814, - 31815, - 31816, - 31817, - 31818, - 31819, - 31820, - 31821, - 31822, - 31823, - 31824, - 31825, - 31826, - 31827, - 31828, - 31829, - 31830, - 31831, - 31832, - 31833, - 31834, - 31835, - 31836, - 31837, - 31838, - 31839, - 31840, - 31841, - 31842, - 31843, - 31844, - 31845, - 31846, - 31847, - 31848, - 31849, - 31850, - 31851, - 31852, - 31853, - 31854, - 31855, - 31856, - 31857, - 31858, - 31859, - 31860, - 31861, - 31862, - 31863, - 31864, - 31865, - 31866, - 31867, - 31868, - 31869, - 31870, - 31871, - 31872, - 31873, - 31874, - 31875, - 31876, - 31877, - 31878, - 31879, - 31880, - 31881, - 31882, - 31883, - 31884, - 31885, - 31886, - 31887, - 31888, - 31889, - 31890, - 31891, - 31892, - 31893, - 31894, - 31895, - 31896, - 31897, - 31898, - 31899, - 31900, - 31901, - 31902, - 31903, - 31904, - 31905, - 31906, - 31907, - 31908, - 31909, - 31910, - 31911, - 31912, - 31913, - 31914, - 31915, - 31916, - 31917, - 31918, - 31919, - 31920, - 31921, - 31922, - 31923, - 31924, - 31925, - 31926, - 31927, - 31928, - 31929, - 31930, - 31931, - 31932, - 31933, - 31934, - 31935, - 31936, - 31937, - 31938, - 31939, - 31940, - 31941, - 31942, - 31943, - 31944, - 31945, - 31946, - 31947, - 31948, - 31949, - 31950, - 31951, - 31952, - 31953, - 31954, - 31955, - 31956, - 31957, - 31958, - 31959, - 31960, - 31961, - 31962, - 31963, - 31964, - 31965, - 31966, - 31967, - 31968, - 31969, - 31970, - 31971, - 31972, - 31973, - 31974, - 31975, - 31976, - 31977, - 31978, - 31979, - 31980, - 31981, - 31982, - 31983, - 31984, - 31985, - 31986, - 31987, - 31988, - 31989, - 31990, - 31991, - 31992, - 31993, - 31994, - 31995, - 31996, - 31997, - 31998, - 31999, - 32000, - 32001, - 32002, - 32003, - 32004, - 32005, - 32006, - 32007, - 32008, - 32009, - 32010, - 32011, - 32012, - 32013, - 32014, - 32015, - 32016, - 32017, - 32018, - 32019, - 32020, - 32021, - 32022, - 32023, - 32024, - 32025, - 32026, - 32027, - 32028, - 32029, - 32030, - 32031, - 32032, - 32033, - 32034, - 32035, - 32036, - 32037, - 32038, - 32039, - 32040, - 32041, - 32042, - 32043, - 32044, - 32045, - 32046, - 32047, - 32048, - 32049, - 32050, - 32051, - 32052, - 32053, - 32054, - 32055, - 32056, - 32057, - 32058, - 32059, - 32060, - 32061, - 32062, - 32063, - 32064, - 32065, - 32066, - 32067, - 32068, - 32069, - 32070, - 32071, - 32072, - 32073, - 32074, - 32075, - 32076, - 32077, - 32078, - 32079, - 32080, - 32081, - 32082, - 32083, - 32084, - 32085, - 32086, - 32087, - 32088, - 32089, - 32090, - 32091, - 32092, - 32093, - 32094, - 32095, - 32096, - 32097, - 32098, - 32099, - 32100, - 32101, - 32102, - 32103, - 32104, - 32105, - 32106, - 32107, - 32108, - 32109, - 32110, - 32111, - 32112, - 32113, - 32114, - 32115, - 32116, - 32117, - 32118, - 32119, - 32120, - 32121, - 32122, - 32123, - 32124, - 32125, - 32126, - 32127, - 32128, - 32129, - 32130, - 32131, - 32132, - 32133, - 32134, - 32135, - 32136, - 32137, - 32138, - 32139, - 32140, - 32141, - 32142, - 32143, - 32144, - 32145, - 32146, - 32147, - 32148, - 32149, - 32150, - 32151, - 32152, - 32153, - 32154, - 32155, - 32156, - 32157, - 32158, - 32159, - 32160, - 32161, - 32162, - 32163, - 32164, - 32165, - 32166, - 32167, - 32168, - 32169, - 32170, - 32171, - 32172, - 32173, - 32174, - 32175, - 32176, - 32177, - 32178, - 32179, - 32180, - 32181, - 32182, - 32183, - 32184, - 32185, - 32186, - 32187, - 32188, - 32189, - 32190, - 32191, - 32192, - 32193, - 32194, - 32195, - 32196, - 32197, - 32198, - 32199, - 32200, - 32201, - 32202, - 32203, - 32204, - 32205, - 32206, - 32207, - 32208, - 32209, - 32210, - 32211, - 32212, - 32213, - 32214, - 32215, - 32216, - 32217, - 32218, - 32219, - 32220, - 32221, - 32222, - 32223, - 32224, - 32225, - 32226, - 32227, - 32228, - 32229, - 32230, - 32231, - 32232, - 32233, - 32234, - 32235, - 32236, - 32237, - 32238, - 32239, - 32240, - 32241, - 32242, - 32243, - 32244, - 32245, - 32246, - 32247, - 32248, - 32249, - 32250, - 32251, - 32252, - 32253, - 32254, - 32255, - 32256, - 32257, - 32258, - 32259, - 32260, - 32261, - 32262, - 32263, - 32264, - 32265, - 32266, - 32267, - 32268, - 32269, - 32270, - 32271, - 32272, - 32273, - 32274, - 32275, - 32276, - 32277, - 32278, - 32279, - 32280, - 32281, - 32282, - 32283, - 32284, - 32285, - 32286, - 32287, - 32288, - 32289, - 32290, - 32291, - 32292, - 32293, - 32294, - 32295, - 32296, - 32297, - 32298, - 32299, - 32300, - 32301, - 32302, - 32303, - 32304, - 32305, - 32306, - 32307, - 32308, - 32309, - 32310, - 32311, - 32312, - 32313, - 32314, - 32315, - 32316, - 32317, - 32318, - 32319, - 32320, - 32321, - 32322, - 32323, - 32324, - 32325, - 32326, - 32327, - 32328, - 32329, - 32330, - 32331, - 32332, - 32333, - 32334, - 32335, - 32336, - 32337, - 32338, - 32339, - 32340, - 32341, - 32342, - 32343, - 32344, - 32345, - 32346, - 32347, - 32348, - 32349, - 32350, - 32351, - 32352, - 32353, - 32354, - 32355, - 32356, - 32357, - 32358, - 32359, - 32360, - 32361, - 32362, - 32363, - 32364, - 32365, - 32366, - 32367, - 32368, - 32369, - 32370, - 32371, - 32372, - 32373, - 32374, - 32375, - 32376, - 32377, - 32378, - 32379, - 32380, - 32381, - 32382, - 32383, - 32384, - 32385, - 32386, - 32387, - 32388, - 32389, - 32390, - 32391, - 32392, - 32393, - 32394, - 32395, - 32396, - 32397, - 32398, - 32399, - 32400, - 32401, - 32402, - 32403, - 32404, - 32405, - 32406, - 32407, - 32408, - 32409, - 32410, - 32411, - 32412, - 32413, - 32414, - 32415, - 32416, - 32417, - 32418, - 32419, - 32420, - 32421, - 32422, - 32423, - 32424, - 32425, - 32426, - 32427, - 32428, - 32429, - 32430, - 32431, - 32432, - 32433, - 32434, - 32435, - 32436, - 32437, - 32438, - 32439, - 32440, - 32441, - 32442, - 32443, - 32444, - 32445, - 32446, - 32447, - 32448, - 32449, - 32450, - 32451, - 32452, - 32453, - 32454, - 32455, - 32456, - 32457, - 32458, - 32459, - 32460, - 32461, - 32462, - 32463, - 32464, - 32465, - 32466, - 32467, - 32468, - 32469, - 32470, - 32471, - 32472, - 32473, - 32474, - 32475, - 32476, - 32477, - 32478, - 32479, - 32480, - 32481, - 32482, - 32483, - 32484, - 32485, - 32486, - 32487, - 32488, - 32489, - 32490, - 32491, - 32492, - 32493, - 32494, - 32495, - 32496, - 32497, - 32498, - 32499, - 32500, - 32501, - 32502, - 32503, - 32504, - 32505, - 32506, - 32507, - 32508, - 32509, - 32510, - 32511, - 32512, - 32513, - 32514, - 32515, - 32516, - 32517, - 32518, - 32519, - 32520, - 32521, - 32522, - 32523, - 32524, - 32525, - 32526, - 32527, - 32528, - 32529, - 32530, - 32531, - 32532, - 32533, - 32534, - 32535, - 32536, - 32537, - 32538, - 32539, - 32540, - 32541, - 32542, - 32543, - 32544, - 32545, - 32546, - 32547, - 32548, - 32549, - 32550, - 32551, - 32552, - 32553, - 32554, - 32555, - 32556, - 32557, - 32558, - 32559, - 32560, - 32561, - 32562, - 32563, - 32564, - 32565, - 32566, - 32567, - 32568, - 32569, - 32570, - 32571, - 32572, - 32573, - 32574, - 32575, - 32576, - 32577, - 32578, - 32579, - 32580, - 32581, - 32582, - 32583, - 32584, - 32585, - 32586, - 32587, - 32588, - 32589, - 32590, - 32591, - 32592, - 32593, - 32594, - 32595, - 32596, - 32597, - 32598, - 32599, - 32600, - 32601, - 32602, - 32603, - 32604, - 32605, - 32606, - 32607, - 32608, - 32609, - 32610, - 32611, - 32612, - 32613, - 32614, - 32615, - 32616, - 32617, - 32618, - 32619, - 32620, - 32621, - 32622, - 32623, - 32624, - 32625, - 32626, - 32627, - 32628, - 32629, - 32630, - 32631, - 32632, - 32633, - 32634, - 32635, - 32636, - 32637, - 32638, - 32639, - 32640, - 32641, - 32642, - 32643, - 32644, - 32645, - 32646, - 32647, - 32648, - 32649, - 32650, - 32651, - 32652, - 32653, - 32654, - 32655, - 32656, - 32657, - 32658, - 32659, - 32660, - 32661, - 32662, - 32663, - 32664, - 32665, - 32666, - 32667, - 32668, - 32669, - 32670, - 32671, - 32672, - 32673, - 32674, - 32675, - 32676, - 32677, - 32678, - 32679, - 32680, - 32681, - 32682, - 32683, - 32684, - 32685, - 32686, - 32687, - 32688, - 32689, - 32690, - 32691, - 32692, - 32693, - 32694, - 32695, - 32696, - 32697, - 32698, - 32699, - 32700, - 32701, - 32702, - 32703, - 32704, - 32705, - 32706, - 32707, - 32708, - 32709, - 32710, - 32711, - 32712, - 32713, - 32714, - 32715, - 32716, - 32717, - 32718, - 32719, - 32720, - 32721, - 32722, - 32723, - 32724, - 32725, - 32726, - 32727, - 32728, - 32729, - 32730, - 32731, - 32732, - 32733, - 32734, - 32735, - 32736, - 32737, - 32738, - 32739, - 32740, - 32741, - 32742, - 32743, - 32744, - 32745, - 32746, - 32747, - 32748, - 32749, - 32750, - 32751, - 32752, - 32753, - 32754, - 32755, - 32756, - 32757, - 32758, - 32759, - 32760, - 32761, - 32762, - 32763, - 32764, - 32765, - 32766, - 32767, - 32768, - 32769, - 32770, - 32771, - 32772, - 32773, - 32774, - 32775, - 32776, - 32777, - 32778, - 32779, - 32780, - 32781, - 32782, - 32783, - 32784, - 32785, - 32786, - 32787, - 32788, - 32789, - 32790, - 32791, - 32792, - 32793, - 32794, - 32795, - 32796, - 32797, - 32798, - 32799, - 32800, - 32801, - 32802, - 32803, - 32804, - 32805, - 32806, - 32807, - 32808, - 32809, - 32810, - 32811, - 32812, - 32813, - 32814, - 32815, - 32816, - 32817, - 32818, - 32819, - 32820, - 32821, - 32822, - 32823, - 32824, - 32825, - 32826, - 32827, - 32828, - 32829, - 32830, - 32831, - 32832, - 32833, - 32834, - 32835, - 32836, - 32837, - 32838, - 32839, - 32840, - 32841, - 32842, - 32843, - 32844, - 32845, - 32846, - 32847, - 32848, - 32849, - 32850, - 32851, - 32852, - 32853, - 32854, - 32855, - 32856, - 32857, - 32858, - 32859, - 32860, - 32861, - 32862, - 32863, - 32864, - 32865, - 32866, - 32867, - 32868, - 32869, - 32870, - 32871, - 32872, - 32873, - 32874, - 32875, - 32876, - 32877, - 32878, - 32879, - 32880, - 32881, - 32882, - 32883, - 32884, - 32885, - 32886, - 32887, - 32888, - 32889, - 32890, - 32891, - 32892, - 32893, - 32894, - 32895, - 32896, - 32897, - 32898, - 32899, - 32900, - 32901, - 32902, - 32903, - 32904, - 32905, - 32906, - 32907, - 32908, - 32909, - 32910, - 32911, - 32912, - 32913, - 32914, - 32915, - 32916, - 32917, - 32918, - 32919, - 32920, - 32921, - 32922, - 32923, - 32924, - 32925, - 32926, - 32927, - 32928, - 32929, - 32930, - 32931, - 32932, - 32933, - 32934, - 32935, - 32936, - 32937, - 32938, - 32939, - 32940, - 32941, - 32942, - 32943, - 32944, - 32945, - 32946, - 32947, - 32948, - 32949, - 32950, - 32951, - 32952, - 32953, - 32954, - 32955, - 32956, - 32957, - 32958, - 32959, - 32960, - 32961, - 32962, - 32963, - 32964, - 32965, - 32966, - 32967, - 32968, - 32969, - 32970, - 32971, - 32972, - 32973, - 32974, - 32975, - 32976, - 32977, - 32978, - 32979, - 32980, - 32981, - 32982, - 32983, - 32984, - 32985, - 32986, - 32987, - 32988, - 32989, - 32990, - 32991, - 32992, - 32993, - 32994, - 32995, - 32996, - 32997, - 32998, - 32999, - 33000, - 33001, - 33002, - 33003, - 33004, - 33005, - 33006, - 33007, - 33008, - 33009, - 33010, - 33011, - 33012, - 33013, - 33014, - 33015, - 33016, - 33017, - 33018, - 33019, - 33020, - 33021, - 33022, - 33023, - 33024, - 33025, - 33026, - 33027, - 33028, - 33029, - 33030, - 33031, - 33032, - 33033, - 33034, - 33035, - 33036, - 33037, - 33038, - 33039, - 33040, - 33041, - 33042, - 33043, - 33044, - 33045, - 33046, - 33047, - 33048, - 33049, - 33050, - 33051, - 33052, - 33053, - 33054, - 33055, - 33056, - 33057, - 33058, - 33059, - 33060, - 33061, - 33062, - 33063, - 33064, - 33065, - 33066, - 33067, - 33068, - 33069, - 33070, - 33071, - 33072, - 33073, - 33074, - 33075, - 33076, - 33077, - 33078, - 33079, - 33080, - 33081, - 33082, - 33083, - 33084, - 33085, - 33086, - 33087, - 33088, - 33089, - 33090, - 33091, - 33092, - 33093, - 33094, - 33095, - 33096, - 33097, - 33098, - 33099, - 33100, - 33101, - 33102, - 33103, - 33104, - 33105, - 33106, - 33107, - 33108, - 33109, - 33110, - 33111, - 33112, - 33113, - 33114, - 33115, - 33116, - 33117, - 33118, - 33119, - 33120, - 33121, - 33122, - 33123, - 33124, - 33125, - 33126, - 33127, - 33128, - 33129, - 33130, - 33131, - 33132, - 33133, - 33134, - 33135, - 33136, - 33137, - 33138, - 33139, - 33140, - 33141, - 33142, - 33143, - 33144, - 33145, - 33146, - 33147, - 33148, - 33149, - 33150, - 33151, - 33152, - 33153, - 33154, - 33155, - 33156, - 33157, - 33158, - 33159, - 33160, - 33161, - 33162, - 33163, - 33164, - 33165, - 33166, - 33167, - 33168, - 33169, - 33170, - 33171, - 33172, - 33173, - 33174, - 33175, - 33176, - 33177, - 33178, - 33179, - 33180, - 33181, - 33182, - 33183, - 33184, - 33185, - 33186, - 33187, - 33188, - 33189, - 33190, - 33191, - 33192, - 33193, - 33194, - 33195, - 33196, - 33197, - 33198, - 33199, - 33200, - 33201, - 33202, - 33203, - 33204, - 33205, - 33206, - 33207, - 33208, - 33209, - 33210, - 33211, - 33212, - 33213, - 33214, - 33215, - 33216, - 33217, - 33218, - 33219, - 33220, - 33221, - 33222, - 33223, - 33224, - 33225, - 33226, - 33227, - 33228, - 33229, - 33230, - 33231, - 33232, - 33233, - 33234, - 33235, - 33236, - 33237, - 33238, - 33239, - 33240, - 33241, - 33242, - 33243, - 33244, - 33245, - 33246, - 33247, - 33248, - 33249, - 33250, - 33251, - 33252, - 33253, - 33254, - 33255, - 33256, - 33257, - 33258, - 33259, - 33260, - 33261, - 33262, - 33263, - 33264, - 33265, - 33266, - 33267, - 33268, - 33269, - 33270, - 33271, - 33272, - 33273, - 33274, - 33275, - 33276, - 33277, - 33278, - 33279, - 33280, - 33281, - 33282, - 33283, - 33284, - 33285, - 33286, - 33287, - 33288, - 33289, - 33290, - 33291, - 33292, - 33293, - 33294, - 33295, - 33296, - 33297, - 33298, - 33299, - 33300, - 33301, - 33302, - 33303, - 33304, - 33305, - 33306, - 33307, - 33308, - 33309, - 33310, - 33311, - 33312, - 33313, - 33314, - 33315, - 33316, - 33317, - 33318, - 33319, - 33320, - 33321, - 33322, - 33323, - 33324, - 33325, - 33326, - 33327, - 33328, - 33329, - 33330, - 33331, - 33332, - 33333, - 33334, - 33335, - 33336, - 33337, - 33338, - 33339, - 33340, - 33341, - 33342, - 33343, - 33344, - 33345, - 33346, - 33347, - 33348, - 33349, - 33350, - 33351, - 33352, - 33353, - 33354, - 33355, - 33356, - 33357, - 33358, - 33359, - 33360, - 33361, - 33362, - 33363, - 33364, - 33365, - 33366, - 33367, - 33368, - 33369, - 33370, - 33371, - 33372, - 33373, - 33374, - 33375, - 33376, - 33377, - 33378, - 33379, - 33380, - 33381, - 33382, - 33383, - 33384, - 33385, - 33386, - 33387, - 33388, - 33389, - 33390, - 33391, - 33392, - 33393, - 33394, - 33395, - 33396, - 33397, - 33398, - 33399, - 33400, - 33401, - 33402, - 33403, - 33404, - 33405, - 33406, - 33407, - 33408, - 33409, - 33410, - 33411, - 33412, - 33413, - 33414, - 33415, - 33416, - 33417, - 33418, - 33419, - 33420, - 33421, - 33422, - 33423, - 33424, - 33425, - 33426, - 33427, - 33428, - 33429, - 33430, - 33431, - 33432, - 33433, - 33434, - 33435, - 33436, - 33437, - 33438, - 33439, - 33440, - 33441, - 33442, - 33443, - 33444, - 33445, - 33446, - 33447, - 33448, - 33449, - 33450, - 33451, - 33452, - 33453, - 33454, - 33455, - 33456, - 33457, - 33458, - 33459, - 33460, - 33461, - 33462, - 33463, - 33464, - 33465, - 33466, - 33467, - 33468, - 33469, - 33470, - 33471, - 33472, - 33473, - 33474, - 33475, - 33476, - 33477, - 33478, - 33479, - 33480, - 33481, - 33482, - 33483, - 33484, - 33485, - 33486, - 33487, - 33488, - 33489, - 33490, - 33491, - 33492, - 33493, - 33494, - 33495, - 33496, - 33497, - 33498, - 33499, - 33500, - 33501, - 33502, - 33503, - 33504, - 33505, - 33506, - 33507, - 33508, - 33509, - 33510, - 33511, - 33512, - 33513, - 33514, - 33515, - 33516, - 33517, - 33518, - 33519, - 33520, - 33521, - 33522, - 33523, - 33524, - 33525, - 33526, - 33527, - 33528, - 33529, - 33530, - 33531, - 33532, - 33533, - 33534, - 33535, - 33536, - 33537, - 33538, - 33539, - 33540, - 33541, - 33542, - 33543, - 33544, - 33545, - 33546, - 33547, - 33548, - 33549, - 33550, - 33551, - 33552, - 33553, - 33554, - 33555, - 33556, - 33557, - 33558, - 33559, - 33560, - 33561, - 33562, - 33563, - 33564, - 33565, - 33566, - 33567, - 33568, - 33569, - 33570, - 33571, - 33572, - 33573, - 33574, - 33575, - 33576, - 33577, - 33578, - 33579, - 33580, - 33581, - 33582, - 33583, - 33584, - 33585, - 33586, - 33587, - 33588, - 33589, - 33590, - 33591, - 33592, - 33593, - 33594, - 33595, - 33596, - 33597, - 33598, - 33599, - 33600, - 33601, - 33602, - 33603, - 33604, - 33605, - 33606, - 33607, - 33608, - 33609, - 33610, - 33611, - 33612, - 33613, - 33614, - 33615, - 33616, - 33617, - 33618, - 33619, - 33620, - 33621, - 33622, - 33623, - 33624, - 33625, - 33626, - 33627, - 33628, - 33629, - 33630, - 33631, - 33632, - 33633, - 33634, - 33635, - 33636, - 33637, - 33638, - 33639, - 33640, - 33641, - 33642, - 33643, - 33644, - 33645, - 33646, - 33647, - 33648, - 33649, - 33650, - 33651, - 33652, - 33653, - 33654, - 33655, - 33656, - 33657, - 33658, - 33659, - 33660, - 33661, - 33662, - 33663, - 33664, - 33665, - 33666, - 33667, - 33668, - 33669, - 33670, - 33671, - 33672, - 33673, - 33674, - 33675, - 33676, - 33677, - 33678, - 33679, - 33680, - 33681, - 33682, - 33683, - 33684, - 33685, - 33686, - 33687, - 33688, - 33689, - 33690, - 33691, - 33692, - 33693, - 33694, - 33695, - 33696, - 33697, - 33698, - 33699, - 33700, - 33701, - 33702, - 33703, - 33704, - 33705, - 33706, - 33707, - 33708, - 33709, - 33710, - 33711, - 33712, - 33713, - 33714, - 33715, - 33716, - 33717, - 33718, - 33719, - 33720, - 33721, - 33722, - 33723, - 33724, - 33725, - 33726, - 33727, - 33728, - 33729, - 33730, - 33731, - 33732, - 33733, - 33734, - 33735, - 33736, - 33737, - 33738, - 33739, - 33740, - 33741, - 33742, - 33743, - 33744, - 33745, - 33746, - 33747, - 33748, - 33749, - 33750, - 33751, - 33752, - 33753, - 33754, - 33755, - 33756, - 33757, - 33758, - 33759, - 33760, - 33761, - 33762, - 33763, - 33764, - 33765, - 33766, - 33767, - 33768, - 33769, - 33770, - 33771, - 33772, - 33773, - 33774, - 33775, - 33776, - 33777, - 33778, - 33779, - 33780, - 33781, - 33782, - 33783, - 33784, - 33785, - 33786, - 33787, - 33788, - 33789, - 33790, - 33791, - 33792, - 33793, - 33794, - 33795, - 33796, - 33797, - 33798, - 33799, - 33800, - 33801, - 33802, - 33803, - 33804, - 33805, - 33806, - 33807, - 33808, - 33809, - 33810, - 33811, - 33812, - 33813, - 33814, - 33815, - 33816, - 33817, - 33818, - 33819, - 33820, - 33821, - 33822, - 33823, - 33824, - 33825, - 33826, - 33827, - 33828, - 33829, - 33830, - 33831, - 33832, - 33833, - 33834, - 33835, - 33836, - 33837, - 33838, - 33839, - 33840, - 33841, - 33842, - 33843, - 33844, - 33845, - 33846, - 33847, - 33848, - 33849, - 33850, - 33851, - 33852, - 33853, - 33854, - 33855, - 33856, - 33857, - 33858, - 33859, - 33860, - 33861, - 33862, - 33863, - 33864, - 33865, - 33866, - 33867, - 33868, - 33869, - 33870, - 33871, - 33872, - 33873, - 33874, - 33875, - 33876, - 33877, - 33878, - 33879, - 33880, - 33881, - 33882, - 33883, - 33884, - 33885, - 33886, - 33887, - 33888, - 33889, - 33890, - 33891, - 33892, - 33893, - 33894, - 33895, - 33896, - 33897, - 33898, - 33899, - 33900, - 33901, - 33902, - 33903, - 33904, - 33905, - 33906, - 33907, - 33908, - 33909, - 33910, - 33911, - 33912, - 33913, - 33914, - 33915, - 33916, - 33917, - 33918, - 33919, - 33920, - 33921, - 33922, - 33923, - 33924, - 33925, - 33926, - 33927, - 33928, - 33929, - 33930, - 33931, - 33932, - 33933, - 33934, - 33935, - 33936, - 33937, - 33938, - 33939, - 33940, - 33941, - 33942, - 33943, - 33944, - 33945, - 33946, - 33947, - 33948, - 33949, - 33950, - 33951, - 33952, - 33953, - 33954, - 33955, - 33956, - 33957, - 33958, - 33959, - 33960, - 33961, - 33962, - 33963, - 33964, - 33965, - 33966, - 33967, - 33968, - 33969, - 33970, - 33971, - 33972, - 33973, - 33974, - 33975, - 33976, - 33977, - 33978, - 33979, - 33980, - 33981, - 33982, - 33983, - 33984, - 33985, - 33986, - 33987, - 33988, - 33989, - 33990, - 33991, - 33992, - 33993, - 33994, - 33995, - 33996, - 33997, - 33998, - 33999, - 34000, - 34001, - 34002, - 34003, - 34004, - 34005, - 34006, - 34007, - 34008, - 34009, - 34010, - 34011, - 34012, - 34013, - 34014, - 34015, - 34016, - 34017, - 34018, - 34019, - 34020, - 34021, - 34022, - 34023, - 34024, - 34025, - 34026, - 34027, - 34028, - 34029, - 34030, - 34031, - 34032, - 34033, - 34034, - 34035, - 34036, - 34037, - 34038, - 34039, - 34040, - 34041, - 34042, - 34043, - 34044, - 34045, - 34046, - 34047, - 34048, - 34049, - 34050, - 34051, - 34052, - 34053, - 34054, - 34055, - 34056, - 34057, - 34058, - 34059, - 34060, - 34061, - 34062, - 34063, - 34064, - 34065, - 34066, - 34067, - 34068, - 34069, - 34070, - 34071, - 34072, - 34073, - 34074, - 34075, - 34076, - 34077, - 34078, - 34079, - 34080, - 34081, - 34082, - 34083, - 34084, - 34085, - 34086, - 34087, - 34088, - 34089, - 34090, - 34091, - 34092, - 34093, - 34094, - 34095, - 34096, - 34097, - 34098, - 34099, - 34100, - 34101, - 34102, - 34103, - 34104, - 34105, - 34106, - 34107, - 34108, - 34109, - 34110, - 34111, - 34112, - 34113, - 34114, - 34115, - 34116, - 34117, - 34118, - 34119, - 34120, - 34121, - 34122, - 34123, - 34124, - 34125, - 34126, - 34127, - 34128, - 34129, - 34130, - 34131, - 34132, - 34133, - 34134, - 34135, - 34136, - 34137, - 34138, - 34139, - 34140, - 34141, - 34142, - 34143, - 34144, - 34145, - 34146, - 34147, - 34148, - 34149, - 34150, - 34151, - 34152, - 34153, - 34154, - 34155, - 34156, - 34157, - 34158, - 34159, - 34160, - 34161, - 34162, - 34163, - 34164, - 34165, - 34166, - 34167, - 34168, - 34169, - 34170, - 34171, - 34172, - 34173, - 34174, - 34175, - 34176, - 34177, - 34178, - 34179, - 34180, - 34181, - 34182, - 34183, - 34184, - 34185, - 34186, - 34187, - 34188, - 34189, - 34190, - 34191, - 34192, - 34193, - 34194, - 34195, - 34196, - 34197, - 34198, - 34199, - 34200, - 34201, - 34202, - 34203, - 34204, - 34205, - 34206, - 34207, - 34208, - 34209, - 34210, - 34211, - 34212, - 34213, - 34214, - 34215, - 34216, - 34217, - 34218, - 34219, - 34220, - 34221, - 34222, - 34223, - 34224, - 34225, - 34226, - 34227, - 34228, - 34229, - 34230, - 34231, - 34232, - 34233, - 34234, - 34235, - 34236, - 34237, - 34238, - 34239, - 34240, - 34241, - 34242, - 34243, - 34244, - 34245, - 34246, - 34247, - 34248, - 34249, - 34250, - 34251, - 34252, - 34253, - 34254, - 34255, - 34256, - 34257, - 34258, - 34259, - 34260, - 34261, - 34262, - 34263, - 34264, - 34265, - 34266, - 34267, - 34268, - 34269, - 34270, - 34271, - 34272, - 34273, - 34274, - 34275, - 34276, - 34277, - 34278, - 34279, - 34280, - 34281, - 34282, - 34283, - 34284, - 34285, - 34286, - 34287, - 34288, - 34289, - 34290, - 34291, - 34292, - 34293, - 34294, - 34295, - 34296, - 34297, - 34298, - 34299, - 34300, - 34301, - 34302, - 34303, - 34304, - 34305, - 34306, - 34307, - 34308, - 34309, - 34310, - 34311, - 34312, - 34313, - 34314, - 34315, - 34316, - 34317, - 34318, - 34319, - 34320, - 34321, - 34322, - 34323, - 34324, - 34325, - 34326, - 34327, - 34328, - 34329, - 34330, - 34331, - 34332, - 34333, - 34334, - 34335, - 34336, - 34337, - 34338, - 34339, - 34340, - 34341, - 34342, - 34343, - 34344, - 34345, - 34346, - 34347, - 34348, - 34349, - 34350, - 34351, - 34352, - 34353, - 34354, - 34355, - 34356, - 34357, - 34358, - 34359, - 34360, - 34361, - 34362, - 34363, - 34364, - 34365, - 34366, - 34367, - 34368, - 34369, - 34370, - 34371, - 34372, - 34373, - 34374, - 34375, - 34376, - 34377, - 34378, - 34379, - 34380, - 34381, - 34382, - 34383, - 34384, - 34385, - 34386, - 34387, - 34388, - 34389, - 34390, - 34391, - 34392, - 34393, - 34394, - 34395, - 34396, - 34397, - 34398, - 34399, - 34400, - 34401, - 34402, - 34403, - 34404, - 34405, - 34406, - 34407, - 34408, - 34409, - 34410, - 34411, - 34412, - 34413, - 34414, - 34415, - 34416, - 34417, - 34418, - 34419, - 34420, - 34421, - 34422, - 34423, - 34424, - 34425, - 34426, - 34427, - 34428, - 34429, - 34430, - 34431, - 34432, - 34433, - 34434, - 34435, - 34436, - 34437, - 34438, - 34439, - 34440, - 34441, - 34442, - 34443, - 34444, - 34445, - 34446, - 34447, - 34448, - 34449, - 34450, - 34451, - 34452, - 34453, - 34454, - 34455, - 34456, - 34457, - 34458, - 34459, - 34460, - 34461, - 34462, - 34463, - 34464, - 34465, - 34466, - 34467, - 34468, - 34469, - 34470, - 34471, - 34472, - 34473, - 34474, - 34475, - 34476, - 34477, - 34478, - 34479, - 34480, - 34481, - 34482, - 34483, - 34484, - 34485, - 34486, - 34487, - 34488, - 34489, - 34490, - 34491, - 34492, - 34493, - 34494, - 34495, - 34496, - 34497, - 34498, - 34499, - 34500, - 34501, - 34502, - 34503, - 34504, - 34505, - 34506, - 34507, - 34508, - 34509, - 34510, - 34511, - 34512, - 34513, - 34514, - 34515, - 34516, - 34517, - 34518, - 34519, - 34520, - 34521, - 34522, - 34523, - 34524, - 34525, - 34526, - 34527, - 34528, - 34529, - 34530, - 34531, - 34532, - 34533, - 34534, - 34535, - 34536, - 34537, - 34538, - 34539, - 34540, - 34541, - 34542, - 34543, - 34544, - 34545, - 34546, - 34547, - 34548, - 34549, - 34550, - 34551, - 34552, - 34553, - 34554, - 34555, - 34556, - 34557, - 34558, - 34559, - 34560, - 34561, - 34562, - 34563, - 34564, - 34565, - 34566, - 34567, - 34568, - 34569, - 34570, - 34571, - 34572, - 34573, - 34574, - 34575, - 34576, - 34577, - 34578, - 34579, - 34580, - 34581, - 34582, - 34583, - 34584, - 34585, - 34586, - 34587, - 34588, - 34589, - 34590, - 34591, - 34592, - 34593, - 34594, - 34595, - 34596, - 34597, - 34598, - 34599, - 34600, - 34601, - 34602, - 34603, - 34604, - 34605, - 34606, - 34607, - 34608, - 34609, - 34610, - 34611, - 34612, - 34613, - 34614, - 34615, - 34616, - 34617, - 34618, - 34619, - 34620, - 34621, - 34622, - 34623, - 34624, - 34625, - 34626, - 34627, - 34628, - 34629, - 34630, - 34631, - 34632, - 34633, - 34634, - 34635, - 34636, - 34637, - 34638, - 34639, - 34640, - 34641, - 34642, - 34643, - 34644, - 34645, - 34646, - 34647, - 34648, - 34649, - 34650, - 34651, - 34652, - 34653, - 34654, - 34655, - 34656, - 34657, - 34658, - 34659, - 34660, - 34661, - 34662, - 34663, - 34664, - 34665, - 34666, - 34667, - 34668, - 34669, - 34670, - 34671, - 34672, - 34673, - 34674, - 34675, - 34676, - 34677, - 34678, - 34679, - 34680, - 34681, - 34682, - 34683, - 34684, - 34685, - 34686, - 34687, - 34688, - 34689, - 34690, - 34691, - 34692, - 34693, - 34694, - 34695, - 34696, - 34697, - 34698, - 34699, - 34700, - 34701, - 34702, - 34703, - 34704, - 34705, - 34706, - 34707, - 34708, - 34709, - 34710, - 34711, - 34712, - 34713, - 34714, - 34715, - 34716, - 34717, - 34718, - 34719, - 34720, - 34721, - 34722, - 34723, - 34724, - 34725, - 34726, - 34727, - 34728, - 34729, - 34730, - 34731, - 34732, - 34733, - 34734, - 34735, - 34736, - 34737, - 34738, - 34739, - 34740, - 34741, - 34742, - 34743, - 34744, - 34745, - 34746, - 34747, - 34748, - 34749, - 34750, - 34751, - 34752, - 34753, - 34754, - 34755, - 34756, - 34757, - 34758, - 34759, - 34760, - 34761, - 34762, - 34763, - 34764, - 34765, - 34766, - 34767, - 34768, - 34769, - 34770, - 34771, - 34772, - 34773, - 34774, - 34775, - 34776, - 34777, - 34778, - 34779, - 34780, - 34781, - 34782, - 34783, - 34784, - 34785, - 34786, - 34787, - 34788, - 34789, - 34790, - 34791, - 34792, - 34793, - 34794, - 34795, - 34796, - 34797, - 34798, - 34799, - 34800, - 34801, - 34802, - 34803, - 34804, - 34805, - 34806, - 34807, - 34808, - 34809, - 34810, - 34811, - 34812, - 34813, - 34814, - 34815, - 34816, - 34817, - 34818, - 34819, - 34820, - 34821, - 34822, - 34823, - 34824, - 34825, - 34826, - 34827, - 34828, - 34829, - 34830, - 34831, - 34832, - 34833, - 34834, - 34835, - 34836, - 34837, - 34838, - 34839, - 34840, - 34841, - 34842, - 34843, - 34844, - 34845, - 34846, - 34847, - 34848, - 34849, - 34850, - 34851, - 34852, - 34853, - 34854, - 34855, - 34856, - 34857, - 34858, - 34859, - 34860, - 34861, - 34862, - 34863, - 34864, - 34865, - 34866, - 34867, - 34868, - 34869, - 34870, - 34871, - 34872, - 34873, - 34874, - 34875, - 34876, - 34877, - 34878, - 34879, - 34880, - 34881, - 34882, - 34883, - 34884, - 34885, - 34886, - 34887, - 34888, - 34889, - 34890, - 34891, - 34892, - 34893, - 34894, - 34895, - 34896, - 34897, - 34898, - 34899, - 34900, - 34901, - 34902, - 34903, - 34904, - 34905, - 34906, - 34907, - 34908, - 34909, - 34910, - 34911, - 34912, - 34913, - 34914, - 34915, - 34916, - 34917, - 34918, - 34919, - 34920, - 34921, - 34922, - 34923, - 34924, - 34925, - 34926, - 34927, - 34928, - 34929, - 34930, - 34931, - 34932, - 34933, - 34934, - 34935, - 34936, - 34937, - 34938, - 34939, - 34940, - 34941, - 34942, - 34943, - 34944, - 34945, - 34946, - 34947, - 34948, - 34949, - 34950, - 34951, - 34952, - 34953, - 34954, - 34955, - 34956, - 34957, - 34958, - 34959, - 34960, - 34961, - 34962, - 34963, - 34964, - 34965, - 34966, - 34967, - 34968, - 34969, - 34970, - 34971, - 34972, - 34973, - 34974, - 34975, - 34976, - 34977, - 34978, - 34979, - 34980, - 34981, - 34982, - 34983, - 34984, - 34985, - 34986, - 34987, - 34988, - 34989, - 34990, - 34991, - 34992, - 34993, - 34994, - 34995, - 34996, - 34997, - 34998, - 34999, - 35000, - 35001, - 35002, - 35003, - 35004, - 35005, - 35006, - 35007, - 35008, - 35009, - 35010, - 35011, - 35012, - 35013, - 35014, - 35015, - 35016, - 35017, - 35018, - 35019, - 35020, - 35021, - 35022, - 35023, - 35024, - 35025, - 35026, - 35027, - 35028, - 35029, - 35030, - 35031, - 35032, - 35033, - 35034, - 35035, - 35036, - 35037, - 35038, - 35039, - 35040, - 35041, - 35042, - 35043, - 35044, - 35045, - 35046, - 35047, - 35048, - 35049, - 35050, - 35051, - 35052, - 35053, - 35054, - 35055, - 35056, - 35057, - 35058, - 35059, - 35060, - 35061, - 35062, - 35063, - 35064, - 35065, - 35066, - 35067, - 35068, - 35069, - 35070, - 35071, - 35072, - 35073, - 35074, - 35075, - 35076, - 35077, - 35078, - 35079, - 35080, - 35081, - 35082, - 35083, - 35084, - 35085, - 35086, - 35087, - 35088, - 35089, - 35090, - 35091, - 35092, - 35093, - 35094, - 35095, - 35096, - 35097, - 35098, - 35099, - 35100, - 35101, - 35102, - 35103, - 35104, - 35105, - 35106, - 35107, - 35108, - 35109, - 35110, - 35111, - 35112, - 35113, - 35114, - 35115, - 35116, - 35117, - 35118, - 35119, - 35120, - 35121, - 35122, - 35123, - 35124, - 35125, - 35126, - 35127, - 35128, - 35129, - 35130, - 35131, - 35132, - 35133, - 35134, - 35135, - 35136, - 35137, - 35138, - 35139, - 35140, - 35141, - 35142, - 35143, - 35144, - 35145, - 35146, - 35147, - 35148, - 35149, - 35150, - 35151, - 35152, - 35153, - 35154, - 35155, - 35156, - 35157, - 35158, - 35159, - 35160, - 35161, - 35162, - 35163, - 35164, - 35165, - 35166, - 35167, - 35168, - 35169, - 35170, - 35171, - 35172, - 35173, - 35174, - 35175, - 35176, - 35177, - 35178, - 35179, - 35180, - 35181, - 35182, - 35183, - 35184, - 35185, - 35186, - 35187, - 35188, - 35189, - 35190, - 35191, - 35192, - 35193, - 35194, - 35195, - 35196, - 35197, - 35198, - 35199, - 35200, - 35201, - 35202, - 35203, - 35204, - 35205, - 35206, - 35207, - 35208, - 35209, - 35210, - 35211, - 35212, - 35213, - 35214, - 35215, - 35216, - 35217, - 35218, - 35219, - 35220, - 35221, - 35222, - 35223, - 35224, - 35225, - 35226, - 35227, - 35228, - 35229, - 35230, - 35231, - 35232, - 35233, - 35234, - 35235, - 35236, - 35237, - 35238, - 35239, - 35240, - 35241, - 35242, - 35243, - 35244, - 35245, - 35246, - 35247, - 35248, - 35249, - 35250, - 35251, - 35252, - 35253, - 35254, - 35255, - 35256, - 35257, - 35258, - 35259, - 35260, - 35261, - 35262, - 35263, - 35264, - 35265, - 35266, - 35267, - 35268, - 35269, - 35270, - 35271, - 35272, - 35273, - 35274, - 35275, - 35276, - 35277, - 35278, - 35279, - 35280, - 35281, - 35282, - 35283, - 35284, - 35285, - 35286, - 35287, - 35288, - 35289, - 35290, - 35291, - 35292, - 35293, - 35294, - 35295, - 35296, - 35297, - 35298, - 35299, - 35300, - 35301, - 35302, - 35303, - 35304, - 35305, - 35306, - 35307, - 35308, - 35309, - 35310, - 35311, - 35312, - 35313, - 35314, - 35315, - 35316, - 35317, - 35318, - 35319, - 35320, - 35321, - 35322, - 35323, - 35324, - 35325, - 35326, - 35327, - 35328, - 35329, - 35330, - 35331, - 35332, - 35333, - 35334, - 35335, - 35336, - 35337, - 35338, - 35339, - 35340, - 35341, - 35342, - 35343, - 35344, - 35345, - 35346, - 35347, - 35348, - 35349, - 35350, - 35351, - 35352, - 35353, - 35354, - 35355, - 35356, - 35357, - 35358, - 35359, - 35360, - 35361, - 35362, - 35363, - 35364, - 35365, - 35366, - 35367, - 35368, - 35369, - 35370, - 35371, - 35372, - 35373, - 35374, - 35375, - 35376, - 35377, - 35378, - 35379, - 35380, - 35381, - 35382, - 35383, - 35384, - 35385, - 35386, - 35387, - 35388, - 35389, - 35390, - 35391, - 35392, - 35393, - 35394, - 35395, - 35396, - 35397, - 35398, - 35399, - 35400, - 35401, - 35402, - 35403, - 35404, - 35405, - 35406, - 35407, - 35408, - 35409, - 35410, - 35411, - 35412, - 35413, - 35414, - 35415, - 35416, - 35417, - 35418, - 35419, - 35420, - 35421, - 35422, - 35423, - 35424, - 35425, - 35426, - 35427, - 35428, - 35429, - 35430, - 35431, - 35432, - 35433, - 35434, - 35435, - 35436, - 35437, - 35438, - 35439, - 35440, - 35441, - 35442, - 35443, - 35444, - 35445, - 35446, - 35447, - 35448, - 35449, - 35450, - 35451, - 35452, - 35453, - 35454, - 35455, - 35456, - 35457, - 35458, - 35459, - 35460, - 35461, - 35462, - 35463, - 35464, - 35465, - 35466, - 35467, - 35468, - 35469, - 35470, - 35471, - 35472, - 35473, - 35474, - 35475, - 35476, - 35477, - 35478, - 35479, - 35480, - 35481, - 35482, - 35483, - 35484, - 35485, - 35486, - 35487, - 35488, - 35489, - 35490, - 35491, - 35492, - 35493, - 35494, - 35495, - 35496, - 35497, - 35498, - 35499, - 35500, - 35501, - 35502, - 35503, - 35504, - 35505, - 35506, - 35507, - 35508, - 35509, - 35510, - 35511, - 35512, - 35513, - 35514, - 35515, - 35516, - 35517, - 35518, - 35519, - 35520, - 35521, - 35522, - 35523, - 35524, - 35525, - 35526, - 35527, - 35528, - 35529, - 35530, - 35531, - 35532, - 35533, - 35534, - 35535, - 35536, - 35537, - 35538, - 35539, - 35540, - 35541, - 35542, - 35543, - 35544, - 35545, - 35546, - 35547, - 35548, - 35549, - 35550, - 35551, - 35552, - 35553, - 35554, - 35555, - 35556, - 35557, - 35558, - 35559, - 35560, - 35561, - 35562, - 35563, - 35564, - 35565, - 35566, - 35567, - 35568, - 35569, - 35570, - 35571, - 35572, - 35573, - 35574, - 35575, - 35576, - 35577, - 35578, - 35579, - 35580, - 35581, - 35582, - 35583, - 35584, - 35585, - 35586, - 35587, - 35588, - 35589, - 35590, - 35591, - 35592, - 35593, - 35594, - 35595, - 35596, - 35597, - 35598, - 35599, - 35600, - 35601, - 35602, - 35603, - 35604, - 35605, - 35606, - 35607, - 35608, - 35609, - 35610, - 35611, - 35612, - 35613, - 35614, - 35615, - 35616, - 35617, - 35618, - 35619, - 35620, - 35621, - 35622, - 35623, - 35624, - 35625, - 35626, - 35627, - 35628, - 35629, - 35630, - 35631, - 35632, - 35633, - 35634, - 35635, - 35636, - 35637, - 35638, - 35639, - 35640, - 35641, - 35642, - 35643, - 35644, - 35645, - 35646, - 35647, - 35648, - 35649, - 35650, - 35651, - 35652, - 35653, - 35654, - 35655, - 35656, - 35657, - 35658, - 35659, - 35660, - 35661, - 35662, - 35663, - 35664, - 35665, - 35666, - 35667, - 35668, - 35669, - 35670, - 35671, - 35672, - 35673, - 35674, - 35675, - 35676, - 35677, - 35678, - 35679, - 35680, - 35681, - 35682, - 35683, - 35684, - 35685, - 35686, - 35687, - 35688, - 35689, - 35690, - 35691, - 35692, - 35693, - 35694, - 35695, - 35696, - 35697, - 35698, - 35699, - 35700, - 35701, - 35702, - 35703, - 35704, - 35705, - 35706, - 35707, - 35708, - 35709, - 35710, - 35711, - 35712, - 35713, - 35714, - 35715, - 35716, - 35717, - 35718, - 35719, - 35720, - 35721, - 35722, - 35723, - 35724, - 35725, - 35726, - 35727, - 35728, - 35729, - 35730, - 35731, - 35732, - 35733, - 35734, - 35735, - 35736, - 35737, - 35738, - 35739, - 35740, - 35741, - 35742, - 35743, - 35744, - 35745, - 35746, - 35747, - 35748, - 35749, - 35750, - 35751, - 35752, - 35753, - 35754, - 35755, - 35756, - 35757, - 35758, - 35759, - 35760, - 35761, - 35762, - 35763, - 35764, - 35765, - 35766, - 35767, - 35768, - 35769, - 35770, - 35771, - 35772, - 35773, - 35774, - 35775, - 35776, - 35777, - 35778, - 35779, - 35780, - 35781, - 35782, - 35783, - 35784, - 35785, - 35786, - 35787, - 35788, - 35789, - 35790, - 35791, - 35792, - 35793, - 35794, - 35795, - 35796, - 35797, - 35798, - 35799, - 35800, - 35801, - 35802, - 35803, - 35804, - 35805, - 35806, - 35807, - 35808, - 35809, - 35810, - 35811, - 35812, - 35813, - 35814, - 35815, - 35816, - 35817, - 35818, - 35819, - 35820, - 35821, - 35822, - 35823, - 35824, - 35825, - 35826, - 35827, - 35828, - 35829, - 35830, - 35831, - 35832, - 35833, - 35834, - 35835, - 35836, - 35837, - 35838, - 35839, - 35840, - 35841, - 35842, - 35843, - 35844, - 35845, - 35846, - 35847, - 35848, - 35849, - 35850, - 35851, - 35852, - 35853, - 35854, - 35855, - 35856, - 35857, - 35858, - 35859, - 35860, - 35861, - 35862, - 35863, - 35864, - 35865, - 35866, - 35867, - 35868, - 35869, - 35870, - 35871, - 35872, - 35873, - 35874, - 35875, - 35876, - 35877, - 35878, - 35879, - 35880, - 35881, - 35882, - 35883, - 35884, - 35885, - 35886, - 35887, - 35888, - 35889, - 35890, - 35891, - 35892, - 35893, - 35894, - 35895, - 35896, - 35897, - 35898, - 35899, - 35900, - 35901, - 35902, - 35903, - 35904, - 35905, - 35906, - 35907, - 35908, - 35909, - 35910, - 35911, - 35912, - 35913, - 35914, - 35915, - 35916, - 35917, - 35918, - 35919, - 35920, - 35921, - 35922, - 35923, - 35924, - 35925, - 35926, - 35927, - 35928, - 35929, - 35930, - 35931, - 35932, - 35933, - 35934, - 35935, - 35936, - 35937, - 35938, - 35939, - 35940, - 35941, - 35942, - 35943, - 35944, - 35945, - 35946, - 35947, - 35948, - 35949, - 35950, - 35951, - 35952, - 35953, - 35954, - 35955, - 35956, - 35957, - 35958, - 35959, - 35960, - 35961, - 35962, - 35963, - 35964, - 35965, - 35966, - 35967, - 35968, - 35969, - 35970, - 35971, - 35972, - 35973, - 35974, - 35975, - 35976, - 35977, - 35978, - 35979, - 35980, - 35981, - 35982, - 35983, - 35984, - 35985, - 35986, - 35987, - 35988, - 35989, - 35990, - 35991, - 35992, - 35993, - 35994, - 35995, - 35996, - 35997, - 35998, - 35999, - 36000, - 36001, - 36002, - 36003, - 36004, - 36005, - 36006, - 36007, - 36008, - 36009, - 36010, - 36011, - 36012, - 36013, - 36014, - 36015, - 36016, - 36017, - 36018, - 36019, - 36020, - 36021, - 36022, - 36023, - 36024, - 36025, - 36026, - 36027, - 36028, - 36029, - 36030, - 36031, - 36032, - 36033, - 36034, - 36035, - 36036, - 36037, - 36038, - 36039, - 36040, - 36041, - 36042, - 36043, - 36044, - 36045, - 36046, - 36047, - 36048, - 36049, - 36050, - 36051, - 36052, - 36053, - 36054, - 36055, - 36056, - 36057, - 36058, - 36059, - 36060, - 36061, - 36062, - 36063, - 36064, - 36065, - 36066, - 36067, - 36068, - 36069, - 36070, - 36071, - 36072, - 36073, - 36074, - 36075, - 36076, - 36077, - 36078, - 36079, - 36080, - 36081, - 36082, - 36083, - 36084, - 36085, - 36086, - 36087, - 36088, - 36089, - 36090, - 36091, - 36092, - 36093, - 36094, - 36095, - 36096, - 36097, - 36098, - 36099, - 36100, - 36101, - 36102, - 36103, - 36104, - 36105, - 36106, - 36107, - 36108, - 36109, - 36110, - 36111, - 36112, - 36113, - 36114, - 36115, - 36116, - 36117, - 36118, - 36119, - 36120, - 36121, - 36122, - 36123, - 36124, - 36125, - 36126, - 36127, - 36128, - 36129, - 36130, - 36131, - 36132, - 36133, - 36134, - 36135, - 36136, - 36137, - 36138, - 36139, - 36140, - 36141, - 36142, - 36143, - 36144, - 36145, - 36146, - 36147, - 36148, - 36149, - 36150, - 36151, - 36152, - 36153, - 36154, - 36155, - 36156, - 36157, - 36158, - 36159, - 36160, - 36161, - 36162, - 36163, - 36164, - 36165, - 36166, - 36167, - 36168, - 36169, - 36170, - 36171, - 36172, - 36173, - 36174, - 36175, - 36176, - 36177, - 36178, - 36179, - 36180, - 36181, - 36182, - 36183, - 36184, - 36185, - 36186, - 36187, - 36188, - 36189, - 36190, - 36191, - 36192, - 36193, - 36194, - 36195, - 36196, - 36197, - 36198, - 36199, - 36200, - 36201, - 36202, - 36203, - 36204, - 36205, - 36206, - 36207, - 36208, - 36209, - 36210, - 36211, - 36212, - 36213, - 36214, - 36215, - 36216, - 36217, - 36218, - 36219, - 36220, - 36221, - 36222, - 36223, - 36224, - 36225, - 36226, - 36227, - 36228, - 36229, - 36230, - 36231, - 36232, - 36233, - 36234, - 36235, - 36236, - 36237, - 36238, - 36239, - 36240, - 36241, - 36242, - 36243, - 36244, - 36245, - 36246, - 36247, - 36248, - 36249, - 36250, - 36251, - 36252, - 36253, - 36254, - 36255, - 36256, - 36257, - 36258, - 36259, - 36260, - 36261, - 36262, - 36263, - 36264, - 36265, - 36266, - 36267, - 36268, - 36269, - 36270, - 36271, - 36272, - 36273, - 36274, - 36275, - 36276, - 36277, - 36278, - 36279, - 36280, - 36281, - 36282, - 36283, - 36284, - 36285, - 36286, - 36287, - 36288, - 36289, - 36290, - 36291, - 36292, - 36293, - 36294, - 36295, - 36296, - 36297, - 36298, - 36299, - 36300, - 36301, - 36302, - 36303, - 36304, - 36305, - 36306, - 36307, - 36308, - 36309, - 36310, - 36311, - 36312, - 36313, - 36314, - 36315, - 36316, - 36317, - 36318, - 36319, - 36320, - 36321, - 36322, - 36323, - 36324, - 36325, - 36326, - 36327, - 36328, - 36329, - 36330, - 36331, - 36332, - 36333, - 36334, - 36335, - 36336, - 36337, - 36338, - 36339, - 36340, - 36341, - 36342, - 36343, - 36344, - 36345, - 36346, - 36347, - 36348, - 36349, - 36350, - 36351, - 36352, - 36353, - 36354, - 36355, - 36356, - 36357, - 36358, - 36359, - 36360, - 36361, - 36362, - 36363, - 36364, - 36365, - 36366, - 36367, - 36368, - 36369, - 36370, - 36371, - 36372, - 36373, - 36374, - 36375, - 36376, - 36377, - 36378, - 36379, - 36380, - 36381, - 36382, - 36383, - 36384, - 36385, - 36386, - 36387, - 36388, - 36389, - 36390, - 36391, - 36392, - 36393, - 36394, - 36395, - 36396, - 36397, - 36398, - 36399, - 36400, - 36401, - 36402, - 36403, - 36404, - 36405, - 36406, - 36407, - 36408, - 36409, - 36410, - 36411, - 36412, - 36413, - 36414, - 36415, - 36416, - 36417, - 36418, - 36419, - 36420, - 36421, - 36422, - 36423, - 36424, - 36425, - 36426, - 36427, - 36428, - 36429, - 36430, - 36431, - 36432, - 36433, - 36434, - 36435, - 36436, - 36437, - 36438, - 36439, - 36440, - 36441, - 36442, - 36443, - 36444, - 36445, - 36446, - 36447, - 36448, - 36449, - 36450, - 36451, - 36452, - 36453, - 36454, - 36455, - 36456, - 36457, - 36458, - 36459, - 36460, - 36461, - 36462, - 36463, - 36464, - 36465, - 36466, - 36467, - 36468, - 36469, - 36470, - 36471, - 36472, - 36473, - 36474, - 36475, - 36476, - 36477, - 36478, - 36479, - 36480, - 36481, - 36482, - 36483, - 36484, - 36485, - 36486, - 36487, - 36488, - 36489, - 36490, - 36491, - 36492, - 36493, - 36494, - 36495, - 36496, - 36497, - 36498, - 36499, - 36500, - 36501, - 36502, - 36503, - 36504, - 36505, - 36506, - 36507, - 36508, - 36509, - 36510, - 36511, - 36512, - 36513, - 36514, - 36515, - 36516, - 36517, - 36518, - 36519, - 36520, - 36521, - 36522, - 36523, - 36524, - 36525, - 36526, - 36527, - 36528, - 36529, - 36530, - 36531, - 36532, - 36533, - 36534, - 36535, - 36536, - 36537, - 36538, - 36539, - 36540, - 36541, - 36542, - 36543, - 36544, - 36545, - 36546, - 36547, - 36548, - 36549, - 36550, - 36551, - 36552, - 36553, - 36554, - 36555, - 36556, - 36557, - 36558, - 36559, - 36560, - 36561, - 36562, - 36563, - 36564, - 36565, - 36566, - 36567, - 36568, - 36569, - 36570, - 36571, - 36572, - 36573, - 36574, - 36575, - 36576, - 36577, - 36578, - 36579, - 36580, - 36581, - 36582, - 36583, - 36584, - 36585, - 36586, - 36587, - 36588, - 36589, - 36590, - 36591, - 36592, - 36593, - 36594, - 36595, - 36596, - 36597, - 36598, - 36599, - 36600, - 36601, - 36602, - 36603, - 36604, - 36605, - 36606, - 36607, - 36608, - 36609, - 36610, - 36611, - 36612, - 36613, - 36614, - 36615, - 36616, - 36617, - 36618, - 36619, - 36620, - 36621, - 36622, - 36623, - 36624, - 36625, - 36626, - 36627, - 36628, - 36629, - 36630, - 36631, - 36632, - 36633, - 36634, - 36635, - 36636, - 36637, - 36638, - 36639, - 36640, - 36641, - 36642, - 36643, - 36644, - 36645, - 36646, - 36647, - 36648, - 36649, - 36650, - 36651, - 36652, - 36653, - 36654, - 36655, - 36656, - 36657, - 36658, - 36659, - 36660, - 36661, - 36662, - 36663, - 36664, - 36665, - 36666, - 36667, - 36668, - 36669, - 36670, - 36671, - 36672, - 36673, - 36674, - 36675, - 36676, - 36677, - 36678, - 36679, - 36680, - 36681, - 36682, - 36683, - 36684, - 36685, - 36686, - 36687, - 36688, - 36689, - 36690, - 36691, - 36692, - 36693, - 36694, - 36695, - 36696, - 36697, - 36698, - 36699, - 36700, - 36701, - 36702, - 36703, - 36704, - 36705, - 36706, - 36707, - 36708, - 36709, - 36710, - 36711, - 36712, - 36713, - 36714, - 36715, - 36716, - 36717, - 36718, - 36719, - 36720, - 36721, - 36722, - 36723, - 36724, - 36725, - 36726, - 36727, - 36728, - 36729, - 36730, - 36731, - 36732, - 36733, - 36734, - 36735, - 36736, - 36737, - 36738, - 36739, - 36740, - 36741, - 36742, - 36743, - 36744, - 36745, - 36746, - 36747, - 36748, - 36749, - 36750, - 36751, - 36752, - 36753, - 36754, - 36755, - 36756, - 36757, - 36758, - 36759, - 36760, - 36761, - 36762, - 36763, - 36764, - 36765, - 36766, - 36767, - 36768, - 36769, - 36770, - 36771, - 36772, - 36773, - 36774, - 36775, - 36776, - 36777, - 36778, - 36779, - 36780, - 36781, - 36782, - 36783, - 36784, - 36785, - 36786, - 36787, - 36788, - 36789, - 36790, - 36791, - 36792, - 36793, - 36794, - 36795, - 36796, - 36797, - 36798, - 36799, - 36800, - 36801, - 36802, - 36803, - 36804, - 36805, - 36806, - 36807, - 36808, - 36809, - 36810, - 36811, - 36812, - 36813, - 36814, - 36815, - 36816, - 36817, - 36818, - 36819, - 36820, - 36821, - 36822, - 36823, - 36824, - 36825, - 36826, - 36827, - 36828, - 36829, - 36830, - 36831, - 36832, - 36833, - 36834, - 36835, - 36836, - 36837, - 36838, - 36839, - 36840, - 36841, - 36842, - 36843, - 36844, - 36845, - 36846, - 36847, - 36848, - 36849, - 36850, - 36851, - 36852, - 36853, - 36854, - 36855, - 36856, - 36857, - 36858, - 36859, - 36860, - 36861, - 36862, - 36863, - 36864, - 36865, - 36866, - 36867, - 36868, - 36869, - 36870, - 36871, - 36872, - 36873, - 36874, - 36875, - 36876, - 36877, - 36878, - 36879, - 36880, - 36881, - 36882, - 36883, - 36884, - 36885, - 36886, - 36887, - 36888, - 36889, - 36890, - 36891, - 36892, - 36893, - 36894, - 36895, - 36896, - 36897, - 36898, - 36899, - 36900, - 36901, - 36902, - 36903, - 36904, - 36905, - 36906, - 36907, - 36908, - 36909, - 36910, - 36911, - 36912, - 36913, - 36914, - 36915, - 36916, - 36917, - 36918, - 36919, - 36920, - 36921, - 36922, - 36923, - 36924, - 36925, - 36926, - 36927, - 36928, - 36929, - 36930, - 36931, - 36932, - 36933, - 36934, - 36935, - 36936, - 36937, - 36938, - 36939, - 36940, - 36941, - 36942, - 36943, - 36944, - 36945, - 36946, - 36947, - 36948, - 36949, - 36950, - 36951, - 36952, - 36953, - 36954, - 36955, - 36956, - 36957, - 36958, - 36959, - 36960, - 36961, - 36962, - 36963, - 36964, - 36965, - 36966, - 36967, - 36968, - 36969, - 36970, - 36971, - 36972, - 36973, - 36974, - 36975, - 36976, - 36977, - 36978, - 36979, - 36980, - 36981, - 36982, - 36983, - 36984, - 36985, - 36986, - 36987, - 36988, - 36989, - 36990, - 36991, - 36992, - 36993, - 36994, - 36995, - 36996, - 36997, - 36998, - 36999, - 37000, - 37001, - 37002, - 37003, - 37004, - 37005, - 37006, - 37007, - 37008, - 37009, - 37010, - 37011, - 37012, - 37013, - 37014, - 37015, - 37016, - 37017, - 37018, - 37019, - 37020, - 37021, - 37022, - 37023, - 37024, - 37025, - 37026, - 37027, - 37028, - 37029, - 37030, - 37031, - 37032, - 37033, - 37034, - 37035, - 37036, - 37037, - 37038, - 37039, - 37040, - 37041, - 37042, - 37043, - 37044, - 37045, - 37046, - 37047, - 37048, - 37049, - 37050, - 37051, - 37052, - 37053, - 37054, - 37055, - 37056, - 37057, - 37058, - 37059, - 37060, - 37061, - 37062, - 37063, - 37064, - 37065, - 37066, - 37067, - 37068, - 37069, - 37070, - 37071, - 37072, - 37073, - 37074, - 37075, - 37076, - 37077, - 37078, - 37079, - 37080, - 37081, - 37082, - 37083, - 37084, - 37085, - 37086, - 37087, - 37088, - 37089, - 37090, - 37091, - 37092, - 37093, - 37094, - 37095, - 37096, - 37097, - 37098, - 37099, - 37100, - 37101, - 37102, - 37103, - 37104, - 37105, - 37106, - 37107, - 37108, - 37109, - 37110, - 37111, - 37112, - 37113, - 37114, - 37115, - 37116, - 37117, - 37118, - 37119, - 37120, - 37121, - 37122, - 37123, - 37124, - 37125, - 37126, - 37127, - 37128, - 37129, - 37130, - 37131, - 37132, - 37133, - 37134, - 37135, - 37136, - 37137, - 37138, - 37139, - 37140, - 37141, - 37142, - 37143, - 37144, - 37145, - 37146, - 37147, - 37148, - 37149, - 37150, - 37151, - 37152, - 37153, - 37154, - 37155, - 37156, - 37157, - 37158, - 37159, - 37160, - 37161, - 37162, - 37163, - 37164, - 37165, - 37166, - 37167, - 37168, - 37169, - 37170, - 37171, - 37172, - 37173, - 37174, - 37175, - 37176, - 37177, - 37178, - 37179, - 37180, - 37181, - 37182, - 37183, - 37184, - 37185, - 37186, - 37187, - 37188, - 37189, - 37190, - 37191, - 37192, - 37193, - 37194, - 37195, - 37196, - 37197, - 37198, - 37199, - 37200, - 37201, - 37202, - 37203, - 37204, - 37205, - 37206, - 37207, - 37208, - 37209, - 37210, - 37211, - 37212, - 37213, - 37214, - 37215, - 37216, - 37217, - 37218, - 37219, - 37220, - 37221, - 37222, - 37223, - 37224, - 37225, - 37226, - 37227, - 37228, - 37229, - 37230, - 37231, - 37232, - 37233, - 37234, - 37235, - 37236, - 37237, - 37238, - 37239, - 37240, - 37241, - 37242, - 37243, - 37244, - 37245, - 37246, - 37247, - 37248, - 37249, - 37250, - 37251, - 37252, - 37253, - 37254, - 37255, - 37256, - 37257, - 37258, - 37259, - 37260, - 37261, - 37262, - 37263, - 37264, - 37265, - 37266, - 37267, - 37268, - 37269, - 37270, - 37271, - 37272, - 37273, - 37274, - 37275, - 37276, - 37277, - 37278, - 37279, - 37280, - 37281, - 37282, - 37283, - 37284, - 37285, - 37286, - 37287, - 37288, - 37289, - 37290, - 37291, - 37292, - 37293, - 37294, - 37295, - 37296, - 37297, - 37298, - 37299, - 37300, - 37301, - 37302, - 37303, - 37304, - 37305, - 37306, - 37307, - 37308, - 37309, - 37310, - 37311, - 37312, - 37313, - 37314, - 37315, - 37316, - 37317, - 37318, - 37319, - 37320, - 37321, - 37322, - 37323, - 37324, - 37325, - 37326, - 37327, - 37328, - 37329, - 37330, - 37331, - 37332, - 37333, - 37334, - 37335, - 37336, - 37337, - 37338, - 37339, - 37340, - 37341, - 37342, - 37343, - 37344, - 37345, - 37346, - 37347, - 37348, - 37349, - 37350, - 37351, - 37352, - 37353, - 37354, - 37355, - 37356, - 37357, - 37358, - 37359, - 37360, - 37361, - 37362, - 37363, - 37364, - 37365, - 37366, - 37367, - 37368, - 37369, - 37370, - 37371, - 37372, - 37373, - 37374, - 37375, - 37376, - 37377, - 37378, - 37379, - 37380, - 37381, - 37382, - 37383, - 37384, - 37385, - 37386, - 37387, - 37388, - 37389, - 37390, - 37391, - 37392, - 37393, - 37394, - 37395, - 37396, - 37397, - 37398, - 37399, - 37400, - 37401, - 37402, - 37403, - 37404, - 37405, - 37406, - 37407, - 37408, - 37409, - 37410, - 37411, - 37412, - 37413, - 37414, - 37415, - 37416, - 37417, - 37418, - 37419, - 37420, - 37421, - 37422, - 37423, - 37424, - 37425, - 37426, - 37427, - 37428, - 37429, - 37430, - 37431, - 37432, - 37433, - 37434, - 37435, - 37436, - 37437, - 37438, - 37439, - 37440, - 37441, - 37442, - 37443, - 37444, - 37445, - 37446, - 37447, - 37448, - 37449, - 37450, - 37451, - 37452, - 37453, - 37454, - 37455, - 37456, - 37457, - 37458, - 37459, - 37460, - 37461, - 37462, - 37463, - 37464, - 37465, - 37466, - 37467, - 37468, - 37469, - 37470, - 37471, - 37472, - 37473, - 37474, - 37475, - 37476, - 37477, - 37478, - 37479, - 37480, - 37481, - 37482, - 37483, - 37484, - 37485, - 37486, - 37487, - 37488, - 37489, - 37490, - 37491, - 37492, - 37493, - 37494, - 37495, - 37496, - 37497, - 37498, - 37499, - 37500, - 37501, - 37502, - 37503, - 37504, - 37505, - 37506, - 37507, - 37508, - 37509, - 37510, - 37511, - 37512, - 37513, - 37514, - 37515, - 37516, - 37517, - 37518, - 37519, - 37520, - 37521, - 37522, - 37523, - 37524, - 37525, - 37526, - 37527, - 37528, - 37529, - 37530, - 37531, - 37532, - 37533, - 37534, - 37535, - 37536, - 37537, - 37538, - 37539, - 37540, - 37541, - 37542, - 37543, - 37544, - 37545, - 37546, - 37547, - 37548, - 37549, - 37550, - 37551, - 37552, - 37553, - 37554, - 37555, - 37556, - 37557, - 37558, - 37559, - 37560, - 37561, - 37562, - 37563, - 37564, - 37565, - 37566, - 37567, - 37568, - 37569, - 37570, - 37571, - 37572, - 37573, - 37574, - 37575, - 37576, - 37577, - 37578, - 37579, - 37580, - 37581, - 37582, - 37583, - 37584, - 37585, - 37586, - 37587, - 37588, - 37589, - 37590, - 37591, - 37592, - 37593, - 37594, - 37595, - 37596, - 37597, - 37598, - 37599, - 37600, - 37601, - 37602, - 37603, - 37604, - 37605, - 37606, - 37607, - 37608, - 37609, - 37610, - 37611, - 37612, - 37613, - 37614, - 37615, - 37616, - 37617, - 37618, - 37619, - 37620, - 37621, - 37622, - 37623, - 37624, - 37625, - 37626, - 37627, - 37628, - 37629, - 37630, - 37631, - 37632, - 37633, - 37634, - 37635, - 37636, - 37637, - 37638, - 37639, - 37640, - 37641, - 37642, - 37643, - 37644, - 37645, - 37646, - 37647, - 37648, - 37649, - 37650, - 37651, - 37652, - 37653, - 37654, - 37655, - 37656, - 37657, - 37658, - 37659, - 37660, - 37661, - 37662, - 37663, - 37664, - 37665, - 37666, - 37667, - 37668, - 37669, - 37670, - 37671, - 37672, - 37673, - 37674, - 37675, - 37676, - 37677, - 37678, - 37679, - 37680, - 37681, - 37682, - 37683, - 37684, - 37685, - 37686, - 37687, - 37688, - 37689, - 37690, - 37691, - 37692, - 37693, - 37694, - 37695, - 37696, - 37697, - 37698, - 37699, - 37700, - 37701, - 37702, - 37703, - 37704, - 37705, - 37706, - 37707, - 37708, - 37709, - 37710, - 37711, - 37712, - 37713, - 37714, - 37715, - 37716, - 37717, - 37718, - 37719, - 37720, - 37721, - 37722, - 37723, - 37724, - 37725, - 37726, - 37727, - 37728, - 37729, - 37730, - 37731, - 37732, - 37733, - 37734, - 37735, - 37736, - 37737, - 37738, - 37739, - 37740, - 37741, - 37742, - 37743, - 37744, - 37745, - 37746, - 37747, - 37748, - 37749, - 37750, - 37751, - 37752, - 37753, - 37754, - 37755, - 37756, - 37757, - 37758, - 37759, - 37760, - 37761, - 37762, - 37763, - 37764, - 37765, - 37766, - 37767, - 37768, - 37769, - 37770, - 37771, - 37772, - 37773, - 37774, - 37775, - 37776, - 37777, - 37778, - 37779, - 37780, - 37781, - 37782, - 37783, - 37784, - 37785, - 37786, - 37787, - 37788, - 37789, - 37790, - 37791, - 37792, - 37793, - 37794, - 37795, - 37796, - 37797, - 37798, - 37799, - 37800, - 37801, - 37802, - 37803, - 37804, - 37805, - 37806, - 37807, - 37808, - 37809, - 37810, - 37811, - 37812, - 37813, - 37814, - 37815, - 37816, - 37817, - 37818, - 37819, - 37820, - 37821, - 37822, - 37823, - 37824, - 37825, - 37826, - 37827, - 37828, - 37829, - 37830, - 37831, - 37832, - 37833, - 37834, - 37835, - 37836, - 37837, - 37838, - 37839, - 37840, - 37841, - 37842, - 37843, - 37844, - 37845, - 37846, - 37847, - 37848, - 37849, - 37850, - 37851, - 37852, - 37853, - 37854, - 37855, - 37856, - 37857, - 37858, - 37859, - 37860, - 37861, - 37862, - 37863, - 37864, - 37865, - 37866, - 37867, - 37868, - 37869, - 37870, - 37871, - 37872, - 37873, - 37874, - 37875, - 37876, - 37877, - 37878, - 37879, - 37880, - 37881, - 37882, - 37883, - 37884, - 37885, - 37886, - 37887, - 37888, - 37889, - 37890, - 37891, - 37892, - 37893, - 37894, - 37895, - 37896, - 37897, - 37898, - 37899, - 37900, - 37901, - 37902, - 37903, - 37904, - 37905, - 37906, - 37907, - 37908, - 37909, - 37910, - 37911, - 37912, - 37913, - 37914, - 37915, - 37916, - 37917, - 37918, - 37919, - 37920, - 37921, - 37922, - 37923, - 37924, - 37925, - 37926, - 37927, - 37928, - 37929, - 37930, - 37931, - 37932, - 37933, - 37934, - 37935, - 37936, - 37937, - 37938, - 37939, - 37940, - 37941, - 37942, - 37943, - 37944, - 37945, - 37946, - 37947, - 37948, - 37949, - 37950, - 37951, - 37952, - 37953, - 37954, - 37955, - 37956, - 37957, - 37958, - 37959, - 37960, - 37961, - 37962, - 37963, - 37964, - 37965, - 37966, - 37967, - 37968, - 37969, - 37970, - 37971, - 37972, - 37973, - 37974, - 37975, - 37976, - 37977, - 37978, - 37979, - 37980, - 37981, - 37982, - 37983, - 37984, - 37985, - 37986, - 37987, - 37988, - 37989, - 37990, - 37991, - 37992, - 37993, - 37994, - 37995, - 37996, - 37997, - 37998, - 37999, - 38000, - 38001, - 38002, - 38003, - 38004, - 38005, - 38006, - 38007, - 38008, - 38009, - 38010, - 38011, - 38012, - 38013, - 38014, - 38015, - 38016, - 38017, - 38018, - 38019, - 38020, - 38021, - 38022, - 38023, - 38024, - 38025, - 38026, - 38027, - 38028, - 38029, - 38030, - 38031, - 38032, - 38033, - 38034, - 38035, - 38036, - 38037, - 38038, - 38039, - 38040, - 38041, - 38042, - 38043, - 38044, - 38045, - 38046, - 38047, - 38048, - 38049, - 38050, - 38051, - 38052, - 38053, - 38054, - 38055, - 38056, - 38057, - 38058, - 38059, - 38060, - 38061, - 38062, - 38063, - 38064, - 38065, - 38066, - 38067, - 38068, - 38069, - 38070, - 38071, - 38072, - 38073, - 38074, - 38075, - 38076, - 38077, - 38078, - 38079, - 38080, - 38081, - 38082, - 38083, - 38084, - 38085, - 38086, - 38087, - 38088, - 38089, - 38090, - 38091, - 38092, - 38093, - 38094, - 38095, - 38096, - 38097, - 38098, - 38099, - 38100, - 38101, - 38102, - 38103, - 38104, - 38105, - 38106, - 38107, - 38108, - 38109, - 38110, - 38111, - 38112, - 38113, - 38114, - 38115, - 38116, - 38117, - 38118, - 38119, - 38120, - 38121, - 38122, - 38123, - 38124, - 38125, - 38126, - 38127, - 38128, - 38129, - 38130, - 38131, - 38132, - 38133, - 38134, - 38135, - 38136, - 38137, - 38138, - 38139, - 38140, - 38141, - 38142, - 38143, - 38144, - 38145, - 38146, - 38147, - 38148, - 38149, - 38150, - 38151, - 38152, - 38153, - 38154, - 38155, - 38156, - 38157, - 38158, - 38159, - 38160, - 38161, - 38162, - 38163, - 38164, - 38165, - 38166, - 38167, - 38168, - 38169, - 38170, - 38171, - 38172, - 38173, - 38174, - 38175, - 38176, - 38177, - 38178, - 38179, - 38180, - 38181, - 38182, - 38183, - 38184, - 38185, - 38186, - 38187, - 38188, - 38189, - 38190, - 38191, - 38192, - 38193, - 38194, - 38195, - 38196, - 38197, - 38198, - 38199, - 38200, - 38201, - 38202, - 38203, - 38204, - 38205, - 38206, - 38207, - 38208, - 38209, - 38210, - 38211, - 38212, - 38213, - 38214, - 38215, - 38216, - 38217, - 38218, - 38219, - 38220, - 38221, - 38222, - 38223, - 38224, - 38225, - 38226, - 38227, - 38228, - 38229, - 38230, - 38231, - 38232, - 38233, - 38234, - 38235, - 38236, - 38237, - 38238, - 38239, - 38240, - 38241, - 38242, - 38243, - 38244, - 38245, - 38246, - 38247, - 38248, - 38249, - 38250, - 38251, - 38252, - 38253, - 38254, - 38255, - 38256, - 38257, - 38258, - 38259, - 38260, - 38261, - 38262, - 38263, - 38264, - 38265, - 38266, - 38267, - 38268, - 38269, - 38270, - 38271, - 38272, - 38273, - 38274, - 38275, - 38276, - 38277, - 38278, - 38279, - 38280, - 38281, - 38282, - 38283, - 38284, - 38285, - 38286, - 38287, - 38288, - 38289, - 38290, - 38291, - 38292, - 38293, - 38294, - 38295, - 38296, - 38297, - 38298, - 38299, - 38300, - 38301, - 38302, - 38303, - 38304, - 38305, - 38306, - 38307, - 38308, - 38309, - 38310, - 38311, - 38312, - 38313, - 38314, - 38315, - 38316, - 38317, - 38318, - 38319, - 38320, - 38321, - 38322, - 38323, - 38324, - 38325, - 38326, - 38327, - 38328, - 38329, - 38330, - 38331, - 38332, - 38333, - 38334, - 38335, - 38336, - 38337, - 38338, - 38339, - 38340, - 38341, - 38342, - 38343, - 38344, - 38345, - 38346, - 38347, - 38348, - 38349, - 38350, - 38351, - 38352, - 38353, - 38354, - 38355, - 38356, - 38357, - 38358, - 38359, - 38360, - 38361, - 38362, - 38363, - 38364, - 38365, - 38366, - 38367, - 38368, - 38369, - 38370, - 38371, - 38372, - 38373, - 38374, - 38375, - 38376, - 38377, - 38378, - 38379, - 38380, - 38381, - 38382, - 38383, - 38384, - 38385, - 38386, - 38387, - 38388, - 38389, - 38390, - 38391, - 38392, - 38393, - 38394, - 38395, - 38396, - 38397, - 38398, - 38399, - 38400, - 38401, - 38402, - 38403, - 38404, - 38405, - 38406, - 38407, - 38408, - 38409, - 38410, - 38411, - 38412, - 38413, - 38414, - 38415, - 38416, - 38417, - 38418, - 38419, - 38420, - 38421, - 38422, - 38423, - 38424, - 38425, - 38426, - 38427, - 38428, - 38429, - 38430, - 38431, - 38432, - 38433, - 38434, - 38435, - 38436, - 38437, - 38438, - 38439, - 38440, - 38441, - 38442, - 38443, - 38444, - 38445, - 38446, - 38447, - 38448, - 38449, - 38450, - 38451, - 38452, - 38453, - 38454, - 38455, - 38456, - 38457, - 38458, - 38459, - 38460, - 38461, - 38462, - 38463, - 38464, - 38465, - 38466, - 38467, - 38468, - 38469, - 38470, - 38471, - 38472, - 38473, - 38474, - 38475, - 38476, - 38477, - 38478, - 38479, - 38480, - 38481, - 38482, - 38483, - 38484, - 38485, - 38486, - 38487, - 38488, - 38489, - 38490, - 38491, - 38492, - 38493, - 38494, - 38495, - 38496, - 38497, - 38498, - 38499, - 38500, - 38501, - 38502, - 38503, - 38504, - 38505, - 38506, - 38507, - 38508, - 38509, - 38510, - 38511, - 38512, - 38513, - 38514, - 38515, - 38516, - 38517, - 38518, - 38519, - 38520, - 38521, - 38522, - 38523, - 38524, - 38525, - 38526, - 38527, - 38528, - 38529, - 38530, - 38531, - 38532, - 38533, - 38534, - 38535, - 38536, - 38537, - 38538, - 38539, - 38540, - 38541, - 38542, - 38543, - 38544, - 38545, - 38546, - 38547, - 38548, - 38549, - 38550, - 38551, - 38552, - 38553, - 38554, - 38555, - 38556, - 38557, - 38558, - 38559, - 38560, - 38561, - 38562, - 38563, - 38564, - 38565, - 38566, - 38567, - 38568, - 38569, - 38570, - 38571, - 38572, - 38573, - 38574, - 38575, - 38576, - 38577, - 38578, - 38579, - 38580, - 38581, - 38582, - 38583, - 38584, - 38585, - 38586, - 38587, - 38588, - 38589, - 38590, - 38591, - 38592, - 38593, - 38594, - 38595, - 38596, - 38597, - 38598, - 38599, - 38600, - 38601, - 38602, - 38603, - 38604, - 38605, - 38606, - 38607, - 38608, - 38609, - 38610, - 38611, - 38612, - 38613, - 38614, - 38615, - 38616, - 38617, - 38618, - 38619, - 38620, - 38621, - 38622, - 38623, - 38624, - 38625, - 38626, - 38627, - 38628, - 38629, - 38630, - 38631, - 38632, - 38633, - 38634, - 38635, - 38636, - 38637, - 38638, - 38639, - 38640, - 38641, - 38642, - 38643, - 38644, - 38645, - 38646, - 38647, - 38648, - 38649, - 38650, - 38651, - 38652, - 38653, - 38654, - 38655, - 38656, - 38657, - 38658, - 38659, - 38660, - 38661, - 38662, - 38663, - 38664, - 38665, - 38666, - 38667, - 38668, - 38669, - 38670, - 38671, - 38672, - 38673, - 38674, - 38675, - 38676, - 38677, - 38678, - 38679, - 38680, - 38681, - 38682, - 38683, - 38684, - 38685, - 38686, - 38687, - 38688, - 38689, - 38690, - 38691, - 38692, - 38693, - 38694, - 38695, - 38696, - 38697, - 38698, - 38699, - 38700, - 38701, - 38702, - 38703, - 38704, - 38705, - 38706, - 38707, - 38708, - 38709, - 38710, - 38711, - 38712, - 38713, - 38714, - 38715, - 38716, - 38717, - 38718, - 38719, - 38720, - 38721, - 38722, - 38723, - 38724, - 38725, - 38726, - 38727, - 38728, - 38729, - 38730, - 38731, - 38732, - 38733, - 38734, - 38735, - 38736, - 38737, - 38738, - 38739, - 38740, - 38741, - 38742, - 38743, - 38744, - 38745, - 38746, - 38747, - 38748, - 38749, - 38750, - 38751, - 38752, - 38753, - 38754, - 38755, - 38756, - 38757, - 38758, - 38759, - 38760, - 38761, - 38762, - 38763, - 38764, - 38765, - 38766, - 38767, - 38768, - 38769, - 38770, - 38771, - 38772, - 38773, - 38774, - 38775, - 38776, - 38777, - 38778, - 38779, - 38780, - 38781, - 38782, - 38783, - 38784, - 38785, - 38786, - 38787, - 38788, - 38789, - 38790, - 38791, - 38792, - 38793, - 38794, - 38795, - 38796, - 38797, - 38798, - 38799, - 38800, - 38801, - 38802, - 38803, - 38804, - 38805, - 38806, - 38807, - 38808, - 38809, - 38810, - 38811, - 38812, - 38813, - 38814, - 38815, - 38816, - 38817, - 38818, - 38819, - 38820, - 38821, - 38822, - 38823, - 38824, - 38825, - 38826, - 38827, - 38828, - 38829, - 38830, - 38831, - 38832, - 38833, - 38834, - 38835, - 38836, - 38837, - 38838, - 38839, - 38840, - 38841, - 38842, - 38843, - 38844, - 38845, - 38846, - 38847, - 38848, - 38849, - 38850, - 38851, - 38852, - 38853, - 38854, - 38855, - 38856, - 38857, - 38858, - 38859, - 38860, - 38861, - 38862, - 38863, - 38864, - 38865, - 38866, - 38867, - 38868, - 38869, - 38870, - 38871, - 38872, - 38873, - 38874, - 38875, - 38876, - 38877, - 38878, - 38879, - 38880, - 38881, - 38882, - 38883, - 38884, - 38885, - 38886, - 38887, - 38888, - 38889, - 38890, - 38891, - 38892, - 38893, - 38894, - 38895, - 38896, - 38897, - 38898, - 38899, - 38900, - 38901, - 38902, - 38903, - 38904, - 38905, - 38906, - 38907, - 38908, - 38909, - 38910, - 38911, - 38912, - 38913, - 38914, - 38915, - 38916, - 38917, - 38918, - 38919, - 38920, - 38921, - 38922, - 38923, - 38924, - 38925, - 38926, - 38927, - 38928, - 38929, - 38930, - 38931, - 38932, - 38933, - 38934, - 38935, - 38936, - 38937, - 38938, - 38939, - 38940, - 38941, - 38942, - 38943, - 38944, - 38945, - 38946, - 38947, - 38948, - 38949, - 38950, - 38951, - 38952, - 38953, - 38954, - 38955, - 38956, - 38957, - 38958, - 38959, - 38960, - 38961, - 38962, - 38963, - 38964, - 38965, - 38966, - 38967, - 38968, - 38969, - 38970, - 38971, - 38972, - 38973, - 38974, - 38975, - 38976, - 38977, - 38978, - 38979, - 38980, - 38981, - 38982, - 38983, - 38984, - 38985, - 38986, - 38987, - 38988, - 38989, - 38990, - 38991, - 38992, - 38993, - 38994, - 38995, - 38996, - 38997, - 38998, - 38999, - 39000, - 39001, - 39002, - 39003, - 39004, - 39005, - 39006, - 39007, - 39008, - 39009, - 39010, - 39011, - 39012, - 39013, - 39014, - 39015, - 39016, - 39017, - 39018, - 39019, - 39020, - 39021, - 39022, - 39023, - 39024, - 39025, - 39026, - 39027, - 39028, - 39029, - 39030, - 39031, - 39032, - 39033, - 39034, - 39035, - 39036, - 39037, - 39038, - 39039, - 39040, - 39041, - 39042, - 39043, - 39044, - 39045, - 39046, - 39047, - 39048, - 39049, - 39050, - 39051, - 39052, - 39053, - 39054, - 39055, - 39056, - 39057, - 39058, - 39059, - 39060, - 39061, - 39062, - 39063, - 39064, - 39065, - 39066, - 39067, - 39068, - 39069, - 39070, - 39071, - 39072, - 39073, - 39074, - 39075, - 39076, - 39077, - 39078, - 39079, - 39080, - 39081, - 39082, - 39083, - 39084, - 39085, - 39086, - 39087, - 39088, - 39089, - 39090, - 39091, - 39092, - 39093, - 39094, - 39095, - 39096, - 39097, - 39098, - 39099, - 39100, - 39101, - 39102, - 39103, - 39104, - 39105, - 39106, - 39107, - 39108, - 39109, - 39110, - 39111, - 39112, - 39113, - 39114, - 39115, - 39116, - 39117, - 39118, - 39119, - 39120, - 39121, - 39122, - 39123, - 39124, - 39125, - 39126, - 39127, - 39128, - 39129, - 39130, - 39131, - 39132, - 39133, - 39134, - 39135, - 39136, - 39137, - 39138, - 39139, - 39140, - 39141, - 39142, - 39143, - 39144, - 39145, - 39146, - 39147, - 39148, - 39149, - 39150, - 39151, - 39152, - 39153, - 39154, - 39155, - 39156, - 39157, - 39158, - 39159, - 39160, - 39161, - 39162, - 39163, - 39164, - 39165, - 39166, - 39167, - 39168, - 39169, - 39170, - 39171, - 39172, - 39173, - 39174, - 39175, - 39176, - 39177, - 39178, - 39179, - 39180, - 39181, - 39182, - 39183, - 39184, - 39185, - 39186, - 39187, - 39188, - 39189, - 39190, - 39191, - 39192, - 39193, - 39194, - 39195, - 39196, - 39197, - 39198, - 39199, - 39200, - 39201, - 39202, - 39203, - 39204, - 39205, - 39206, - 39207, - 39208, - 39209, - 39210, - 39211, - 39212, - 39213, - 39214, - 39215, - 39216, - 39217, - 39218, - 39219, - 39220, - 39221, - 39222, - 39223, - 39224, - 39225, - 39226, - 39227, - 39228, - 39229, - 39230, - 39231, - 39232, - 39233, - 39234, - 39235, - 39236, - 39237, - 39238, - 39239, - 39240, - 39241, - 39242, - 39243, - 39244, - 39245, - 39246, - 39247, - 39248, - 39249, - 39250, - 39251, - 39252, - 39253, - 39254, - 39255, - 39256, - 39257, - 39258, - 39259, - 39260, - 39261, - 39262, - 39263, - 39264, - 39265, - 39266, - 39267, - 39268, - 39269, - 39270, - 39271, - 39272, - 39273, - 39274, - 39275, - 39276, - 39277, - 39278, - 39279, - 39280, - 39281, - 39282, - 39283, - 39284, - 39285, - 39286, - 39287, - 39288, - 39289, - 39290, - 39291, - 39292, - 39293, - 39294, - 39295, - 39296, - 39297, - 39298, - 39299, - 39300, - 39301, - 39302, - 39303, - 39304, - 39305, - 39306, - 39307, - 39308, - 39309, - 39310, - 39311, - 39312, - 39313, - 39314, - 39315, - 39316, - 39317, - 39318, - 39319, - 39320, - 39321, - 39322, - 39323, - 39324, - 39325, - 39326, - 39327, - 39328, - 39329, - 39330, - 39331, - 39332, - 39333, - 39334, - 39335, - 39336, - 39337, - 39338, - 39339, - 39340, - 39341, - 39342, - 39343, - 39344, - 39345, - 39346, - 39347, - 39348, - 39349, - 39350, - 39351, - 39352, - 39353, - 39354, - 39355, - 39356, - 39357, - 39358, - 39359, - 39360, - 39361, - 39362, - 39363, - 39364, - 39365, - 39366, - 39367, - 39368, - 39369, - 39370, - 39371, - 39372, - 39373, - 39374, - 39375, - 39376, - 39377, - 39378, - 39379, - 39380, - 39381, - 39382, - 39383, - 39384, - 39385, - 39386, - 39387, - 39388, - 39389, - 39390, - 39391, - 39392, - 39393, - 39394, - 39395, - 39396, - 39397, - 39398, - 39399, - 39400, - 39401, - 39402, - 39403, - 39404, - 39405, - 39406, - 39407, - 39408, - 39409, - 39410, - 39411, - 39412, - 39413, - 39414, - 39415, - 39416, - 39417, - 39418, - 39419, - 39420, - 39421, - 39422, - 39423, - 39424, - 39425, - 39426, - 39427, - 39428, - 39429, - 39430, - 39431, - 39432, - 39433, - 39434, - 39435, - 39436, - 39437, - 39438, - 39439, - 39440, - 39441, - 39442, - 39443, - 39444, - 39445, - 39446, - 39447, - 39448, - 39449, - 39450, - 39451, - 39452, - 39453, - 39454, - 39455, - 39456, - 39457, - 39458, - 39459, - 39460, - 39461, - 39462, - 39463, - 39464, - 39465, - 39466, - 39467, - 39468, - 39469, - 39470, - 39471, - 39472, - 39473, - 39474, - 39475, - 39476, - 39477, - 39478, - 39479, - 39480, - 39481, - 39482, - 39483, - 39484, - 39485, - 39486, - 39487, - 39488, - 39489, - 39490, - 39491, - 39492, - 39493, - 39494, - 39495, - 39496, - 39497, - 39498, - 39499, - 39500, - 39501, - 39502, - 39503, - 39504, - 39505, - 39506, - 39507, - 39508, - 39509, - 39510, - 39511, - 39512, - 39513, - 39514, - 39515, - 39516, - 39517, - 39518, - 39519, - 39520, - 39521, - 39522, - 39523, - 39524, - 39525, - 39526, - 39527, - 39528, - 39529, - 39530, - 39531, - 39532, - 39533, - 39534, - 39535, - 39536, - 39537, - 39538, - 39539, - 39540, - 39541, - 39542, - 39543, - 39544, - 39545, - 39546, - 39547, - 39548, - 39549, - 39550, - 39551, - 39552, - 39553, - 39554, - 39555, - 39556, - 39557, - 39558, - 39559, - 39560, - 39561, - 39562, - 39563, - 39564, - 39565, - 39566, - 39567, - 39568, - 39569, - 39570, - 39571, - 39572, - 39573, - 39574, - 39575, - 39576, - 39577, - 39578, - 39579, - 39580, - 39581, - 39582, - 39583, - 39584, - 39585, - 39586, - 39587, - 39588, - 39589, - 39590, - 39591, - 39592, - 39593, - 39594, - 39595, - 39596, - 39597, - 39598, - 39599, - 39600, - 39601, - 39602, - 39603, - 39604, - 39605, - 39606, - 39607, - 39608, - 39609, - 39610, - 39611, - 39612, - 39613, - 39614, - 39615, - 39616, - 39617, - 39618, - 39619, - 39620, - 39621, - 39622, - 39623, - 39624, - 39625, - 39626, - 39627, - 39628, - 39629, - 39630, - 39631, - 39632, - 39633, - 39634, - 39635, - 39636, - 39637, - 39638, - 39639, - 39640, - 39641, - 39642, - 39643, - 39644, - 39645, - 39646, - 39647, - 39648, - 39649, - 39650, - 39651, - 39652, - 39653, - 39654, - 39655, - 39656, - 39657, - 39658, - 39659, - 39660, - 39661, - 39662, - 39663, - 39664, - 39665, - 39666, - 39667, - 39668, - 39669, - 39670, - 39671, - 39672, - 39673, - 39674, - 39675, - 39676, - 39677, - 39678, - 39679, - 39680, - 39681, - 39682, - 39683, - 39684, - 39685, - 39686, - 39687, - 39688, - 39689, - 39690, - 39691, - 39692, - 39693, - 39694, - 39695, - 39696, - 39697, - 39698, - 39699, - 39700, - 39701, - 39702, - 39703, - 39704, - 39705, - 39706, - 39707, - 39708, - 39709, - 39710, - 39711, - 39712, - 39713, - 39714, - 39715, - 39716, - 39717, - 39718, - 39719, - 39720, - 39721, - 39722, - 39723, - 39724, - 39725, - 39726, - 39727, - 39728, - 39729, - 39730, - 39731, - 39732, - 39733, - 39734, - 39735, - 39736, - 39737, - 39738, - 39739, - 39740, - 39741, - 39742, - 39743, - 39744, - 39745, - 39746, - 39747, - 39748, - 39749, - 39750, - 39751, - 39752, - 39753, - 39754, - 39755, - 39756, - 39757, - 39758, - 39759, - 39760, - 39761, - 39762, - 39763, - 39764, - 39765, - 39766, - 39767, - 39768, - 39769, - 39770, - 39771, - 39772, - 39773, - 39774, - 39775, - 39776, - 39777, - 39778, - 39779, - 39780, - 39781, - 39782, - 39783, - 39784, - 39785, - 39786, - 39787, - 39788, - 39789, - 39790, - 39791, - 39792, - 39793, - 39794, - 39795, - 39796, - 39797, - 39798, - 39799, - 39800, - 39801, - 39802, - 39803, - 39804, - 39805, - 39806, - 39807, - 39808, - 39809, - 39810, - 39811, - 39812, - 39813, - 39814, - 39815, - 39816, - 39817, - 39818, - 39819, - 39820, - 39821, - 39822, - 39823, - 39824, - 39825, - 39826, - 39827, - 39828, - 39829, - 39830, - 39831, - 39832, - 39833, - 39834, - 39835, - 39836, - 39837, - 39838, - 39839, - 39840, - 39841, - 39842, - 39843, - 39844, - 39845, - 39846, - 39847, - 39848, - 39849, - 39850, - 39851, - 39852, - 39853, - 39854, - 39855, - 39856, - 39857, - 39858, - 39859, - 39860, - 39861, - 39862, - 39863, - 39864, - 39865, - 39866, - 39867, - 39868, - 39869, - 39870, - 39871, - 39872, - 39873, - 39874, - 39875, - 39876, - 39877, - 39878, - 39879, - 39880, - 39881, - 39882, - 39883, - 39884, - 39885, - 39886, - 39887, - 39888, - 39889, - 39890, - 39891, - 39892, - 39893, - 39894, - 39895, - 39896, - 39897, - 39898, - 39899, - 39900, - 39901, - 39902, - 39903, - 39904, - 39905, - 39906, - 39907, - 39908, - 39909, - 39910, - 39911, - 39912, - 39913, - 39914, - 39915, - 39916, - 39917, - 39918, - 39919, - 39920, - 39921, - 39922, - 39923, - 39924, - 39925, - 39926, - 39927, - 39928, - 39929, - 39930, - 39931, - 39932, - 39933, - 39934, - 39935, - 39936, - 39937, - 39938, - 39939, - 39940, - 39941, - 39942, - 39943, - 39944, - 39945, - 39946, - 39947, - 39948, - 39949, - 39950, - 39951, - 39952, - 39953, - 39954, - 39955, - 39956, - 39957, - 39958, - 39959, - 39960, - 39961, - 39962, - 39963, - 39964, - 39965, - 39966, - 39967, - 39968, - 39969, - 39970, - 39971, - 39972, - 39973, - 39974, - 39975, - 39976, - 39977, - 39978, - 39979, - 39980, - 39981, - 39982, - 39983, - 39984, - 39985, - 39986, - 39987, - 39988, - 39989, - 39990, - 39991, - 39992, - 39993, - 39994, - 39995, - 39996, - 39997, - 39998, - 39999, - 40000, - 40001, - 40002, - 40003, - 40004, - 40005, - 40006, - 40007, - 40008, - 40009, - 40010, - 40011, - 40012, - 40013, - 40014, - 40015, - 40016, - 40017, - 40018, - 40019, - 40020, - 40021, - 40022, - 40023, - 40024, - 40025, - 40026, - 40027, - 40028, - 40029, - 40030, - 40031, - 40032, - 40033, - 40034, - 40035, - 40036, - 40037, - 40038, - 40039, - 40040, - 40041, - 40042, - 40043, - 40044, - 40045, - 40046, - 40047, - 40048, - 40049, - 40050, - 40051, - 40052, - 40053, - 40054, - 40055, - 40056, - 40057, - 40058, - 40059, - 40060, - 40061, - 40062, - 40063, - 40064, - 40065, - 40066, - 40067, - 40068, - 40069, - 40070, - 40071, - 40072, - 40073, - 40074, - 40075, - 40076, - 40077, - 40078, - 40079, - 40080, - 40081, - 40082, - 40083, - 40084, - 40085, - 40086, - 40087, - 40088, - 40089, - 40090, - 40091, - 40092, - 40093, - 40094, - 40095, - 40096, - 40097, - 40098, - 40099, - 40100, - 40101, - 40102, - 40103, - 40104, - 40105, - 40106, - 40107, - 40108, - 40109, - 40110, - 40111, - 40112, - 40113, - 40114, - 40115, - 40116, - 40117, - 40118, - 40119, - 40120, - 40121, - 40122, - 40123, - 40124, - 40125, - 40126, - 40127, - 40128, - 40129, - 40130, - 40131, - 40132, - 40133, - 40134, - 40135, - 40136, - 40137, - 40138, - 40139, - 40140, - 40141, - 40142, - 40143, - 40144, - 40145, - 40146, - 40147, - 40148, - 40149, - 40150, - 40151, - 40152, - 40153, - 40154, - 40155, - 40156, - 40157, - 40158, - 40159, - 40160, - 40161, - 40162, - 40163, - 40164, - 40165, - 40166, - 40167, - 40168, - 40169, - 40170, - 40171, - 40172, - 40173, - 40174, - 40175, - 40176, - 40177, - 40178, - 40179, - 40180, - 40181, - 40182, - 40183, - 40184, - 40185, - 40186, - 40187, - 40188, - 40189, - 40190, - 40191, - 40192, - 40193, - 40194, - 40195, - 40196, - 40197, - 40198, - 40199, - 40200, - 40201, - 40202, - 40203, - 40204, - 40205, - 40206, - 40207, - 40208, - 40209, - 40210, - 40211, - 40212, - 40213, - 40214, - 40215, - 40216, - 40217, - 40218, - 40219, - 40220, - 40221, - 40222, - 40223, - 40224, - 40225, - 40226, - 40227, - 40228, - 40229, - 40230, - 40231, - 40232, - 40233, - 40234, - 40235, - 40236, - 40237, - 40238, - 40239, - 40240, - 40241, - 40242, - 40243, - 40244, - 40245, - 40246, - 40247, - 40248, - 40249, - 40250, - 40251, - 40252, - 40253, - 40254, - 40255, - 40256, - 40257, - 40258, - 40259, - 40260, - 40261, - 40262, - 40263, - 40264, - 40265, - 40266, - 40267, - 40268, - 40269, - 40270, - 40271, - 40272, - 40273, - 40274, - 40275, - 40276, - 40277, - 40278, - 40279, - 40280, - 40281, - 40282, - 40283, - 40284, - 40285, - 40286, - 40287, - 40288, - 40289, - 40290, - 40291, - 40292, - 40293, - 40294, - 40295, - 40296, - 40297, - 40298, - 40299, - 40300, - 40301, - 40302, - 40303, - 40304, - 40305, - 40306, - 40307, - 40308, - 40309, - 40310, - 40311, - 40312, - 40313, - 40314, - 40315, - 40316, - 40317, - 40318, - 40319, - 40320, - 40321, - 40322, - 40323, - 40324, - 40325, - 40326, - 40327, - 40328, - 40329, - 40330, - 40331, - 40332, - 40333, - 40334, - 40335, - 40336, - 40337, - 40338, - 40339, - 40340, - 40341, - 40342, - 40343, - 40344, - 40345, - 40346, - 40347, - 40348, - 40349, - 40350, - 40351, - 40352, - 40353, - 40354, - 40355, - 40356, - 40357, - 40358, - 40359, - 40360, - 40361, - 40362, - 40363, - 40364, - 40365, - 40366, - 40367, - 40368, - 40369, - 40370, - 40371, - 40372, - 40373, - 40374, - 40375, - 40376, - 40377, - 40378, - 40379, - 40380, - 40381, - 40382, - 40383, - 40384, - 40385, - 40386, - 40387, - 40388, - 40389, - 40390, - 40391, - 40392, - 40393, - 40394, - 40395, - 40396, - 40397, - 40398, - 40399, - 40400, - 40401, - 40402, - 40403, - 40404, - 40405, - 40406, - 40407, - 40408, - 40409, - 40410, - 40411, - 40412, - 40413, - 40414, - 40415, - 40416, - 40417, - 40418, - 40419, - 40420, - 40421, - 40422, - 40423, - 40424, - 40425, - 40426, - 40427, - 40428, - 40429, - 40430, - 40431, - 40432, - 40433, - 40434, - 40435, - 40436, - 40437, - 40438, - 40439, - 40440, - 40441, - 40442, - 40443, - 40444, - 40445, - 40446, - 40447, - 40448, - 40449, - 40450, - 40451, - 40452, - 40453, - 40454, - 40455, - 40456, - 40457, - 40458, - 40459, - 40460, - 40461, - 40462, - 40463, - 40464, - 40465, - 40466, - 40467, - 40468, - 40469, - 40470, - 40471, - 40472, - 40473, - 40474, - 40475, - 40476, - 40477, - 40478, - 40479, - 40480, - 40481, - 40482, - 40483, - 40484, - 40485, - 40486, - 40487, - 40488, - 40489, - 40490, - 40491, - 40492, - 40493, - 40494, - 40495, - 40496, - 40497, - 40498, - 40499, - 40500, - 40501, - 40502, - 40503, - 40504, - 40505, - 40506, - 40507, - 40508, - 40509, - 40510, - 40511, - 40512, - 40513, - 40514, - 40515, - 40516, - 40517, - 40518, - 40519, - 40520, - 40521, - 40522, - 40523, - 40524, - 40525, - 40526, - 40527, - 40528, - 40529, - 40530, - 40531, - 40532, - 40533, - 40534, - 40535, - 40536, - 40537, - 40538, - 40539, - 40540, - 40541, - 40542, - 40543, - 40544, - 40545, - 40546, - 40547, - 40548, - 40549, - 40550, - 40551, - 40552, - 40553, - 40554, - 40555, - 40556, - 40557, - 40558, - 40559, - 40560, - 40561, - 40562, - 40563, - 40564, - 40565, - 40566, - 40567, - 40568, - 40569, - 40570, - 40571, - 40572, - 40573, - 40574, - 40575, - 40576, - 40577, - 40578, - 40579, - 40580, - 40581, - 40582, - 40583, - 40584, - 40585, - 40586, - 40587, - 40588, - 40589, - 40590, - 40591, - 40592, - 40593, - 40594, - 40595, - 40596, - 40597, - 40598, - 40599, - 40600, - 40601, - 40602, - 40603, - 40604, - 40605, - 40606, - 40607, - 40608, - 40609, - 40610, - 40611, - 40612, - 40613, - 40614, - 40615, - 40616, - 40617, - 40618, - 40619, - 40620, - 40621, - 40622, - 40623, - 40624, - 40625, - 40626, - 40627, - 40628, - 40629, - 40630, - 40631, - 40632, - 40633, - 40634, - 40635, - 40636, - 40637, - 40638, - 40639, - 40640, - 40641, - 40642, - 40643, - 40644, - 40645, - 40646, - 40647, - 40648, - 40649, - 40650, - 40651, - 40652, - 40653, - 40654, - 40655, - 40656, - 40657, - 40658, - 40659, - 40660, - 40661, - 40662, - 40663, - 40664, - 40665, - 40666, - 40667, - 40668, - 40669, - 40670, - 40671, - 40672, - 40673, - 40674, - 40675, - 40676, - 40677, - 40678, - 40679, - 40680, - 40681, - 40682, - 40683, - 40684, - 40685, - 40686, - 40687, - 40688, - 40689, - 40690, - 40691, - 40692, - 40693, - 40694, - 40695, - 40696, - 40697, - 40698, - 40699, - 40700, - 40701, - 40702, - 40703, - 40704, - 40705, - 40706, - 40707, - 40708, - 40709, - 40710, - 40711, - 40712, - 40713, - 40714, - 40715, - 40716, - 40717, - 40718, - 40719, - 40720, - 40721, - 40722, - 40723, - 40724, - 40725, - 40726, - 40727, - 40728, - 40729, - 40730, - 40731, - 40732, - 40733, - 40734, - 40735, - 40736, - 40737, - 40738, - 40739, - 40740, - 40741, - 40742, - 40743, - 40744, - 40745, - 40746, - 40747, - 40748, - 40749, - 40750, - 40751, - 40752, - 40753, - 40754, - 40755, - 40756, - 40757, - 40758, - 40759, - 40760, - 40761, - 40762, - 40763, - 40764, - 40765, - 40766, - 40767, - 40768, - 40769, - 40770, - 40771, - 40772, - 40773, - 40774, - 40775, - 40776, - 40777, - 40778, - 40779, - 40780, - 40781, - 40782, - 40783, - 40784, - 40785, - 40786, - 40787, - 40788, - 40789, - 40790, - 40791, - 40792, - 40793, - 40794, - 40795, - 40796, - 40797, - 40798, - 40799, - 40800, - 40801, - 40802, - 40803, - 40804, - 40805, - 40806, - 40807, - 40808, - 40809, - 40810, - 40811, - 40812, - 40813, - 40814, - 40815, - 40816, - 40817, - 40818, - 40819, - 40820, - 40821, - 40822, - 40823, - 40824, - 40825, - 40826, - 40827, - 40828, - 40829, - 40830, - 40831, - 40832, - 40833, - 40834, - 40835, - 40836, - 40837, - 40838, - 40839, - 40840, - 40841, - 40842, - 40843, - 40844, - 40845, - 40846, - 40847, - 40848, - 40849, - 40850, - 40851, - 40852, - 40853, - 40854, - 40855, - 40856, - 40857, - 40858, - 40859, - 40860, - 40861, - 40862, - 40863, - 40864, - 40865, - 40866, - 40867, - 40868, - 40869, - 40870, - 40871, - 40872, - 40873, - 40874, - 40875, - 40876, - 40877, - 40878, - 40879, - 40880, - 40881, - 40882, - 40883, - 40884, - 40885, - 40886, - 40887, - 40888, - 40889, - 40890, - 40891, - 40892, - 40893, - 40894, - 40895, - 40896, - 40897, - 40898, - 40899, - 40900, - 40901, - 40902, - 40903, - 40904, - 40905, - 40906, - 40907, - 40908, - 40960, - 40961, - 40962, - 40963, - 40964, - 40965, - 40966, - 40967, - 40968, - 40969, - 40970, - 40971, - 40972, - 40973, - 40974, - 40975, - 40976, - 40977, - 40978, - 40979, - 40980, - 40981, - 40982, - 40983, - 40984, - 40985, - 40986, - 40987, - 40988, - 40989, - 40990, - 40991, - 40992, - 40993, - 40994, - 40995, - 40996, - 40997, - 40998, - 40999, - 41000, - 41001, - 41002, - 41003, - 41004, - 41005, - 41006, - 41007, - 41008, - 41009, - 41010, - 41011, - 41012, - 41013, - 41014, - 41015, - 41016, - 41017, - 41018, - 41019, - 41020, - 41021, - 41022, - 41023, - 41024, - 41025, - 41026, - 41027, - 41028, - 41029, - 41030, - 41031, - 41032, - 41033, - 41034, - 41035, - 41036, - 41037, - 41038, - 41039, - 41040, - 41041, - 41042, - 41043, - 41044, - 41045, - 41046, - 41047, - 41048, - 41049, - 41050, - 41051, - 41052, - 41053, - 41054, - 41055, - 41056, - 41057, - 41058, - 41059, - 41060, - 41061, - 41062, - 41063, - 41064, - 41065, - 41066, - 41067, - 41068, - 41069, - 41070, - 41071, - 41072, - 41073, - 41074, - 41075, - 41076, - 41077, - 41078, - 41079, - 41080, - 41081, - 41082, - 41083, - 41084, - 41085, - 41086, - 41087, - 41088, - 41089, - 41090, - 41091, - 41092, - 41093, - 41094, - 41095, - 41096, - 41097, - 41098, - 41099, - 41100, - 41101, - 41102, - 41103, - 41104, - 41105, - 41106, - 41107, - 41108, - 41109, - 41110, - 41111, - 41112, - 41113, - 41114, - 41115, - 41116, - 41117, - 41118, - 41119, - 41120, - 41121, - 41122, - 41123, - 41124, - 41125, - 41126, - 41127, - 41128, - 41129, - 41130, - 41131, - 41132, - 41133, - 41134, - 41135, - 41136, - 41137, - 41138, - 41139, - 41140, - 41141, - 41142, - 41143, - 41144, - 41145, - 41146, - 41147, - 41148, - 41149, - 41150, - 41151, - 41152, - 41153, - 41154, - 41155, - 41156, - 41157, - 41158, - 41159, - 41160, - 41161, - 41162, - 41163, - 41164, - 41165, - 41166, - 41167, - 41168, - 41169, - 41170, - 41171, - 41172, - 41173, - 41174, - 41175, - 41176, - 41177, - 41178, - 41179, - 41180, - 41181, - 41182, - 41183, - 41184, - 41185, - 41186, - 41187, - 41188, - 41189, - 41190, - 41191, - 41192, - 41193, - 41194, - 41195, - 41196, - 41197, - 41198, - 41199, - 41200, - 41201, - 41202, - 41203, - 41204, - 41205, - 41206, - 41207, - 41208, - 41209, - 41210, - 41211, - 41212, - 41213, - 41214, - 41215, - 41216, - 41217, - 41218, - 41219, - 41220, - 41221, - 41222, - 41223, - 41224, - 41225, - 41226, - 41227, - 41228, - 41229, - 41230, - 41231, - 41232, - 41233, - 41234, - 41235, - 41236, - 41237, - 41238, - 41239, - 41240, - 41241, - 41242, - 41243, - 41244, - 41245, - 41246, - 41247, - 41248, - 41249, - 41250, - 41251, - 41252, - 41253, - 41254, - 41255, - 41256, - 41257, - 41258, - 41259, - 41260, - 41261, - 41262, - 41263, - 41264, - 41265, - 41266, - 41267, - 41268, - 41269, - 41270, - 41271, - 41272, - 41273, - 41274, - 41275, - 41276, - 41277, - 41278, - 41279, - 41280, - 41281, - 41282, - 41283, - 41284, - 41285, - 41286, - 41287, - 41288, - 41289, - 41290, - 41291, - 41292, - 41293, - 41294, - 41295, - 41296, - 41297, - 41298, - 41299, - 41300, - 41301, - 41302, - 41303, - 41304, - 41305, - 41306, - 41307, - 41308, - 41309, - 41310, - 41311, - 41312, - 41313, - 41314, - 41315, - 41316, - 41317, - 41318, - 41319, - 41320, - 41321, - 41322, - 41323, - 41324, - 41325, - 41326, - 41327, - 41328, - 41329, - 41330, - 41331, - 41332, - 41333, - 41334, - 41335, - 41336, - 41337, - 41338, - 41339, - 41340, - 41341, - 41342, - 41343, - 41344, - 41345, - 41346, - 41347, - 41348, - 41349, - 41350, - 41351, - 41352, - 41353, - 41354, - 41355, - 41356, - 41357, - 41358, - 41359, - 41360, - 41361, - 41362, - 41363, - 41364, - 41365, - 41366, - 41367, - 41368, - 41369, - 41370, - 41371, - 41372, - 41373, - 41374, - 41375, - 41376, - 41377, - 41378, - 41379, - 41380, - 41381, - 41382, - 41383, - 41384, - 41385, - 41386, - 41387, - 41388, - 41389, - 41390, - 41391, - 41392, - 41393, - 41394, - 41395, - 41396, - 41397, - 41398, - 41399, - 41400, - 41401, - 41402, - 41403, - 41404, - 41405, - 41406, - 41407, - 41408, - 41409, - 41410, - 41411, - 41412, - 41413, - 41414, - 41415, - 41416, - 41417, - 41418, - 41419, - 41420, - 41421, - 41422, - 41423, - 41424, - 41425, - 41426, - 41427, - 41428, - 41429, - 41430, - 41431, - 41432, - 41433, - 41434, - 41435, - 41436, - 41437, - 41438, - 41439, - 41440, - 41441, - 41442, - 41443, - 41444, - 41445, - 41446, - 41447, - 41448, - 41449, - 41450, - 41451, - 41452, - 41453, - 41454, - 41455, - 41456, - 41457, - 41458, - 41459, - 41460, - 41461, - 41462, - 41463, - 41464, - 41465, - 41466, - 41467, - 41468, - 41469, - 41470, - 41471, - 41472, - 41473, - 41474, - 41475, - 41476, - 41477, - 41478, - 41479, - 41480, - 41481, - 41482, - 41483, - 41484, - 41485, - 41486, - 41487, - 41488, - 41489, - 41490, - 41491, - 41492, - 41493, - 41494, - 41495, - 41496, - 41497, - 41498, - 41499, - 41500, - 41501, - 41502, - 41503, - 41504, - 41505, - 41506, - 41507, - 41508, - 41509, - 41510, - 41511, - 41512, - 41513, - 41514, - 41515, - 41516, - 41517, - 41518, - 41519, - 41520, - 41521, - 41522, - 41523, - 41524, - 41525, - 41526, - 41527, - 41528, - 41529, - 41530, - 41531, - 41532, - 41533, - 41534, - 41535, - 41536, - 41537, - 41538, - 41539, - 41540, - 41541, - 41542, - 41543, - 41544, - 41545, - 41546, - 41547, - 41548, - 41549, - 41550, - 41551, - 41552, - 41553, - 41554, - 41555, - 41556, - 41557, - 41558, - 41559, - 41560, - 41561, - 41562, - 41563, - 41564, - 41565, - 41566, - 41567, - 41568, - 41569, - 41570, - 41571, - 41572, - 41573, - 41574, - 41575, - 41576, - 41577, - 41578, - 41579, - 41580, - 41581, - 41582, - 41583, - 41584, - 41585, - 41586, - 41587, - 41588, - 41589, - 41590, - 41591, - 41592, - 41593, - 41594, - 41595, - 41596, - 41597, - 41598, - 41599, - 41600, - 41601, - 41602, - 41603, - 41604, - 41605, - 41606, - 41607, - 41608, - 41609, - 41610, - 41611, - 41612, - 41613, - 41614, - 41615, - 41616, - 41617, - 41618, - 41619, - 41620, - 41621, - 41622, - 41623, - 41624, - 41625, - 41626, - 41627, - 41628, - 41629, - 41630, - 41631, - 41632, - 41633, - 41634, - 41635, - 41636, - 41637, - 41638, - 41639, - 41640, - 41641, - 41642, - 41643, - 41644, - 41645, - 41646, - 41647, - 41648, - 41649, - 41650, - 41651, - 41652, - 41653, - 41654, - 41655, - 41656, - 41657, - 41658, - 41659, - 41660, - 41661, - 41662, - 41663, - 41664, - 41665, - 41666, - 41667, - 41668, - 41669, - 41670, - 41671, - 41672, - 41673, - 41674, - 41675, - 41676, - 41677, - 41678, - 41679, - 41680, - 41681, - 41682, - 41683, - 41684, - 41685, - 41686, - 41687, - 41688, - 41689, - 41690, - 41691, - 41692, - 41693, - 41694, - 41695, - 41696, - 41697, - 41698, - 41699, - 41700, - 41701, - 41702, - 41703, - 41704, - 41705, - 41706, - 41707, - 41708, - 41709, - 41710, - 41711, - 41712, - 41713, - 41714, - 41715, - 41716, - 41717, - 41718, - 41719, - 41720, - 41721, - 41722, - 41723, - 41724, - 41725, - 41726, - 41727, - 41728, - 41729, - 41730, - 41731, - 41732, - 41733, - 41734, - 41735, - 41736, - 41737, - 41738, - 41739, - 41740, - 41741, - 41742, - 41743, - 41744, - 41745, - 41746, - 41747, - 41748, - 41749, - 41750, - 41751, - 41752, - 41753, - 41754, - 41755, - 41756, - 41757, - 41758, - 41759, - 41760, - 41761, - 41762, - 41763, - 41764, - 41765, - 41766, - 41767, - 41768, - 41769, - 41770, - 41771, - 41772, - 41773, - 41774, - 41775, - 41776, - 41777, - 41778, - 41779, - 41780, - 41781, - 41782, - 41783, - 41784, - 41785, - 41786, - 41787, - 41788, - 41789, - 41790, - 41791, - 41792, - 41793, - 41794, - 41795, - 41796, - 41797, - 41798, - 41799, - 41800, - 41801, - 41802, - 41803, - 41804, - 41805, - 41806, - 41807, - 41808, - 41809, - 41810, - 41811, - 41812, - 41813, - 41814, - 41815, - 41816, - 41817, - 41818, - 41819, - 41820, - 41821, - 41822, - 41823, - 41824, - 41825, - 41826, - 41827, - 41828, - 41829, - 41830, - 41831, - 41832, - 41833, - 41834, - 41835, - 41836, - 41837, - 41838, - 41839, - 41840, - 41841, - 41842, - 41843, - 41844, - 41845, - 41846, - 41847, - 41848, - 41849, - 41850, - 41851, - 41852, - 41853, - 41854, - 41855, - 41856, - 41857, - 41858, - 41859, - 41860, - 41861, - 41862, - 41863, - 41864, - 41865, - 41866, - 41867, - 41868, - 41869, - 41870, - 41871, - 41872, - 41873, - 41874, - 41875, - 41876, - 41877, - 41878, - 41879, - 41880, - 41881, - 41882, - 41883, - 41884, - 41885, - 41886, - 41887, - 41888, - 41889, - 41890, - 41891, - 41892, - 41893, - 41894, - 41895, - 41896, - 41897, - 41898, - 41899, - 41900, - 41901, - 41902, - 41903, - 41904, - 41905, - 41906, - 41907, - 41908, - 41909, - 41910, - 41911, - 41912, - 41913, - 41914, - 41915, - 41916, - 41917, - 41918, - 41919, - 41920, - 41921, - 41922, - 41923, - 41924, - 41925, - 41926, - 41927, - 41928, - 41929, - 41930, - 41931, - 41932, - 41933, - 41934, - 41935, - 41936, - 41937, - 41938, - 41939, - 41940, - 41941, - 41942, - 41943, - 41944, - 41945, - 41946, - 41947, - 41948, - 41949, - 41950, - 41951, - 41952, - 41953, - 41954, - 41955, - 41956, - 41957, - 41958, - 41959, - 41960, - 41961, - 41962, - 41963, - 41964, - 41965, - 41966, - 41967, - 41968, - 41969, - 41970, - 41971, - 41972, - 41973, - 41974, - 41975, - 41976, - 41977, - 41978, - 41979, - 41980, - 41981, - 41982, - 41983, - 41984, - 41985, - 41986, - 41987, - 41988, - 41989, - 41990, - 41991, - 41992, - 41993, - 41994, - 41995, - 41996, - 41997, - 41998, - 41999, - 42000, - 42001, - 42002, - 42003, - 42004, - 42005, - 42006, - 42007, - 42008, - 42009, - 42010, - 42011, - 42012, - 42013, - 42014, - 42015, - 42016, - 42017, - 42018, - 42019, - 42020, - 42021, - 42022, - 42023, - 42024, - 42025, - 42026, - 42027, - 42028, - 42029, - 42030, - 42031, - 42032, - 42033, - 42034, - 42035, - 42036, - 42037, - 42038, - 42039, - 42040, - 42041, - 42042, - 42043, - 42044, - 42045, - 42046, - 42047, - 42048, - 42049, - 42050, - 42051, - 42052, - 42053, - 42054, - 42055, - 42056, - 42057, - 42058, - 42059, - 42060, - 42061, - 42062, - 42063, - 42064, - 42065, - 42066, - 42067, - 42068, - 42069, - 42070, - 42071, - 42072, - 42073, - 42074, - 42075, - 42076, - 42077, - 42078, - 42079, - 42080, - 42081, - 42082, - 42083, - 42084, - 42085, - 42086, - 42087, - 42088, - 42089, - 42090, - 42091, - 42092, - 42093, - 42094, - 42095, - 42096, - 42097, - 42098, - 42099, - 42100, - 42101, - 42102, - 42103, - 42104, - 42105, - 42106, - 42107, - 42108, - 42109, - 42110, - 42111, - 42112, - 42113, - 42114, - 42115, - 42116, - 42117, - 42118, - 42119, - 42120, - 42121, - 42122, - 42123, - 42124, - 42192, - 42193, - 42194, - 42195, - 42196, - 42197, - 42198, - 42199, - 42200, - 42201, - 42202, - 42203, - 42204, - 42205, - 42206, - 42207, - 42208, - 42209, - 42210, - 42211, - 42212, - 42213, - 42214, - 42215, - 42216, - 42217, - 42218, - 42219, - 42220, - 42221, - 42222, - 42223, - 42224, - 42225, - 42226, - 42227, - 42228, - 42229, - 42230, - 42231, - 42232, - 42233, - 42234, - 42235, - 42236, - 42237, - 42240, - 42241, - 42242, - 42243, - 42244, - 42245, - 42246, - 42247, - 42248, - 42249, - 42250, - 42251, - 42252, - 42253, - 42254, - 42255, - 42256, - 42257, - 42258, - 42259, - 42260, - 42261, - 42262, - 42263, - 42264, - 42265, - 42266, - 42267, - 42268, - 42269, - 42270, - 42271, - 42272, - 42273, - 42274, - 42275, - 42276, - 42277, - 42278, - 42279, - 42280, - 42281, - 42282, - 42283, - 42284, - 42285, - 42286, - 42287, - 42288, - 42289, - 42290, - 42291, - 42292, - 42293, - 42294, - 42295, - 42296, - 42297, - 42298, - 42299, - 42300, - 42301, - 42302, - 42303, - 42304, - 42305, - 42306, - 42307, - 42308, - 42309, - 42310, - 42311, - 42312, - 42313, - 42314, - 42315, - 42316, - 42317, - 42318, - 42319, - 42320, - 42321, - 42322, - 42323, - 42324, - 42325, - 42326, - 42327, - 42328, - 42329, - 42330, - 42331, - 42332, - 42333, - 42334, - 42335, - 42336, - 42337, - 42338, - 42339, - 42340, - 42341, - 42342, - 42343, - 42344, - 42345, - 42346, - 42347, - 42348, - 42349, - 42350, - 42351, - 42352, - 42353, - 42354, - 42355, - 42356, - 42357, - 42358, - 42359, - 42360, - 42361, - 42362, - 42363, - 42364, - 42365, - 42366, - 42367, - 42368, - 42369, - 42370, - 42371, - 42372, - 42373, - 42374, - 42375, - 42376, - 42377, - 42378, - 42379, - 42380, - 42381, - 42382, - 42383, - 42384, - 42385, - 42386, - 42387, - 42388, - 42389, - 42390, - 42391, - 42392, - 42393, - 42394, - 42395, - 42396, - 42397, - 42398, - 42399, - 42400, - 42401, - 42402, - 42403, - 42404, - 42405, - 42406, - 42407, - 42408, - 42409, - 42410, - 42411, - 42412, - 42413, - 42414, - 42415, - 42416, - 42417, - 42418, - 42419, - 42420, - 42421, - 42422, - 42423, - 42424, - 42425, - 42426, - 42427, - 42428, - 42429, - 42430, - 42431, - 42432, - 42433, - 42434, - 42435, - 42436, - 42437, - 42438, - 42439, - 42440, - 42441, - 42442, - 42443, - 42444, - 42445, - 42446, - 42447, - 42448, - 42449, - 42450, - 42451, - 42452, - 42453, - 42454, - 42455, - 42456, - 42457, - 42458, - 42459, - 42460, - 42461, - 42462, - 42463, - 42464, - 42465, - 42466, - 42467, - 42468, - 42469, - 42470, - 42471, - 42472, - 42473, - 42474, - 42475, - 42476, - 42477, - 42478, - 42479, - 42480, - 42481, - 42482, - 42483, - 42484, - 42485, - 42486, - 42487, - 42488, - 42489, - 42490, - 42491, - 42492, - 42493, - 42494, - 42495, - 42496, - 42497, - 42498, - 42499, - 42500, - 42501, - 42502, - 42503, - 42504, - 42505, - 42506, - 42507, - 42508, - 42512, - 42513, - 42514, - 42515, - 42516, - 42517, - 42518, - 42519, - 42520, - 42521, - 42522, - 42523, - 42524, - 42525, - 42526, - 42527, - 42538, - 42539, - 42560, - 42561, - 42562, - 42563, - 42564, - 42565, - 42566, - 42567, - 42568, - 42569, - 42570, - 42571, - 42572, - 42573, - 42574, - 42575, - 42576, - 42577, - 42578, - 42579, - 42580, - 42581, - 42582, - 42583, - 42584, - 42585, - 42586, - 42587, - 42588, - 42589, - 42590, - 42591, - 42592, - 42593, - 42594, - 42595, - 42596, - 42597, - 42598, - 42599, - 42600, - 42601, - 42602, - 42603, - 42604, - 42605, - 42606, - 42623, - 42624, - 42625, - 42626, - 42627, - 42628, - 42629, - 42630, - 42631, - 42632, - 42633, - 42634, - 42635, - 42636, - 42637, - 42638, - 42639, - 42640, - 42641, - 42642, - 42643, - 42644, - 42645, - 42646, - 42647, - 42656, - 42657, - 42658, - 42659, - 42660, - 42661, - 42662, - 42663, - 42664, - 42665, - 42666, - 42667, - 42668, - 42669, - 42670, - 42671, - 42672, - 42673, - 42674, - 42675, - 42676, - 42677, - 42678, - 42679, - 42680, - 42681, - 42682, - 42683, - 42684, - 42685, - 42686, - 42687, - 42688, - 42689, - 42690, - 42691, - 42692, - 42693, - 42694, - 42695, - 42696, - 42697, - 42698, - 42699, - 42700, - 42701, - 42702, - 42703, - 42704, - 42705, - 42706, - 42707, - 42708, - 42709, - 42710, - 42711, - 42712, - 42713, - 42714, - 42715, - 42716, - 42717, - 42718, - 42719, - 42720, - 42721, - 42722, - 42723, - 42724, - 42725, - 42726, - 42727, - 42728, - 42729, - 42730, - 42731, - 42732, - 42733, - 42734, - 42735, - 42775, - 42776, - 42777, - 42778, - 42779, - 42780, - 42781, - 42782, - 42783, - 42786, - 42787, - 42788, - 42789, - 42790, - 42791, - 42792, - 42793, - 42794, - 42795, - 42796, - 42797, - 42798, - 42799, - 42800, - 42801, - 42802, - 42803, - 42804, - 42805, - 42806, - 42807, - 42808, - 42809, - 42810, - 42811, - 42812, - 42813, - 42814, - 42815, - 42816, - 42817, - 42818, - 42819, - 42820, - 42821, - 42822, - 42823, - 42824, - 42825, - 42826, - 42827, - 42828, - 42829, - 42830, - 42831, - 42832, - 42833, - 42834, - 42835, - 42836, - 42837, - 42838, - 42839, - 42840, - 42841, - 42842, - 42843, - 42844, - 42845, - 42846, - 42847, - 42848, - 42849, - 42850, - 42851, - 42852, - 42853, - 42854, - 42855, - 42856, - 42857, - 42858, - 42859, - 42860, - 42861, - 42862, - 42863, - 42864, - 42865, - 42866, - 42867, - 42868, - 42869, - 42870, - 42871, - 42872, - 42873, - 42874, - 42875, - 42876, - 42877, - 42878, - 42879, - 42880, - 42881, - 42882, - 42883, - 42884, - 42885, - 42886, - 42887, - 42888, - 42891, - 42892, - 42893, - 42894, - 42896, - 42897, - 42898, - 42899, - 42912, - 42913, - 42914, - 42915, - 42916, - 42917, - 42918, - 42919, - 42920, - 42921, - 42922, - 43000, - 43001, - 43002, - 43003, - 43004, - 43005, - 43006, - 43007, - 43008, - 43009, - 43011, - 43012, - 43013, - 43015, - 43016, - 43017, - 43018, - 43020, - 43021, - 43022, - 43023, - 43024, - 43025, - 43026, - 43027, - 43028, - 43029, - 43030, - 43031, - 43032, - 43033, - 43034, - 43035, - 43036, - 43037, - 43038, - 43039, - 43040, - 43041, - 43042, - 43072, - 43073, - 43074, - 43075, - 43076, - 43077, - 43078, - 43079, - 43080, - 43081, - 43082, - 43083, - 43084, - 43085, - 43086, - 43087, - 43088, - 43089, - 43090, - 43091, - 43092, - 43093, - 43094, - 43095, - 43096, - 43097, - 43098, - 43099, - 43100, - 43101, - 43102, - 43103, - 43104, - 43105, - 43106, - 43107, - 43108, - 43109, - 43110, - 43111, - 43112, - 43113, - 43114, - 43115, - 43116, - 43117, - 43118, - 43119, - 43120, - 43121, - 43122, - 43123, - 43138, - 43139, - 43140, - 43141, - 43142, - 43143, - 43144, - 43145, - 43146, - 43147, - 43148, - 43149, - 43150, - 43151, - 43152, - 43153, - 43154, - 43155, - 43156, - 43157, - 43158, - 43159, - 43160, - 43161, - 43162, - 43163, - 43164, - 43165, - 43166, - 43167, - 43168, - 43169, - 43170, - 43171, - 43172, - 43173, - 43174, - 43175, - 43176, - 43177, - 43178, - 43179, - 43180, - 43181, - 43182, - 43183, - 43184, - 43185, - 43186, - 43187, - 43250, - 43251, - 43252, - 43253, - 43254, - 43255, - 43259, - 43274, - 43275, - 43276, - 43277, - 43278, - 43279, - 43280, - 43281, - 43282, - 43283, - 43284, - 43285, - 43286, - 43287, - 43288, - 43289, - 43290, - 43291, - 43292, - 43293, - 43294, - 43295, - 43296, - 43297, - 43298, - 43299, - 43300, - 43301, - 43312, - 43313, - 43314, - 43315, - 43316, - 43317, - 43318, - 43319, - 43320, - 43321, - 43322, - 43323, - 43324, - 43325, - 43326, - 43327, - 43328, - 43329, - 43330, - 43331, - 43332, - 43333, - 43334, - 43360, - 43361, - 43362, - 43363, - 43364, - 43365, - 43366, - 43367, - 43368, - 43369, - 43370, - 43371, - 43372, - 43373, - 43374, - 43375, - 43376, - 43377, - 43378, - 43379, - 43380, - 43381, - 43382, - 43383, - 43384, - 43385, - 43386, - 43387, - 43388, - 43396, - 43397, - 43398, - 43399, - 43400, - 43401, - 43402, - 43403, - 43404, - 43405, - 43406, - 43407, - 43408, - 43409, - 43410, - 43411, - 43412, - 43413, - 43414, - 43415, - 43416, - 43417, - 43418, - 43419, - 43420, - 43421, - 43422, - 43423, - 43424, - 43425, - 43426, - 43427, - 43428, - 43429, - 43430, - 43431, - 43432, - 43433, - 43434, - 43435, - 43436, - 43437, - 43438, - 43439, - 43440, - 43441, - 43442, - 43471, - 43520, - 43521, - 43522, - 43523, - 43524, - 43525, - 43526, - 43527, - 43528, - 43529, - 43530, - 43531, - 43532, - 43533, - 43534, - 43535, - 43536, - 43537, - 43538, - 43539, - 43540, - 43541, - 43542, - 43543, - 43544, - 43545, - 43546, - 43547, - 43548, - 43549, - 43550, - 43551, - 43552, - 43553, - 43554, - 43555, - 43556, - 43557, - 43558, - 43559, - 43560, - 43584, - 43585, - 43586, - 43588, - 43589, - 43590, - 43591, - 43592, - 43593, - 43594, - 43595, - 43616, - 43617, - 43618, - 43619, - 43620, - 43621, - 43622, - 43623, - 43624, - 43625, - 43626, - 43627, - 43628, - 43629, - 43630, - 43631, - 43632, - 43633, - 43634, - 43635, - 43636, - 43637, - 43638, - 43642, - 43648, - 43649, - 43650, - 43651, - 43652, - 43653, - 43654, - 43655, - 43656, - 43657, - 43658, - 43659, - 43660, - 43661, - 43662, - 43663, - 43664, - 43665, - 43666, - 43667, - 43668, - 43669, - 43670, - 43671, - 43672, - 43673, - 43674, - 43675, - 43676, - 43677, - 43678, - 43679, - 43680, - 43681, - 43682, - 43683, - 43684, - 43685, - 43686, - 43687, - 43688, - 43689, - 43690, - 43691, - 43692, - 43693, - 43694, - 43695, - 43697, - 43701, - 43702, - 43705, - 43706, - 43707, - 43708, - 43709, - 43712, - 43714, - 43739, - 43740, - 43741, - 43744, - 43745, - 43746, - 43747, - 43748, - 43749, - 43750, - 43751, - 43752, - 43753, - 43754, - 43762, - 43763, - 43764, - 43777, - 43778, - 43779, - 43780, - 43781, - 43782, - 43785, - 43786, - 43787, - 43788, - 43789, - 43790, - 43793, - 43794, - 43795, - 43796, - 43797, - 43798, - 43808, - 43809, - 43810, - 43811, - 43812, - 43813, - 43814, - 43816, - 43817, - 43818, - 43819, - 43820, - 43821, - 43822, - 43968, - 43969, - 43970, - 43971, - 43972, - 43973, - 43974, - 43975, - 43976, - 43977, - 43978, - 43979, - 43980, - 43981, - 43982, - 43983, - 43984, - 43985, - 43986, - 43987, - 43988, - 43989, - 43990, - 43991, - 43992, - 43993, - 43994, - 43995, - 43996, - 43997, - 43998, - 43999, - 44000, - 44001, - 44002, - 44032, - 44033, - 44034, - 44035, - 44036, - 44037, - 44038, - 44039, - 44040, - 44041, - 44042, - 44043, - 44044, - 44045, - 44046, - 44047, - 44048, - 44049, - 44050, - 44051, - 44052, - 44053, - 44054, - 44055, - 44056, - 44057, - 44058, - 44059, - 44060, - 44061, - 44062, - 44063, - 44064, - 44065, - 44066, - 44067, - 44068, - 44069, - 44070, - 44071, - 44072, - 44073, - 44074, - 44075, - 44076, - 44077, - 44078, - 44079, - 44080, - 44081, - 44082, - 44083, - 44084, - 44085, - 44086, - 44087, - 44088, - 44089, - 44090, - 44091, - 44092, - 44093, - 44094, - 44095, - 44096, - 44097, - 44098, - 44099, - 44100, - 44101, - 44102, - 44103, - 44104, - 44105, - 44106, - 44107, - 44108, - 44109, - 44110, - 44111, - 44112, - 44113, - 44114, - 44115, - 44116, - 44117, - 44118, - 44119, - 44120, - 44121, - 44122, - 44123, - 44124, - 44125, - 44126, - 44127, - 44128, - 44129, - 44130, - 44131, - 44132, - 44133, - 44134, - 44135, - 44136, - 44137, - 44138, - 44139, - 44140, - 44141, - 44142, - 44143, - 44144, - 44145, - 44146, - 44147, - 44148, - 44149, - 44150, - 44151, - 44152, - 44153, - 44154, - 44155, - 44156, - 44157, - 44158, - 44159, - 44160, - 44161, - 44162, - 44163, - 44164, - 44165, - 44166, - 44167, - 44168, - 44169, - 44170, - 44171, - 44172, - 44173, - 44174, - 44175, - 44176, - 44177, - 44178, - 44179, - 44180, - 44181, - 44182, - 44183, - 44184, - 44185, - 44186, - 44187, - 44188, - 44189, - 44190, - 44191, - 44192, - 44193, - 44194, - 44195, - 44196, - 44197, - 44198, - 44199, - 44200, - 44201, - 44202, - 44203, - 44204, - 44205, - 44206, - 44207, - 44208, - 44209, - 44210, - 44211, - 44212, - 44213, - 44214, - 44215, - 44216, - 44217, - 44218, - 44219, - 44220, - 44221, - 44222, - 44223, - 44224, - 44225, - 44226, - 44227, - 44228, - 44229, - 44230, - 44231, - 44232, - 44233, - 44234, - 44235, - 44236, - 44237, - 44238, - 44239, - 44240, - 44241, - 44242, - 44243, - 44244, - 44245, - 44246, - 44247, - 44248, - 44249, - 44250, - 44251, - 44252, - 44253, - 44254, - 44255, - 44256, - 44257, - 44258, - 44259, - 44260, - 44261, - 44262, - 44263, - 44264, - 44265, - 44266, - 44267, - 44268, - 44269, - 44270, - 44271, - 44272, - 44273, - 44274, - 44275, - 44276, - 44277, - 44278, - 44279, - 44280, - 44281, - 44282, - 44283, - 44284, - 44285, - 44286, - 44287, - 44288, - 44289, - 44290, - 44291, - 44292, - 44293, - 44294, - 44295, - 44296, - 44297, - 44298, - 44299, - 44300, - 44301, - 44302, - 44303, - 44304, - 44305, - 44306, - 44307, - 44308, - 44309, - 44310, - 44311, - 44312, - 44313, - 44314, - 44315, - 44316, - 44317, - 44318, - 44319, - 44320, - 44321, - 44322, - 44323, - 44324, - 44325, - 44326, - 44327, - 44328, - 44329, - 44330, - 44331, - 44332, - 44333, - 44334, - 44335, - 44336, - 44337, - 44338, - 44339, - 44340, - 44341, - 44342, - 44343, - 44344, - 44345, - 44346, - 44347, - 44348, - 44349, - 44350, - 44351, - 44352, - 44353, - 44354, - 44355, - 44356, - 44357, - 44358, - 44359, - 44360, - 44361, - 44362, - 44363, - 44364, - 44365, - 44366, - 44367, - 44368, - 44369, - 44370, - 44371, - 44372, - 44373, - 44374, - 44375, - 44376, - 44377, - 44378, - 44379, - 44380, - 44381, - 44382, - 44383, - 44384, - 44385, - 44386, - 44387, - 44388, - 44389, - 44390, - 44391, - 44392, - 44393, - 44394, - 44395, - 44396, - 44397, - 44398, - 44399, - 44400, - 44401, - 44402, - 44403, - 44404, - 44405, - 44406, - 44407, - 44408, - 44409, - 44410, - 44411, - 44412, - 44413, - 44414, - 44415, - 44416, - 44417, - 44418, - 44419, - 44420, - 44421, - 44422, - 44423, - 44424, - 44425, - 44426, - 44427, - 44428, - 44429, - 44430, - 44431, - 44432, - 44433, - 44434, - 44435, - 44436, - 44437, - 44438, - 44439, - 44440, - 44441, - 44442, - 44443, - 44444, - 44445, - 44446, - 44447, - 44448, - 44449, - 44450, - 44451, - 44452, - 44453, - 44454, - 44455, - 44456, - 44457, - 44458, - 44459, - 44460, - 44461, - 44462, - 44463, - 44464, - 44465, - 44466, - 44467, - 44468, - 44469, - 44470, - 44471, - 44472, - 44473, - 44474, - 44475, - 44476, - 44477, - 44478, - 44479, - 44480, - 44481, - 44482, - 44483, - 44484, - 44485, - 44486, - 44487, - 44488, - 44489, - 44490, - 44491, - 44492, - 44493, - 44494, - 44495, - 44496, - 44497, - 44498, - 44499, - 44500, - 44501, - 44502, - 44503, - 44504, - 44505, - 44506, - 44507, - 44508, - 44509, - 44510, - 44511, - 44512, - 44513, - 44514, - 44515, - 44516, - 44517, - 44518, - 44519, - 44520, - 44521, - 44522, - 44523, - 44524, - 44525, - 44526, - 44527, - 44528, - 44529, - 44530, - 44531, - 44532, - 44533, - 44534, - 44535, - 44536, - 44537, - 44538, - 44539, - 44540, - 44541, - 44542, - 44543, - 44544, - 44545, - 44546, - 44547, - 44548, - 44549, - 44550, - 44551, - 44552, - 44553, - 44554, - 44555, - 44556, - 44557, - 44558, - 44559, - 44560, - 44561, - 44562, - 44563, - 44564, - 44565, - 44566, - 44567, - 44568, - 44569, - 44570, - 44571, - 44572, - 44573, - 44574, - 44575, - 44576, - 44577, - 44578, - 44579, - 44580, - 44581, - 44582, - 44583, - 44584, - 44585, - 44586, - 44587, - 44588, - 44589, - 44590, - 44591, - 44592, - 44593, - 44594, - 44595, - 44596, - 44597, - 44598, - 44599, - 44600, - 44601, - 44602, - 44603, - 44604, - 44605, - 44606, - 44607, - 44608, - 44609, - 44610, - 44611, - 44612, - 44613, - 44614, - 44615, - 44616, - 44617, - 44618, - 44619, - 44620, - 44621, - 44622, - 44623, - 44624, - 44625, - 44626, - 44627, - 44628, - 44629, - 44630, - 44631, - 44632, - 44633, - 44634, - 44635, - 44636, - 44637, - 44638, - 44639, - 44640, - 44641, - 44642, - 44643, - 44644, - 44645, - 44646, - 44647, - 44648, - 44649, - 44650, - 44651, - 44652, - 44653, - 44654, - 44655, - 44656, - 44657, - 44658, - 44659, - 44660, - 44661, - 44662, - 44663, - 44664, - 44665, - 44666, - 44667, - 44668, - 44669, - 44670, - 44671, - 44672, - 44673, - 44674, - 44675, - 44676, - 44677, - 44678, - 44679, - 44680, - 44681, - 44682, - 44683, - 44684, - 44685, - 44686, - 44687, - 44688, - 44689, - 44690, - 44691, - 44692, - 44693, - 44694, - 44695, - 44696, - 44697, - 44698, - 44699, - 44700, - 44701, - 44702, - 44703, - 44704, - 44705, - 44706, - 44707, - 44708, - 44709, - 44710, - 44711, - 44712, - 44713, - 44714, - 44715, - 44716, - 44717, - 44718, - 44719, - 44720, - 44721, - 44722, - 44723, - 44724, - 44725, - 44726, - 44727, - 44728, - 44729, - 44730, - 44731, - 44732, - 44733, - 44734, - 44735, - 44736, - 44737, - 44738, - 44739, - 44740, - 44741, - 44742, - 44743, - 44744, - 44745, - 44746, - 44747, - 44748, - 44749, - 44750, - 44751, - 44752, - 44753, - 44754, - 44755, - 44756, - 44757, - 44758, - 44759, - 44760, - 44761, - 44762, - 44763, - 44764, - 44765, - 44766, - 44767, - 44768, - 44769, - 44770, - 44771, - 44772, - 44773, - 44774, - 44775, - 44776, - 44777, - 44778, - 44779, - 44780, - 44781, - 44782, - 44783, - 44784, - 44785, - 44786, - 44787, - 44788, - 44789, - 44790, - 44791, - 44792, - 44793, - 44794, - 44795, - 44796, - 44797, - 44798, - 44799, - 44800, - 44801, - 44802, - 44803, - 44804, - 44805, - 44806, - 44807, - 44808, - 44809, - 44810, - 44811, - 44812, - 44813, - 44814, - 44815, - 44816, - 44817, - 44818, - 44819, - 44820, - 44821, - 44822, - 44823, - 44824, - 44825, - 44826, - 44827, - 44828, - 44829, - 44830, - 44831, - 44832, - 44833, - 44834, - 44835, - 44836, - 44837, - 44838, - 44839, - 44840, - 44841, - 44842, - 44843, - 44844, - 44845, - 44846, - 44847, - 44848, - 44849, - 44850, - 44851, - 44852, - 44853, - 44854, - 44855, - 44856, - 44857, - 44858, - 44859, - 44860, - 44861, - 44862, - 44863, - 44864, - 44865, - 44866, - 44867, - 44868, - 44869, - 44870, - 44871, - 44872, - 44873, - 44874, - 44875, - 44876, - 44877, - 44878, - 44879, - 44880, - 44881, - 44882, - 44883, - 44884, - 44885, - 44886, - 44887, - 44888, - 44889, - 44890, - 44891, - 44892, - 44893, - 44894, - 44895, - 44896, - 44897, - 44898, - 44899, - 44900, - 44901, - 44902, - 44903, - 44904, - 44905, - 44906, - 44907, - 44908, - 44909, - 44910, - 44911, - 44912, - 44913, - 44914, - 44915, - 44916, - 44917, - 44918, - 44919, - 44920, - 44921, - 44922, - 44923, - 44924, - 44925, - 44926, - 44927, - 44928, - 44929, - 44930, - 44931, - 44932, - 44933, - 44934, - 44935, - 44936, - 44937, - 44938, - 44939, - 44940, - 44941, - 44942, - 44943, - 44944, - 44945, - 44946, - 44947, - 44948, - 44949, - 44950, - 44951, - 44952, - 44953, - 44954, - 44955, - 44956, - 44957, - 44958, - 44959, - 44960, - 44961, - 44962, - 44963, - 44964, - 44965, - 44966, - 44967, - 44968, - 44969, - 44970, - 44971, - 44972, - 44973, - 44974, - 44975, - 44976, - 44977, - 44978, - 44979, - 44980, - 44981, - 44982, - 44983, - 44984, - 44985, - 44986, - 44987, - 44988, - 44989, - 44990, - 44991, - 44992, - 44993, - 44994, - 44995, - 44996, - 44997, - 44998, - 44999, - 45000, - 45001, - 45002, - 45003, - 45004, - 45005, - 45006, - 45007, - 45008, - 45009, - 45010, - 45011, - 45012, - 45013, - 45014, - 45015, - 45016, - 45017, - 45018, - 45019, - 45020, - 45021, - 45022, - 45023, - 45024, - 45025, - 45026, - 45027, - 45028, - 45029, - 45030, - 45031, - 45032, - 45033, - 45034, - 45035, - 45036, - 45037, - 45038, - 45039, - 45040, - 45041, - 45042, - 45043, - 45044, - 45045, - 45046, - 45047, - 45048, - 45049, - 45050, - 45051, - 45052, - 45053, - 45054, - 45055, - 45056, - 45057, - 45058, - 45059, - 45060, - 45061, - 45062, - 45063, - 45064, - 45065, - 45066, - 45067, - 45068, - 45069, - 45070, - 45071, - 45072, - 45073, - 45074, - 45075, - 45076, - 45077, - 45078, - 45079, - 45080, - 45081, - 45082, - 45083, - 45084, - 45085, - 45086, - 45087, - 45088, - 45089, - 45090, - 45091, - 45092, - 45093, - 45094, - 45095, - 45096, - 45097, - 45098, - 45099, - 45100, - 45101, - 45102, - 45103, - 45104, - 45105, - 45106, - 45107, - 45108, - 45109, - 45110, - 45111, - 45112, - 45113, - 45114, - 45115, - 45116, - 45117, - 45118, - 45119, - 45120, - 45121, - 45122, - 45123, - 45124, - 45125, - 45126, - 45127, - 45128, - 45129, - 45130, - 45131, - 45132, - 45133, - 45134, - 45135, - 45136, - 45137, - 45138, - 45139, - 45140, - 45141, - 45142, - 45143, - 45144, - 45145, - 45146, - 45147, - 45148, - 45149, - 45150, - 45151, - 45152, - 45153, - 45154, - 45155, - 45156, - 45157, - 45158, - 45159, - 45160, - 45161, - 45162, - 45163, - 45164, - 45165, - 45166, - 45167, - 45168, - 45169, - 45170, - 45171, - 45172, - 45173, - 45174, - 45175, - 45176, - 45177, - 45178, - 45179, - 45180, - 45181, - 45182, - 45183, - 45184, - 45185, - 45186, - 45187, - 45188, - 45189, - 45190, - 45191, - 45192, - 45193, - 45194, - 45195, - 45196, - 45197, - 45198, - 45199, - 45200, - 45201, - 45202, - 45203, - 45204, - 45205, - 45206, - 45207, - 45208, - 45209, - 45210, - 45211, - 45212, - 45213, - 45214, - 45215, - 45216, - 45217, - 45218, - 45219, - 45220, - 45221, - 45222, - 45223, - 45224, - 45225, - 45226, - 45227, - 45228, - 45229, - 45230, - 45231, - 45232, - 45233, - 45234, - 45235, - 45236, - 45237, - 45238, - 45239, - 45240, - 45241, - 45242, - 45243, - 45244, - 45245, - 45246, - 45247, - 45248, - 45249, - 45250, - 45251, - 45252, - 45253, - 45254, - 45255, - 45256, - 45257, - 45258, - 45259, - 45260, - 45261, - 45262, - 45263, - 45264, - 45265, - 45266, - 45267, - 45268, - 45269, - 45270, - 45271, - 45272, - 45273, - 45274, - 45275, - 45276, - 45277, - 45278, - 45279, - 45280, - 45281, - 45282, - 45283, - 45284, - 45285, - 45286, - 45287, - 45288, - 45289, - 45290, - 45291, - 45292, - 45293, - 45294, - 45295, - 45296, - 45297, - 45298, - 45299, - 45300, - 45301, - 45302, - 45303, - 45304, - 45305, - 45306, - 45307, - 45308, - 45309, - 45310, - 45311, - 45312, - 45313, - 45314, - 45315, - 45316, - 45317, - 45318, - 45319, - 45320, - 45321, - 45322, - 45323, - 45324, - 45325, - 45326, - 45327, - 45328, - 45329, - 45330, - 45331, - 45332, - 45333, - 45334, - 45335, - 45336, - 45337, - 45338, - 45339, - 45340, - 45341, - 45342, - 45343, - 45344, - 45345, - 45346, - 45347, - 45348, - 45349, - 45350, - 45351, - 45352, - 45353, - 45354, - 45355, - 45356, - 45357, - 45358, - 45359, - 45360, - 45361, - 45362, - 45363, - 45364, - 45365, - 45366, - 45367, - 45368, - 45369, - 45370, - 45371, - 45372, - 45373, - 45374, - 45375, - 45376, - 45377, - 45378, - 45379, - 45380, - 45381, - 45382, - 45383, - 45384, - 45385, - 45386, - 45387, - 45388, - 45389, - 45390, - 45391, - 45392, - 45393, - 45394, - 45395, - 45396, - 45397, - 45398, - 45399, - 45400, - 45401, - 45402, - 45403, - 45404, - 45405, - 45406, - 45407, - 45408, - 45409, - 45410, - 45411, - 45412, - 45413, - 45414, - 45415, - 45416, - 45417, - 45418, - 45419, - 45420, - 45421, - 45422, - 45423, - 45424, - 45425, - 45426, - 45427, - 45428, - 45429, - 45430, - 45431, - 45432, - 45433, - 45434, - 45435, - 45436, - 45437, - 45438, - 45439, - 45440, - 45441, - 45442, - 45443, - 45444, - 45445, - 45446, - 45447, - 45448, - 45449, - 45450, - 45451, - 45452, - 45453, - 45454, - 45455, - 45456, - 45457, - 45458, - 45459, - 45460, - 45461, - 45462, - 45463, - 45464, - 45465, - 45466, - 45467, - 45468, - 45469, - 45470, - 45471, - 45472, - 45473, - 45474, - 45475, - 45476, - 45477, - 45478, - 45479, - 45480, - 45481, - 45482, - 45483, - 45484, - 45485, - 45486, - 45487, - 45488, - 45489, - 45490, - 45491, - 45492, - 45493, - 45494, - 45495, - 45496, - 45497, - 45498, - 45499, - 45500, - 45501, - 45502, - 45503, - 45504, - 45505, - 45506, - 45507, - 45508, - 45509, - 45510, - 45511, - 45512, - 45513, - 45514, - 45515, - 45516, - 45517, - 45518, - 45519, - 45520, - 45521, - 45522, - 45523, - 45524, - 45525, - 45526, - 45527, - 45528, - 45529, - 45530, - 45531, - 45532, - 45533, - 45534, - 45535, - 45536, - 45537, - 45538, - 45539, - 45540, - 45541, - 45542, - 45543, - 45544, - 45545, - 45546, - 45547, - 45548, - 45549, - 45550, - 45551, - 45552, - 45553, - 45554, - 45555, - 45556, - 45557, - 45558, - 45559, - 45560, - 45561, - 45562, - 45563, - 45564, - 45565, - 45566, - 45567, - 45568, - 45569, - 45570, - 45571, - 45572, - 45573, - 45574, - 45575, - 45576, - 45577, - 45578, - 45579, - 45580, - 45581, - 45582, - 45583, - 45584, - 45585, - 45586, - 45587, - 45588, - 45589, - 45590, - 45591, - 45592, - 45593, - 45594, - 45595, - 45596, - 45597, - 45598, - 45599, - 45600, - 45601, - 45602, - 45603, - 45604, - 45605, - 45606, - 45607, - 45608, - 45609, - 45610, - 45611, - 45612, - 45613, - 45614, - 45615, - 45616, - 45617, - 45618, - 45619, - 45620, - 45621, - 45622, - 45623, - 45624, - 45625, - 45626, - 45627, - 45628, - 45629, - 45630, - 45631, - 45632, - 45633, - 45634, - 45635, - 45636, - 45637, - 45638, - 45639, - 45640, - 45641, - 45642, - 45643, - 45644, - 45645, - 45646, - 45647, - 45648, - 45649, - 45650, - 45651, - 45652, - 45653, - 45654, - 45655, - 45656, - 45657, - 45658, - 45659, - 45660, - 45661, - 45662, - 45663, - 45664, - 45665, - 45666, - 45667, - 45668, - 45669, - 45670, - 45671, - 45672, - 45673, - 45674, - 45675, - 45676, - 45677, - 45678, - 45679, - 45680, - 45681, - 45682, - 45683, - 45684, - 45685, - 45686, - 45687, - 45688, - 45689, - 45690, - 45691, - 45692, - 45693, - 45694, - 45695, - 45696, - 45697, - 45698, - 45699, - 45700, - 45701, - 45702, - 45703, - 45704, - 45705, - 45706, - 45707, - 45708, - 45709, - 45710, - 45711, - 45712, - 45713, - 45714, - 45715, - 45716, - 45717, - 45718, - 45719, - 45720, - 45721, - 45722, - 45723, - 45724, - 45725, - 45726, - 45727, - 45728, - 45729, - 45730, - 45731, - 45732, - 45733, - 45734, - 45735, - 45736, - 45737, - 45738, - 45739, - 45740, - 45741, - 45742, - 45743, - 45744, - 45745, - 45746, - 45747, - 45748, - 45749, - 45750, - 45751, - 45752, - 45753, - 45754, - 45755, - 45756, - 45757, - 45758, - 45759, - 45760, - 45761, - 45762, - 45763, - 45764, - 45765, - 45766, - 45767, - 45768, - 45769, - 45770, - 45771, - 45772, - 45773, - 45774, - 45775, - 45776, - 45777, - 45778, - 45779, - 45780, - 45781, - 45782, - 45783, - 45784, - 45785, - 45786, - 45787, - 45788, - 45789, - 45790, - 45791, - 45792, - 45793, - 45794, - 45795, - 45796, - 45797, - 45798, - 45799, - 45800, - 45801, - 45802, - 45803, - 45804, - 45805, - 45806, - 45807, - 45808, - 45809, - 45810, - 45811, - 45812, - 45813, - 45814, - 45815, - 45816, - 45817, - 45818, - 45819, - 45820, - 45821, - 45822, - 45823, - 45824, - 45825, - 45826, - 45827, - 45828, - 45829, - 45830, - 45831, - 45832, - 45833, - 45834, - 45835, - 45836, - 45837, - 45838, - 45839, - 45840, - 45841, - 45842, - 45843, - 45844, - 45845, - 45846, - 45847, - 45848, - 45849, - 45850, - 45851, - 45852, - 45853, - 45854, - 45855, - 45856, - 45857, - 45858, - 45859, - 45860, - 45861, - 45862, - 45863, - 45864, - 45865, - 45866, - 45867, - 45868, - 45869, - 45870, - 45871, - 45872, - 45873, - 45874, - 45875, - 45876, - 45877, - 45878, - 45879, - 45880, - 45881, - 45882, - 45883, - 45884, - 45885, - 45886, - 45887, - 45888, - 45889, - 45890, - 45891, - 45892, - 45893, - 45894, - 45895, - 45896, - 45897, - 45898, - 45899, - 45900, - 45901, - 45902, - 45903, - 45904, - 45905, - 45906, - 45907, - 45908, - 45909, - 45910, - 45911, - 45912, - 45913, - 45914, - 45915, - 45916, - 45917, - 45918, - 45919, - 45920, - 45921, - 45922, - 45923, - 45924, - 45925, - 45926, - 45927, - 45928, - 45929, - 45930, - 45931, - 45932, - 45933, - 45934, - 45935, - 45936, - 45937, - 45938, - 45939, - 45940, - 45941, - 45942, - 45943, - 45944, - 45945, - 45946, - 45947, - 45948, - 45949, - 45950, - 45951, - 45952, - 45953, - 45954, - 45955, - 45956, - 45957, - 45958, - 45959, - 45960, - 45961, - 45962, - 45963, - 45964, - 45965, - 45966, - 45967, - 45968, - 45969, - 45970, - 45971, - 45972, - 45973, - 45974, - 45975, - 45976, - 45977, - 45978, - 45979, - 45980, - 45981, - 45982, - 45983, - 45984, - 45985, - 45986, - 45987, - 45988, - 45989, - 45990, - 45991, - 45992, - 45993, - 45994, - 45995, - 45996, - 45997, - 45998, - 45999, - 46000, - 46001, - 46002, - 46003, - 46004, - 46005, - 46006, - 46007, - 46008, - 46009, - 46010, - 46011, - 46012, - 46013, - 46014, - 46015, - 46016, - 46017, - 46018, - 46019, - 46020, - 46021, - 46022, - 46023, - 46024, - 46025, - 46026, - 46027, - 46028, - 46029, - 46030, - 46031, - 46032, - 46033, - 46034, - 46035, - 46036, - 46037, - 46038, - 46039, - 46040, - 46041, - 46042, - 46043, - 46044, - 46045, - 46046, - 46047, - 46048, - 46049, - 46050, - 46051, - 46052, - 46053, - 46054, - 46055, - 46056, - 46057, - 46058, - 46059, - 46060, - 46061, - 46062, - 46063, - 46064, - 46065, - 46066, - 46067, - 46068, - 46069, - 46070, - 46071, - 46072, - 46073, - 46074, - 46075, - 46076, - 46077, - 46078, - 46079, - 46080, - 46081, - 46082, - 46083, - 46084, - 46085, - 46086, - 46087, - 46088, - 46089, - 46090, - 46091, - 46092, - 46093, - 46094, - 46095, - 46096, - 46097, - 46098, - 46099, - 46100, - 46101, - 46102, - 46103, - 46104, - 46105, - 46106, - 46107, - 46108, - 46109, - 46110, - 46111, - 46112, - 46113, - 46114, - 46115, - 46116, - 46117, - 46118, - 46119, - 46120, - 46121, - 46122, - 46123, - 46124, - 46125, - 46126, - 46127, - 46128, - 46129, - 46130, - 46131, - 46132, - 46133, - 46134, - 46135, - 46136, - 46137, - 46138, - 46139, - 46140, - 46141, - 46142, - 46143, - 46144, - 46145, - 46146, - 46147, - 46148, - 46149, - 46150, - 46151, - 46152, - 46153, - 46154, - 46155, - 46156, - 46157, - 46158, - 46159, - 46160, - 46161, - 46162, - 46163, - 46164, - 46165, - 46166, - 46167, - 46168, - 46169, - 46170, - 46171, - 46172, - 46173, - 46174, - 46175, - 46176, - 46177, - 46178, - 46179, - 46180, - 46181, - 46182, - 46183, - 46184, - 46185, - 46186, - 46187, - 46188, - 46189, - 46190, - 46191, - 46192, - 46193, - 46194, - 46195, - 46196, - 46197, - 46198, - 46199, - 46200, - 46201, - 46202, - 46203, - 46204, - 46205, - 46206, - 46207, - 46208, - 46209, - 46210, - 46211, - 46212, - 46213, - 46214, - 46215, - 46216, - 46217, - 46218, - 46219, - 46220, - 46221, - 46222, - 46223, - 46224, - 46225, - 46226, - 46227, - 46228, - 46229, - 46230, - 46231, - 46232, - 46233, - 46234, - 46235, - 46236, - 46237, - 46238, - 46239, - 46240, - 46241, - 46242, - 46243, - 46244, - 46245, - 46246, - 46247, - 46248, - 46249, - 46250, - 46251, - 46252, - 46253, - 46254, - 46255, - 46256, - 46257, - 46258, - 46259, - 46260, - 46261, - 46262, - 46263, - 46264, - 46265, - 46266, - 46267, - 46268, - 46269, - 46270, - 46271, - 46272, - 46273, - 46274, - 46275, - 46276, - 46277, - 46278, - 46279, - 46280, - 46281, - 46282, - 46283, - 46284, - 46285, - 46286, - 46287, - 46288, - 46289, - 46290, - 46291, - 46292, - 46293, - 46294, - 46295, - 46296, - 46297, - 46298, - 46299, - 46300, - 46301, - 46302, - 46303, - 46304, - 46305, - 46306, - 46307, - 46308, - 46309, - 46310, - 46311, - 46312, - 46313, - 46314, - 46315, - 46316, - 46317, - 46318, - 46319, - 46320, - 46321, - 46322, - 46323, - 46324, - 46325, - 46326, - 46327, - 46328, - 46329, - 46330, - 46331, - 46332, - 46333, - 46334, - 46335, - 46336, - 46337, - 46338, - 46339, - 46340, - 46341, - 46342, - 46343, - 46344, - 46345, - 46346, - 46347, - 46348, - 46349, - 46350, - 46351, - 46352, - 46353, - 46354, - 46355, - 46356, - 46357, - 46358, - 46359, - 46360, - 46361, - 46362, - 46363, - 46364, - 46365, - 46366, - 46367, - 46368, - 46369, - 46370, - 46371, - 46372, - 46373, - 46374, - 46375, - 46376, - 46377, - 46378, - 46379, - 46380, - 46381, - 46382, - 46383, - 46384, - 46385, - 46386, - 46387, - 46388, - 46389, - 46390, - 46391, - 46392, - 46393, - 46394, - 46395, - 46396, - 46397, - 46398, - 46399, - 46400, - 46401, - 46402, - 46403, - 46404, - 46405, - 46406, - 46407, - 46408, - 46409, - 46410, - 46411, - 46412, - 46413, - 46414, - 46415, - 46416, - 46417, - 46418, - 46419, - 46420, - 46421, - 46422, - 46423, - 46424, - 46425, - 46426, - 46427, - 46428, - 46429, - 46430, - 46431, - 46432, - 46433, - 46434, - 46435, - 46436, - 46437, - 46438, - 46439, - 46440, - 46441, - 46442, - 46443, - 46444, - 46445, - 46446, - 46447, - 46448, - 46449, - 46450, - 46451, - 46452, - 46453, - 46454, - 46455, - 46456, - 46457, - 46458, - 46459, - 46460, - 46461, - 46462, - 46463, - 46464, - 46465, - 46466, - 46467, - 46468, - 46469, - 46470, - 46471, - 46472, - 46473, - 46474, - 46475, - 46476, - 46477, - 46478, - 46479, - 46480, - 46481, - 46482, - 46483, - 46484, - 46485, - 46486, - 46487, - 46488, - 46489, - 46490, - 46491, - 46492, - 46493, - 46494, - 46495, - 46496, - 46497, - 46498, - 46499, - 46500, - 46501, - 46502, - 46503, - 46504, - 46505, - 46506, - 46507, - 46508, - 46509, - 46510, - 46511, - 46512, - 46513, - 46514, - 46515, - 46516, - 46517, - 46518, - 46519, - 46520, - 46521, - 46522, - 46523, - 46524, - 46525, - 46526, - 46527, - 46528, - 46529, - 46530, - 46531, - 46532, - 46533, - 46534, - 46535, - 46536, - 46537, - 46538, - 46539, - 46540, - 46541, - 46542, - 46543, - 46544, - 46545, - 46546, - 46547, - 46548, - 46549, - 46550, - 46551, - 46552, - 46553, - 46554, - 46555, - 46556, - 46557, - 46558, - 46559, - 46560, - 46561, - 46562, - 46563, - 46564, - 46565, - 46566, - 46567, - 46568, - 46569, - 46570, - 46571, - 46572, - 46573, - 46574, - 46575, - 46576, - 46577, - 46578, - 46579, - 46580, - 46581, - 46582, - 46583, - 46584, - 46585, - 46586, - 46587, - 46588, - 46589, - 46590, - 46591, - 46592, - 46593, - 46594, - 46595, - 46596, - 46597, - 46598, - 46599, - 46600, - 46601, - 46602, - 46603, - 46604, - 46605, - 46606, - 46607, - 46608, - 46609, - 46610, - 46611, - 46612, - 46613, - 46614, - 46615, - 46616, - 46617, - 46618, - 46619, - 46620, - 46621, - 46622, - 46623, - 46624, - 46625, - 46626, - 46627, - 46628, - 46629, - 46630, - 46631, - 46632, - 46633, - 46634, - 46635, - 46636, - 46637, - 46638, - 46639, - 46640, - 46641, - 46642, - 46643, - 46644, - 46645, - 46646, - 46647, - 46648, - 46649, - 46650, - 46651, - 46652, - 46653, - 46654, - 46655, - 46656, - 46657, - 46658, - 46659, - 46660, - 46661, - 46662, - 46663, - 46664, - 46665, - 46666, - 46667, - 46668, - 46669, - 46670, - 46671, - 46672, - 46673, - 46674, - 46675, - 46676, - 46677, - 46678, - 46679, - 46680, - 46681, - 46682, - 46683, - 46684, - 46685, - 46686, - 46687, - 46688, - 46689, - 46690, - 46691, - 46692, - 46693, - 46694, - 46695, - 46696, - 46697, - 46698, - 46699, - 46700, - 46701, - 46702, - 46703, - 46704, - 46705, - 46706, - 46707, - 46708, - 46709, - 46710, - 46711, - 46712, - 46713, - 46714, - 46715, - 46716, - 46717, - 46718, - 46719, - 46720, - 46721, - 46722, - 46723, - 46724, - 46725, - 46726, - 46727, - 46728, - 46729, - 46730, - 46731, - 46732, - 46733, - 46734, - 46735, - 46736, - 46737, - 46738, - 46739, - 46740, - 46741, - 46742, - 46743, - 46744, - 46745, - 46746, - 46747, - 46748, - 46749, - 46750, - 46751, - 46752, - 46753, - 46754, - 46755, - 46756, - 46757, - 46758, - 46759, - 46760, - 46761, - 46762, - 46763, - 46764, - 46765, - 46766, - 46767, - 46768, - 46769, - 46770, - 46771, - 46772, - 46773, - 46774, - 46775, - 46776, - 46777, - 46778, - 46779, - 46780, - 46781, - 46782, - 46783, - 46784, - 46785, - 46786, - 46787, - 46788, - 46789, - 46790, - 46791, - 46792, - 46793, - 46794, - 46795, - 46796, - 46797, - 46798, - 46799, - 46800, - 46801, - 46802, - 46803, - 46804, - 46805, - 46806, - 46807, - 46808, - 46809, - 46810, - 46811, - 46812, - 46813, - 46814, - 46815, - 46816, - 46817, - 46818, - 46819, - 46820, - 46821, - 46822, - 46823, - 46824, - 46825, - 46826, - 46827, - 46828, - 46829, - 46830, - 46831, - 46832, - 46833, - 46834, - 46835, - 46836, - 46837, - 46838, - 46839, - 46840, - 46841, - 46842, - 46843, - 46844, - 46845, - 46846, - 46847, - 46848, - 46849, - 46850, - 46851, - 46852, - 46853, - 46854, - 46855, - 46856, - 46857, - 46858, - 46859, - 46860, - 46861, - 46862, - 46863, - 46864, - 46865, - 46866, - 46867, - 46868, - 46869, - 46870, - 46871, - 46872, - 46873, - 46874, - 46875, - 46876, - 46877, - 46878, - 46879, - 46880, - 46881, - 46882, - 46883, - 46884, - 46885, - 46886, - 46887, - 46888, - 46889, - 46890, - 46891, - 46892, - 46893, - 46894, - 46895, - 46896, - 46897, - 46898, - 46899, - 46900, - 46901, - 46902, - 46903, - 46904, - 46905, - 46906, - 46907, - 46908, - 46909, - 46910, - 46911, - 46912, - 46913, - 46914, - 46915, - 46916, - 46917, - 46918, - 46919, - 46920, - 46921, - 46922, - 46923, - 46924, - 46925, - 46926, - 46927, - 46928, - 46929, - 46930, - 46931, - 46932, - 46933, - 46934, - 46935, - 46936, - 46937, - 46938, - 46939, - 46940, - 46941, - 46942, - 46943, - 46944, - 46945, - 46946, - 46947, - 46948, - 46949, - 46950, - 46951, - 46952, - 46953, - 46954, - 46955, - 46956, - 46957, - 46958, - 46959, - 46960, - 46961, - 46962, - 46963, - 46964, - 46965, - 46966, - 46967, - 46968, - 46969, - 46970, - 46971, - 46972, - 46973, - 46974, - 46975, - 46976, - 46977, - 46978, - 46979, - 46980, - 46981, - 46982, - 46983, - 46984, - 46985, - 46986, - 46987, - 46988, - 46989, - 46990, - 46991, - 46992, - 46993, - 46994, - 46995, - 46996, - 46997, - 46998, - 46999, - 47000, - 47001, - 47002, - 47003, - 47004, - 47005, - 47006, - 47007, - 47008, - 47009, - 47010, - 47011, - 47012, - 47013, - 47014, - 47015, - 47016, - 47017, - 47018, - 47019, - 47020, - 47021, - 47022, - 47023, - 47024, - 47025, - 47026, - 47027, - 47028, - 47029, - 47030, - 47031, - 47032, - 47033, - 47034, - 47035, - 47036, - 47037, - 47038, - 47039, - 47040, - 47041, - 47042, - 47043, - 47044, - 47045, - 47046, - 47047, - 47048, - 47049, - 47050, - 47051, - 47052, - 47053, - 47054, - 47055, - 47056, - 47057, - 47058, - 47059, - 47060, - 47061, - 47062, - 47063, - 47064, - 47065, - 47066, - 47067, - 47068, - 47069, - 47070, - 47071, - 47072, - 47073, - 47074, - 47075, - 47076, - 47077, - 47078, - 47079, - 47080, - 47081, - 47082, - 47083, - 47084, - 47085, - 47086, - 47087, - 47088, - 47089, - 47090, - 47091, - 47092, - 47093, - 47094, - 47095, - 47096, - 47097, - 47098, - 47099, - 47100, - 47101, - 47102, - 47103, - 47104, - 47105, - 47106, - 47107, - 47108, - 47109, - 47110, - 47111, - 47112, - 47113, - 47114, - 47115, - 47116, - 47117, - 47118, - 47119, - 47120, - 47121, - 47122, - 47123, - 47124, - 47125, - 47126, - 47127, - 47128, - 47129, - 47130, - 47131, - 47132, - 47133, - 47134, - 47135, - 47136, - 47137, - 47138, - 47139, - 47140, - 47141, - 47142, - 47143, - 47144, - 47145, - 47146, - 47147, - 47148, - 47149, - 47150, - 47151, - 47152, - 47153, - 47154, - 47155, - 47156, - 47157, - 47158, - 47159, - 47160, - 47161, - 47162, - 47163, - 47164, - 47165, - 47166, - 47167, - 47168, - 47169, - 47170, - 47171, - 47172, - 47173, - 47174, - 47175, - 47176, - 47177, - 47178, - 47179, - 47180, - 47181, - 47182, - 47183, - 47184, - 47185, - 47186, - 47187, - 47188, - 47189, - 47190, - 47191, - 47192, - 47193, - 47194, - 47195, - 47196, - 47197, - 47198, - 47199, - 47200, - 47201, - 47202, - 47203, - 47204, - 47205, - 47206, - 47207, - 47208, - 47209, - 47210, - 47211, - 47212, - 47213, - 47214, - 47215, - 47216, - 47217, - 47218, - 47219, - 47220, - 47221, - 47222, - 47223, - 47224, - 47225, - 47226, - 47227, - 47228, - 47229, - 47230, - 47231, - 47232, - 47233, - 47234, - 47235, - 47236, - 47237, - 47238, - 47239, - 47240, - 47241, - 47242, - 47243, - 47244, - 47245, - 47246, - 47247, - 47248, - 47249, - 47250, - 47251, - 47252, - 47253, - 47254, - 47255, - 47256, - 47257, - 47258, - 47259, - 47260, - 47261, - 47262, - 47263, - 47264, - 47265, - 47266, - 47267, - 47268, - 47269, - 47270, - 47271, - 47272, - 47273, - 47274, - 47275, - 47276, - 47277, - 47278, - 47279, - 47280, - 47281, - 47282, - 47283, - 47284, - 47285, - 47286, - 47287, - 47288, - 47289, - 47290, - 47291, - 47292, - 47293, - 47294, - 47295, - 47296, - 47297, - 47298, - 47299, - 47300, - 47301, - 47302, - 47303, - 47304, - 47305, - 47306, - 47307, - 47308, - 47309, - 47310, - 47311, - 47312, - 47313, - 47314, - 47315, - 47316, - 47317, - 47318, - 47319, - 47320, - 47321, - 47322, - 47323, - 47324, - 47325, - 47326, - 47327, - 47328, - 47329, - 47330, - 47331, - 47332, - 47333, - 47334, - 47335, - 47336, - 47337, - 47338, - 47339, - 47340, - 47341, - 47342, - 47343, - 47344, - 47345, - 47346, - 47347, - 47348, - 47349, - 47350, - 47351, - 47352, - 47353, - 47354, - 47355, - 47356, - 47357, - 47358, - 47359, - 47360, - 47361, - 47362, - 47363, - 47364, - 47365, - 47366, - 47367, - 47368, - 47369, - 47370, - 47371, - 47372, - 47373, - 47374, - 47375, - 47376, - 47377, - 47378, - 47379, - 47380, - 47381, - 47382, - 47383, - 47384, - 47385, - 47386, - 47387, - 47388, - 47389, - 47390, - 47391, - 47392, - 47393, - 47394, - 47395, - 47396, - 47397, - 47398, - 47399, - 47400, - 47401, - 47402, - 47403, - 47404, - 47405, - 47406, - 47407, - 47408, - 47409, - 47410, - 47411, - 47412, - 47413, - 47414, - 47415, - 47416, - 47417, - 47418, - 47419, - 47420, - 47421, - 47422, - 47423, - 47424, - 47425, - 47426, - 47427, - 47428, - 47429, - 47430, - 47431, - 47432, - 47433, - 47434, - 47435, - 47436, - 47437, - 47438, - 47439, - 47440, - 47441, - 47442, - 47443, - 47444, - 47445, - 47446, - 47447, - 47448, - 47449, - 47450, - 47451, - 47452, - 47453, - 47454, - 47455, - 47456, - 47457, - 47458, - 47459, - 47460, - 47461, - 47462, - 47463, - 47464, - 47465, - 47466, - 47467, - 47468, - 47469, - 47470, - 47471, - 47472, - 47473, - 47474, - 47475, - 47476, - 47477, - 47478, - 47479, - 47480, - 47481, - 47482, - 47483, - 47484, - 47485, - 47486, - 47487, - 47488, - 47489, - 47490, - 47491, - 47492, - 47493, - 47494, - 47495, - 47496, - 47497, - 47498, - 47499, - 47500, - 47501, - 47502, - 47503, - 47504, - 47505, - 47506, - 47507, - 47508, - 47509, - 47510, - 47511, - 47512, - 47513, - 47514, - 47515, - 47516, - 47517, - 47518, - 47519, - 47520, - 47521, - 47522, - 47523, - 47524, - 47525, - 47526, - 47527, - 47528, - 47529, - 47530, - 47531, - 47532, - 47533, - 47534, - 47535, - 47536, - 47537, - 47538, - 47539, - 47540, - 47541, - 47542, - 47543, - 47544, - 47545, - 47546, - 47547, - 47548, - 47549, - 47550, - 47551, - 47552, - 47553, - 47554, - 47555, - 47556, - 47557, - 47558, - 47559, - 47560, - 47561, - 47562, - 47563, - 47564, - 47565, - 47566, - 47567, - 47568, - 47569, - 47570, - 47571, - 47572, - 47573, - 47574, - 47575, - 47576, - 47577, - 47578, - 47579, - 47580, - 47581, - 47582, - 47583, - 47584, - 47585, - 47586, - 47587, - 47588, - 47589, - 47590, - 47591, - 47592, - 47593, - 47594, - 47595, - 47596, - 47597, - 47598, - 47599, - 47600, - 47601, - 47602, - 47603, - 47604, - 47605, - 47606, - 47607, - 47608, - 47609, - 47610, - 47611, - 47612, - 47613, - 47614, - 47615, - 47616, - 47617, - 47618, - 47619, - 47620, - 47621, - 47622, - 47623, - 47624, - 47625, - 47626, - 47627, - 47628, - 47629, - 47630, - 47631, - 47632, - 47633, - 47634, - 47635, - 47636, - 47637, - 47638, - 47639, - 47640, - 47641, - 47642, - 47643, - 47644, - 47645, - 47646, - 47647, - 47648, - 47649, - 47650, - 47651, - 47652, - 47653, - 47654, - 47655, - 47656, - 47657, - 47658, - 47659, - 47660, - 47661, - 47662, - 47663, - 47664, - 47665, - 47666, - 47667, - 47668, - 47669, - 47670, - 47671, - 47672, - 47673, - 47674, - 47675, - 47676, - 47677, - 47678, - 47679, - 47680, - 47681, - 47682, - 47683, - 47684, - 47685, - 47686, - 47687, - 47688, - 47689, - 47690, - 47691, - 47692, - 47693, - 47694, - 47695, - 47696, - 47697, - 47698, - 47699, - 47700, - 47701, - 47702, - 47703, - 47704, - 47705, - 47706, - 47707, - 47708, - 47709, - 47710, - 47711, - 47712, - 47713, - 47714, - 47715, - 47716, - 47717, - 47718, - 47719, - 47720, - 47721, - 47722, - 47723, - 47724, - 47725, - 47726, - 47727, - 47728, - 47729, - 47730, - 47731, - 47732, - 47733, - 47734, - 47735, - 47736, - 47737, - 47738, - 47739, - 47740, - 47741, - 47742, - 47743, - 47744, - 47745, - 47746, - 47747, - 47748, - 47749, - 47750, - 47751, - 47752, - 47753, - 47754, - 47755, - 47756, - 47757, - 47758, - 47759, - 47760, - 47761, - 47762, - 47763, - 47764, - 47765, - 47766, - 47767, - 47768, - 47769, - 47770, - 47771, - 47772, - 47773, - 47774, - 47775, - 47776, - 47777, - 47778, - 47779, - 47780, - 47781, - 47782, - 47783, - 47784, - 47785, - 47786, - 47787, - 47788, - 47789, - 47790, - 47791, - 47792, - 47793, - 47794, - 47795, - 47796, - 47797, - 47798, - 47799, - 47800, - 47801, - 47802, - 47803, - 47804, - 47805, - 47806, - 47807, - 47808, - 47809, - 47810, - 47811, - 47812, - 47813, - 47814, - 47815, - 47816, - 47817, - 47818, - 47819, - 47820, - 47821, - 47822, - 47823, - 47824, - 47825, - 47826, - 47827, - 47828, - 47829, - 47830, - 47831, - 47832, - 47833, - 47834, - 47835, - 47836, - 47837, - 47838, - 47839, - 47840, - 47841, - 47842, - 47843, - 47844, - 47845, - 47846, - 47847, - 47848, - 47849, - 47850, - 47851, - 47852, - 47853, - 47854, - 47855, - 47856, - 47857, - 47858, - 47859, - 47860, - 47861, - 47862, - 47863, - 47864, - 47865, - 47866, - 47867, - 47868, - 47869, - 47870, - 47871, - 47872, - 47873, - 47874, - 47875, - 47876, - 47877, - 47878, - 47879, - 47880, - 47881, - 47882, - 47883, - 47884, - 47885, - 47886, - 47887, - 47888, - 47889, - 47890, - 47891, - 47892, - 47893, - 47894, - 47895, - 47896, - 47897, - 47898, - 47899, - 47900, - 47901, - 47902, - 47903, - 47904, - 47905, - 47906, - 47907, - 47908, - 47909, - 47910, - 47911, - 47912, - 47913, - 47914, - 47915, - 47916, - 47917, - 47918, - 47919, - 47920, - 47921, - 47922, - 47923, - 47924, - 47925, - 47926, - 47927, - 47928, - 47929, - 47930, - 47931, - 47932, - 47933, - 47934, - 47935, - 47936, - 47937, - 47938, - 47939, - 47940, - 47941, - 47942, - 47943, - 47944, - 47945, - 47946, - 47947, - 47948, - 47949, - 47950, - 47951, - 47952, - 47953, - 47954, - 47955, - 47956, - 47957, - 47958, - 47959, - 47960, - 47961, - 47962, - 47963, - 47964, - 47965, - 47966, - 47967, - 47968, - 47969, - 47970, - 47971, - 47972, - 47973, - 47974, - 47975, - 47976, - 47977, - 47978, - 47979, - 47980, - 47981, - 47982, - 47983, - 47984, - 47985, - 47986, - 47987, - 47988, - 47989, - 47990, - 47991, - 47992, - 47993, - 47994, - 47995, - 47996, - 47997, - 47998, - 47999, - 48000, - 48001, - 48002, - 48003, - 48004, - 48005, - 48006, - 48007, - 48008, - 48009, - 48010, - 48011, - 48012, - 48013, - 48014, - 48015, - 48016, - 48017, - 48018, - 48019, - 48020, - 48021, - 48022, - 48023, - 48024, - 48025, - 48026, - 48027, - 48028, - 48029, - 48030, - 48031, - 48032, - 48033, - 48034, - 48035, - 48036, - 48037, - 48038, - 48039, - 48040, - 48041, - 48042, - 48043, - 48044, - 48045, - 48046, - 48047, - 48048, - 48049, - 48050, - 48051, - 48052, - 48053, - 48054, - 48055, - 48056, - 48057, - 48058, - 48059, - 48060, - 48061, - 48062, - 48063, - 48064, - 48065, - 48066, - 48067, - 48068, - 48069, - 48070, - 48071, - 48072, - 48073, - 48074, - 48075, - 48076, - 48077, - 48078, - 48079, - 48080, - 48081, - 48082, - 48083, - 48084, - 48085, - 48086, - 48087, - 48088, - 48089, - 48090, - 48091, - 48092, - 48093, - 48094, - 48095, - 48096, - 48097, - 48098, - 48099, - 48100, - 48101, - 48102, - 48103, - 48104, - 48105, - 48106, - 48107, - 48108, - 48109, - 48110, - 48111, - 48112, - 48113, - 48114, - 48115, - 48116, - 48117, - 48118, - 48119, - 48120, - 48121, - 48122, - 48123, - 48124, - 48125, - 48126, - 48127, - 48128, - 48129, - 48130, - 48131, - 48132, - 48133, - 48134, - 48135, - 48136, - 48137, - 48138, - 48139, - 48140, - 48141, - 48142, - 48143, - 48144, - 48145, - 48146, - 48147, - 48148, - 48149, - 48150, - 48151, - 48152, - 48153, - 48154, - 48155, - 48156, - 48157, - 48158, - 48159, - 48160, - 48161, - 48162, - 48163, - 48164, - 48165, - 48166, - 48167, - 48168, - 48169, - 48170, - 48171, - 48172, - 48173, - 48174, - 48175, - 48176, - 48177, - 48178, - 48179, - 48180, - 48181, - 48182, - 48183, - 48184, - 48185, - 48186, - 48187, - 48188, - 48189, - 48190, - 48191, - 48192, - 48193, - 48194, - 48195, - 48196, - 48197, - 48198, - 48199, - 48200, - 48201, - 48202, - 48203, - 48204, - 48205, - 48206, - 48207, - 48208, - 48209, - 48210, - 48211, - 48212, - 48213, - 48214, - 48215, - 48216, - 48217, - 48218, - 48219, - 48220, - 48221, - 48222, - 48223, - 48224, - 48225, - 48226, - 48227, - 48228, - 48229, - 48230, - 48231, - 48232, - 48233, - 48234, - 48235, - 48236, - 48237, - 48238, - 48239, - 48240, - 48241, - 48242, - 48243, - 48244, - 48245, - 48246, - 48247, - 48248, - 48249, - 48250, - 48251, - 48252, - 48253, - 48254, - 48255, - 48256, - 48257, - 48258, - 48259, - 48260, - 48261, - 48262, - 48263, - 48264, - 48265, - 48266, - 48267, - 48268, - 48269, - 48270, - 48271, - 48272, - 48273, - 48274, - 48275, - 48276, - 48277, - 48278, - 48279, - 48280, - 48281, - 48282, - 48283, - 48284, - 48285, - 48286, - 48287, - 48288, - 48289, - 48290, - 48291, - 48292, - 48293, - 48294, - 48295, - 48296, - 48297, - 48298, - 48299, - 48300, - 48301, - 48302, - 48303, - 48304, - 48305, - 48306, - 48307, - 48308, - 48309, - 48310, - 48311, - 48312, - 48313, - 48314, - 48315, - 48316, - 48317, - 48318, - 48319, - 48320, - 48321, - 48322, - 48323, - 48324, - 48325, - 48326, - 48327, - 48328, - 48329, - 48330, - 48331, - 48332, - 48333, - 48334, - 48335, - 48336, - 48337, - 48338, - 48339, - 48340, - 48341, - 48342, - 48343, - 48344, - 48345, - 48346, - 48347, - 48348, - 48349, - 48350, - 48351, - 48352, - 48353, - 48354, - 48355, - 48356, - 48357, - 48358, - 48359, - 48360, - 48361, - 48362, - 48363, - 48364, - 48365, - 48366, - 48367, - 48368, - 48369, - 48370, - 48371, - 48372, - 48373, - 48374, - 48375, - 48376, - 48377, - 48378, - 48379, - 48380, - 48381, - 48382, - 48383, - 48384, - 48385, - 48386, - 48387, - 48388, - 48389, - 48390, - 48391, - 48392, - 48393, - 48394, - 48395, - 48396, - 48397, - 48398, - 48399, - 48400, - 48401, - 48402, - 48403, - 48404, - 48405, - 48406, - 48407, - 48408, - 48409, - 48410, - 48411, - 48412, - 48413, - 48414, - 48415, - 48416, - 48417, - 48418, - 48419, - 48420, - 48421, - 48422, - 48423, - 48424, - 48425, - 48426, - 48427, - 48428, - 48429, - 48430, - 48431, - 48432, - 48433, - 48434, - 48435, - 48436, - 48437, - 48438, - 48439, - 48440, - 48441, - 48442, - 48443, - 48444, - 48445, - 48446, - 48447, - 48448, - 48449, - 48450, - 48451, - 48452, - 48453, - 48454, - 48455, - 48456, - 48457, - 48458, - 48459, - 48460, - 48461, - 48462, - 48463, - 48464, - 48465, - 48466, - 48467, - 48468, - 48469, - 48470, - 48471, - 48472, - 48473, - 48474, - 48475, - 48476, - 48477, - 48478, - 48479, - 48480, - 48481, - 48482, - 48483, - 48484, - 48485, - 48486, - 48487, - 48488, - 48489, - 48490, - 48491, - 48492, - 48493, - 48494, - 48495, - 48496, - 48497, - 48498, - 48499, - 48500, - 48501, - 48502, - 48503, - 48504, - 48505, - 48506, - 48507, - 48508, - 48509, - 48510, - 48511, - 48512, - 48513, - 48514, - 48515, - 48516, - 48517, - 48518, - 48519, - 48520, - 48521, - 48522, - 48523, - 48524, - 48525, - 48526, - 48527, - 48528, - 48529, - 48530, - 48531, - 48532, - 48533, - 48534, - 48535, - 48536, - 48537, - 48538, - 48539, - 48540, - 48541, - 48542, - 48543, - 48544, - 48545, - 48546, - 48547, - 48548, - 48549, - 48550, - 48551, - 48552, - 48553, - 48554, - 48555, - 48556, - 48557, - 48558, - 48559, - 48560, - 48561, - 48562, - 48563, - 48564, - 48565, - 48566, - 48567, - 48568, - 48569, - 48570, - 48571, - 48572, - 48573, - 48574, - 48575, - 48576, - 48577, - 48578, - 48579, - 48580, - 48581, - 48582, - 48583, - 48584, - 48585, - 48586, - 48587, - 48588, - 48589, - 48590, - 48591, - 48592, - 48593, - 48594, - 48595, - 48596, - 48597, - 48598, - 48599, - 48600, - 48601, - 48602, - 48603, - 48604, - 48605, - 48606, - 48607, - 48608, - 48609, - 48610, - 48611, - 48612, - 48613, - 48614, - 48615, - 48616, - 48617, - 48618, - 48619, - 48620, - 48621, - 48622, - 48623, - 48624, - 48625, - 48626, - 48627, - 48628, - 48629, - 48630, - 48631, - 48632, - 48633, - 48634, - 48635, - 48636, - 48637, - 48638, - 48639, - 48640, - 48641, - 48642, - 48643, - 48644, - 48645, - 48646, - 48647, - 48648, - 48649, - 48650, - 48651, - 48652, - 48653, - 48654, - 48655, - 48656, - 48657, - 48658, - 48659, - 48660, - 48661, - 48662, - 48663, - 48664, - 48665, - 48666, - 48667, - 48668, - 48669, - 48670, - 48671, - 48672, - 48673, - 48674, - 48675, - 48676, - 48677, - 48678, - 48679, - 48680, - 48681, - 48682, - 48683, - 48684, - 48685, - 48686, - 48687, - 48688, - 48689, - 48690, - 48691, - 48692, - 48693, - 48694, - 48695, - 48696, - 48697, - 48698, - 48699, - 48700, - 48701, - 48702, - 48703, - 48704, - 48705, - 48706, - 48707, - 48708, - 48709, - 48710, - 48711, - 48712, - 48713, - 48714, - 48715, - 48716, - 48717, - 48718, - 48719, - 48720, - 48721, - 48722, - 48723, - 48724, - 48725, - 48726, - 48727, - 48728, - 48729, - 48730, - 48731, - 48732, - 48733, - 48734, - 48735, - 48736, - 48737, - 48738, - 48739, - 48740, - 48741, - 48742, - 48743, - 48744, - 48745, - 48746, - 48747, - 48748, - 48749, - 48750, - 48751, - 48752, - 48753, - 48754, - 48755, - 48756, - 48757, - 48758, - 48759, - 48760, - 48761, - 48762, - 48763, - 48764, - 48765, - 48766, - 48767, - 48768, - 48769, - 48770, - 48771, - 48772, - 48773, - 48774, - 48775, - 48776, - 48777, - 48778, - 48779, - 48780, - 48781, - 48782, - 48783, - 48784, - 48785, - 48786, - 48787, - 48788, - 48789, - 48790, - 48791, - 48792, - 48793, - 48794, - 48795, - 48796, - 48797, - 48798, - 48799, - 48800, - 48801, - 48802, - 48803, - 48804, - 48805, - 48806, - 48807, - 48808, - 48809, - 48810, - 48811, - 48812, - 48813, - 48814, - 48815, - 48816, - 48817, - 48818, - 48819, - 48820, - 48821, - 48822, - 48823, - 48824, - 48825, - 48826, - 48827, - 48828, - 48829, - 48830, - 48831, - 48832, - 48833, - 48834, - 48835, - 48836, - 48837, - 48838, - 48839, - 48840, - 48841, - 48842, - 48843, - 48844, - 48845, - 48846, - 48847, - 48848, - 48849, - 48850, - 48851, - 48852, - 48853, - 48854, - 48855, - 48856, - 48857, - 48858, - 48859, - 48860, - 48861, - 48862, - 48863, - 48864, - 48865, - 48866, - 48867, - 48868, - 48869, - 48870, - 48871, - 48872, - 48873, - 48874, - 48875, - 48876, - 48877, - 48878, - 48879, - 48880, - 48881, - 48882, - 48883, - 48884, - 48885, - 48886, - 48887, - 48888, - 48889, - 48890, - 48891, - 48892, - 48893, - 48894, - 48895, - 48896, - 48897, - 48898, - 48899, - 48900, - 48901, - 48902, - 48903, - 48904, - 48905, - 48906, - 48907, - 48908, - 48909, - 48910, - 48911, - 48912, - 48913, - 48914, - 48915, - 48916, - 48917, - 48918, - 48919, - 48920, - 48921, - 48922, - 48923, - 48924, - 48925, - 48926, - 48927, - 48928, - 48929, - 48930, - 48931, - 48932, - 48933, - 48934, - 48935, - 48936, - 48937, - 48938, - 48939, - 48940, - 48941, - 48942, - 48943, - 48944, - 48945, - 48946, - 48947, - 48948, - 48949, - 48950, - 48951, - 48952, - 48953, - 48954, - 48955, - 48956, - 48957, - 48958, - 48959, - 48960, - 48961, - 48962, - 48963, - 48964, - 48965, - 48966, - 48967, - 48968, - 48969, - 48970, - 48971, - 48972, - 48973, - 48974, - 48975, - 48976, - 48977, - 48978, - 48979, - 48980, - 48981, - 48982, - 48983, - 48984, - 48985, - 48986, - 48987, - 48988, - 48989, - 48990, - 48991, - 48992, - 48993, - 48994, - 48995, - 48996, - 48997, - 48998, - 48999, - 49000, - 49001, - 49002, - 49003, - 49004, - 49005, - 49006, - 49007, - 49008, - 49009, - 49010, - 49011, - 49012, - 49013, - 49014, - 49015, - 49016, - 49017, - 49018, - 49019, - 49020, - 49021, - 49022, - 49023, - 49024, - 49025, - 49026, - 49027, - 49028, - 49029, - 49030, - 49031, - 49032, - 49033, - 49034, - 49035, - 49036, - 49037, - 49038, - 49039, - 49040, - 49041, - 49042, - 49043, - 49044, - 49045, - 49046, - 49047, - 49048, - 49049, - 49050, - 49051, - 49052, - 49053, - 49054, - 49055, - 49056, - 49057, - 49058, - 49059, - 49060, - 49061, - 49062, - 49063, - 49064, - 49065, - 49066, - 49067, - 49068, - 49069, - 49070, - 49071, - 49072, - 49073, - 49074, - 49075, - 49076, - 49077, - 49078, - 49079, - 49080, - 49081, - 49082, - 49083, - 49084, - 49085, - 49086, - 49087, - 49088, - 49089, - 49090, - 49091, - 49092, - 49093, - 49094, - 49095, - 49096, - 49097, - 49098, - 49099, - 49100, - 49101, - 49102, - 49103, - 49104, - 49105, - 49106, - 49107, - 49108, - 49109, - 49110, - 49111, - 49112, - 49113, - 49114, - 49115, - 49116, - 49117, - 49118, - 49119, - 49120, - 49121, - 49122, - 49123, - 49124, - 49125, - 49126, - 49127, - 49128, - 49129, - 49130, - 49131, - 49132, - 49133, - 49134, - 49135, - 49136, - 49137, - 49138, - 49139, - 49140, - 49141, - 49142, - 49143, - 49144, - 49145, - 49146, - 49147, - 49148, - 49149, - 49150, - 49151, - 49152, - 49153, - 49154, - 49155, - 49156, - 49157, - 49158, - 49159, - 49160, - 49161, - 49162, - 49163, - 49164, - 49165, - 49166, - 49167, - 49168, - 49169, - 49170, - 49171, - 49172, - 49173, - 49174, - 49175, - 49176, - 49177, - 49178, - 49179, - 49180, - 49181, - 49182, - 49183, - 49184, - 49185, - 49186, - 49187, - 49188, - 49189, - 49190, - 49191, - 49192, - 49193, - 49194, - 49195, - 49196, - 49197, - 49198, - 49199, - 49200, - 49201, - 49202, - 49203, - 49204, - 49205, - 49206, - 49207, - 49208, - 49209, - 49210, - 49211, - 49212, - 49213, - 49214, - 49215, - 49216, - 49217, - 49218, - 49219, - 49220, - 49221, - 49222, - 49223, - 49224, - 49225, - 49226, - 49227, - 49228, - 49229, - 49230, - 49231, - 49232, - 49233, - 49234, - 49235, - 49236, - 49237, - 49238, - 49239, - 49240, - 49241, - 49242, - 49243, - 49244, - 49245, - 49246, - 49247, - 49248, - 49249, - 49250, - 49251, - 49252, - 49253, - 49254, - 49255, - 49256, - 49257, - 49258, - 49259, - 49260, - 49261, - 49262, - 49263, - 49264, - 49265, - 49266, - 49267, - 49268, - 49269, - 49270, - 49271, - 49272, - 49273, - 49274, - 49275, - 49276, - 49277, - 49278, - 49279, - 49280, - 49281, - 49282, - 49283, - 49284, - 49285, - 49286, - 49287, - 49288, - 49289, - 49290, - 49291, - 49292, - 49293, - 49294, - 49295, - 49296, - 49297, - 49298, - 49299, - 49300, - 49301, - 49302, - 49303, - 49304, - 49305, - 49306, - 49307, - 49308, - 49309, - 49310, - 49311, - 49312, - 49313, - 49314, - 49315, - 49316, - 49317, - 49318, - 49319, - 49320, - 49321, - 49322, - 49323, - 49324, - 49325, - 49326, - 49327, - 49328, - 49329, - 49330, - 49331, - 49332, - 49333, - 49334, - 49335, - 49336, - 49337, - 49338, - 49339, - 49340, - 49341, - 49342, - 49343, - 49344, - 49345, - 49346, - 49347, - 49348, - 49349, - 49350, - 49351, - 49352, - 49353, - 49354, - 49355, - 49356, - 49357, - 49358, - 49359, - 49360, - 49361, - 49362, - 49363, - 49364, - 49365, - 49366, - 49367, - 49368, - 49369, - 49370, - 49371, - 49372, - 49373, - 49374, - 49375, - 49376, - 49377, - 49378, - 49379, - 49380, - 49381, - 49382, - 49383, - 49384, - 49385, - 49386, - 49387, - 49388, - 49389, - 49390, - 49391, - 49392, - 49393, - 49394, - 49395, - 49396, - 49397, - 49398, - 49399, - 49400, - 49401, - 49402, - 49403, - 49404, - 49405, - 49406, - 49407, - 49408, - 49409, - 49410, - 49411, - 49412, - 49413, - 49414, - 49415, - 49416, - 49417, - 49418, - 49419, - 49420, - 49421, - 49422, - 49423, - 49424, - 49425, - 49426, - 49427, - 49428, - 49429, - 49430, - 49431, - 49432, - 49433, - 49434, - 49435, - 49436, - 49437, - 49438, - 49439, - 49440, - 49441, - 49442, - 49443, - 49444, - 49445, - 49446, - 49447, - 49448, - 49449, - 49450, - 49451, - 49452, - 49453, - 49454, - 49455, - 49456, - 49457, - 49458, - 49459, - 49460, - 49461, - 49462, - 49463, - 49464, - 49465, - 49466, - 49467, - 49468, - 49469, - 49470, - 49471, - 49472, - 49473, - 49474, - 49475, - 49476, - 49477, - 49478, - 49479, - 49480, - 49481, - 49482, - 49483, - 49484, - 49485, - 49486, - 49487, - 49488, - 49489, - 49490, - 49491, - 49492, - 49493, - 49494, - 49495, - 49496, - 49497, - 49498, - 49499, - 49500, - 49501, - 49502, - 49503, - 49504, - 49505, - 49506, - 49507, - 49508, - 49509, - 49510, - 49511, - 49512, - 49513, - 49514, - 49515, - 49516, - 49517, - 49518, - 49519, - 49520, - 49521, - 49522, - 49523, - 49524, - 49525, - 49526, - 49527, - 49528, - 49529, - 49530, - 49531, - 49532, - 49533, - 49534, - 49535, - 49536, - 49537, - 49538, - 49539, - 49540, - 49541, - 49542, - 49543, - 49544, - 49545, - 49546, - 49547, - 49548, - 49549, - 49550, - 49551, - 49552, - 49553, - 49554, - 49555, - 49556, - 49557, - 49558, - 49559, - 49560, - 49561, - 49562, - 49563, - 49564, - 49565, - 49566, - 49567, - 49568, - 49569, - 49570, - 49571, - 49572, - 49573, - 49574, - 49575, - 49576, - 49577, - 49578, - 49579, - 49580, - 49581, - 49582, - 49583, - 49584, - 49585, - 49586, - 49587, - 49588, - 49589, - 49590, - 49591, - 49592, - 49593, - 49594, - 49595, - 49596, - 49597, - 49598, - 49599, - 49600, - 49601, - 49602, - 49603, - 49604, - 49605, - 49606, - 49607, - 49608, - 49609, - 49610, - 49611, - 49612, - 49613, - 49614, - 49615, - 49616, - 49617, - 49618, - 49619, - 49620, - 49621, - 49622, - 49623, - 49624, - 49625, - 49626, - 49627, - 49628, - 49629, - 49630, - 49631, - 49632, - 49633, - 49634, - 49635, - 49636, - 49637, - 49638, - 49639, - 49640, - 49641, - 49642, - 49643, - 49644, - 49645, - 49646, - 49647, - 49648, - 49649, - 49650, - 49651, - 49652, - 49653, - 49654, - 49655, - 49656, - 49657, - 49658, - 49659, - 49660, - 49661, - 49662, - 49663, - 49664, - 49665, - 49666, - 49667, - 49668, - 49669, - 49670, - 49671, - 49672, - 49673, - 49674, - 49675, - 49676, - 49677, - 49678, - 49679, - 49680, - 49681, - 49682, - 49683, - 49684, - 49685, - 49686, - 49687, - 49688, - 49689, - 49690, - 49691, - 49692, - 49693, - 49694, - 49695, - 49696, - 49697, - 49698, - 49699, - 49700, - 49701, - 49702, - 49703, - 49704, - 49705, - 49706, - 49707, - 49708, - 49709, - 49710, - 49711, - 49712, - 49713, - 49714, - 49715, - 49716, - 49717, - 49718, - 49719, - 49720, - 49721, - 49722, - 49723, - 49724, - 49725, - 49726, - 49727, - 49728, - 49729, - 49730, - 49731, - 49732, - 49733, - 49734, - 49735, - 49736, - 49737, - 49738, - 49739, - 49740, - 49741, - 49742, - 49743, - 49744, - 49745, - 49746, - 49747, - 49748, - 49749, - 49750, - 49751, - 49752, - 49753, - 49754, - 49755, - 49756, - 49757, - 49758, - 49759, - 49760, - 49761, - 49762, - 49763, - 49764, - 49765, - 49766, - 49767, - 49768, - 49769, - 49770, - 49771, - 49772, - 49773, - 49774, - 49775, - 49776, - 49777, - 49778, - 49779, - 49780, - 49781, - 49782, - 49783, - 49784, - 49785, - 49786, - 49787, - 49788, - 49789, - 49790, - 49791, - 49792, - 49793, - 49794, - 49795, - 49796, - 49797, - 49798, - 49799, - 49800, - 49801, - 49802, - 49803, - 49804, - 49805, - 49806, - 49807, - 49808, - 49809, - 49810, - 49811, - 49812, - 49813, - 49814, - 49815, - 49816, - 49817, - 49818, - 49819, - 49820, - 49821, - 49822, - 49823, - 49824, - 49825, - 49826, - 49827, - 49828, - 49829, - 49830, - 49831, - 49832, - 49833, - 49834, - 49835, - 49836, - 49837, - 49838, - 49839, - 49840, - 49841, - 49842, - 49843, - 49844, - 49845, - 49846, - 49847, - 49848, - 49849, - 49850, - 49851, - 49852, - 49853, - 49854, - 49855, - 49856, - 49857, - 49858, - 49859, - 49860, - 49861, - 49862, - 49863, - 49864, - 49865, - 49866, - 49867, - 49868, - 49869, - 49870, - 49871, - 49872, - 49873, - 49874, - 49875, - 49876, - 49877, - 49878, - 49879, - 49880, - 49881, - 49882, - 49883, - 49884, - 49885, - 49886, - 49887, - 49888, - 49889, - 49890, - 49891, - 49892, - 49893, - 49894, - 49895, - 49896, - 49897, - 49898, - 49899, - 49900, - 49901, - 49902, - 49903, - 49904, - 49905, - 49906, - 49907, - 49908, - 49909, - 49910, - 49911, - 49912, - 49913, - 49914, - 49915, - 49916, - 49917, - 49918, - 49919, - 49920, - 49921, - 49922, - 49923, - 49924, - 49925, - 49926, - 49927, - 49928, - 49929, - 49930, - 49931, - 49932, - 49933, - 49934, - 49935, - 49936, - 49937, - 49938, - 49939, - 49940, - 49941, - 49942, - 49943, - 49944, - 49945, - 49946, - 49947, - 49948, - 49949, - 49950, - 49951, - 49952, - 49953, - 49954, - 49955, - 49956, - 49957, - 49958, - 49959, - 49960, - 49961, - 49962, - 49963, - 49964, - 49965, - 49966, - 49967, - 49968, - 49969, - 49970, - 49971, - 49972, - 49973, - 49974, - 49975, - 49976, - 49977, - 49978, - 49979, - 49980, - 49981, - 49982, - 49983, - 49984, - 49985, - 49986, - 49987, - 49988, - 49989, - 49990, - 49991, - 49992, - 49993, - 49994, - 49995, - 49996, - 49997, - 49998, - 49999, - 50000, - 50001, - 50002, - 50003, - 50004, - 50005, - 50006, - 50007, - 50008, - 50009, - 50010, - 50011, - 50012, - 50013, - 50014, - 50015, - 50016, - 50017, - 50018, - 50019, - 50020, - 50021, - 50022, - 50023, - 50024, - 50025, - 50026, - 50027, - 50028, - 50029, - 50030, - 50031, - 50032, - 50033, - 50034, - 50035, - 50036, - 50037, - 50038, - 50039, - 50040, - 50041, - 50042, - 50043, - 50044, - 50045, - 50046, - 50047, - 50048, - 50049, - 50050, - 50051, - 50052, - 50053, - 50054, - 50055, - 50056, - 50057, - 50058, - 50059, - 50060, - 50061, - 50062, - 50063, - 50064, - 50065, - 50066, - 50067, - 50068, - 50069, - 50070, - 50071, - 50072, - 50073, - 50074, - 50075, - 50076, - 50077, - 50078, - 50079, - 50080, - 50081, - 50082, - 50083, - 50084, - 50085, - 50086, - 50087, - 50088, - 50089, - 50090, - 50091, - 50092, - 50093, - 50094, - 50095, - 50096, - 50097, - 50098, - 50099, - 50100, - 50101, - 50102, - 50103, - 50104, - 50105, - 50106, - 50107, - 50108, - 50109, - 50110, - 50111, - 50112, - 50113, - 50114, - 50115, - 50116, - 50117, - 50118, - 50119, - 50120, - 50121, - 50122, - 50123, - 50124, - 50125, - 50126, - 50127, - 50128, - 50129, - 50130, - 50131, - 50132, - 50133, - 50134, - 50135, - 50136, - 50137, - 50138, - 50139, - 50140, - 50141, - 50142, - 50143, - 50144, - 50145, - 50146, - 50147, - 50148, - 50149, - 50150, - 50151, - 50152, - 50153, - 50154, - 50155, - 50156, - 50157, - 50158, - 50159, - 50160, - 50161, - 50162, - 50163, - 50164, - 50165, - 50166, - 50167, - 50168, - 50169, - 50170, - 50171, - 50172, - 50173, - 50174, - 50175, - 50176, - 50177, - 50178, - 50179, - 50180, - 50181, - 50182, - 50183, - 50184, - 50185, - 50186, - 50187, - 50188, - 50189, - 50190, - 50191, - 50192, - 50193, - 50194, - 50195, - 50196, - 50197, - 50198, - 50199, - 50200, - 50201, - 50202, - 50203, - 50204, - 50205, - 50206, - 50207, - 50208, - 50209, - 50210, - 50211, - 50212, - 50213, - 50214, - 50215, - 50216, - 50217, - 50218, - 50219, - 50220, - 50221, - 50222, - 50223, - 50224, - 50225, - 50226, - 50227, - 50228, - 50229, - 50230, - 50231, - 50232, - 50233, - 50234, - 50235, - 50236, - 50237, - 50238, - 50239, - 50240, - 50241, - 50242, - 50243, - 50244, - 50245, - 50246, - 50247, - 50248, - 50249, - 50250, - 50251, - 50252, - 50253, - 50254, - 50255, - 50256, - 50257, - 50258, - 50259, - 50260, - 50261, - 50262, - 50263, - 50264, - 50265, - 50266, - 50267, - 50268, - 50269, - 50270, - 50271, - 50272, - 50273, - 50274, - 50275, - 50276, - 50277, - 50278, - 50279, - 50280, - 50281, - 50282, - 50283, - 50284, - 50285, - 50286, - 50287, - 50288, - 50289, - 50290, - 50291, - 50292, - 50293, - 50294, - 50295, - 50296, - 50297, - 50298, - 50299, - 50300, - 50301, - 50302, - 50303, - 50304, - 50305, - 50306, - 50307, - 50308, - 50309, - 50310, - 50311, - 50312, - 50313, - 50314, - 50315, - 50316, - 50317, - 50318, - 50319, - 50320, - 50321, - 50322, - 50323, - 50324, - 50325, - 50326, - 50327, - 50328, - 50329, - 50330, - 50331, - 50332, - 50333, - 50334, - 50335, - 50336, - 50337, - 50338, - 50339, - 50340, - 50341, - 50342, - 50343, - 50344, - 50345, - 50346, - 50347, - 50348, - 50349, - 50350, - 50351, - 50352, - 50353, - 50354, - 50355, - 50356, - 50357, - 50358, - 50359, - 50360, - 50361, - 50362, - 50363, - 50364, - 50365, - 50366, - 50367, - 50368, - 50369, - 50370, - 50371, - 50372, - 50373, - 50374, - 50375, - 50376, - 50377, - 50378, - 50379, - 50380, - 50381, - 50382, - 50383, - 50384, - 50385, - 50386, - 50387, - 50388, - 50389, - 50390, - 50391, - 50392, - 50393, - 50394, - 50395, - 50396, - 50397, - 50398, - 50399, - 50400, - 50401, - 50402, - 50403, - 50404, - 50405, - 50406, - 50407, - 50408, - 50409, - 50410, - 50411, - 50412, - 50413, - 50414, - 50415, - 50416, - 50417, - 50418, - 50419, - 50420, - 50421, - 50422, - 50423, - 50424, - 50425, - 50426, - 50427, - 50428, - 50429, - 50430, - 50431, - 50432, - 50433, - 50434, - 50435, - 50436, - 50437, - 50438, - 50439, - 50440, - 50441, - 50442, - 50443, - 50444, - 50445, - 50446, - 50447, - 50448, - 50449, - 50450, - 50451, - 50452, - 50453, - 50454, - 50455, - 50456, - 50457, - 50458, - 50459, - 50460, - 50461, - 50462, - 50463, - 50464, - 50465, - 50466, - 50467, - 50468, - 50469, - 50470, - 50471, - 50472, - 50473, - 50474, - 50475, - 50476, - 50477, - 50478, - 50479, - 50480, - 50481, - 50482, - 50483, - 50484, - 50485, - 50486, - 50487, - 50488, - 50489, - 50490, - 50491, - 50492, - 50493, - 50494, - 50495, - 50496, - 50497, - 50498, - 50499, - 50500, - 50501, - 50502, - 50503, - 50504, - 50505, - 50506, - 50507, - 50508, - 50509, - 50510, - 50511, - 50512, - 50513, - 50514, - 50515, - 50516, - 50517, - 50518, - 50519, - 50520, - 50521, - 50522, - 50523, - 50524, - 50525, - 50526, - 50527, - 50528, - 50529, - 50530, - 50531, - 50532, - 50533, - 50534, - 50535, - 50536, - 50537, - 50538, - 50539, - 50540, - 50541, - 50542, - 50543, - 50544, - 50545, - 50546, - 50547, - 50548, - 50549, - 50550, - 50551, - 50552, - 50553, - 50554, - 50555, - 50556, - 50557, - 50558, - 50559, - 50560, - 50561, - 50562, - 50563, - 50564, - 50565, - 50566, - 50567, - 50568, - 50569, - 50570, - 50571, - 50572, - 50573, - 50574, - 50575, - 50576, - 50577, - 50578, - 50579, - 50580, - 50581, - 50582, - 50583, - 50584, - 50585, - 50586, - 50587, - 50588, - 50589, - 50590, - 50591, - 50592, - 50593, - 50594, - 50595, - 50596, - 50597, - 50598, - 50599, - 50600, - 50601, - 50602, - 50603, - 50604, - 50605, - 50606, - 50607, - 50608, - 50609, - 50610, - 50611, - 50612, - 50613, - 50614, - 50615, - 50616, - 50617, - 50618, - 50619, - 50620, - 50621, - 50622, - 50623, - 50624, - 50625, - 50626, - 50627, - 50628, - 50629, - 50630, - 50631, - 50632, - 50633, - 50634, - 50635, - 50636, - 50637, - 50638, - 50639, - 50640, - 50641, - 50642, - 50643, - 50644, - 50645, - 50646, - 50647, - 50648, - 50649, - 50650, - 50651, - 50652, - 50653, - 50654, - 50655, - 50656, - 50657, - 50658, - 50659, - 50660, - 50661, - 50662, - 50663, - 50664, - 50665, - 50666, - 50667, - 50668, - 50669, - 50670, - 50671, - 50672, - 50673, - 50674, - 50675, - 50676, - 50677, - 50678, - 50679, - 50680, - 50681, - 50682, - 50683, - 50684, - 50685, - 50686, - 50687, - 50688, - 50689, - 50690, - 50691, - 50692, - 50693, - 50694, - 50695, - 50696, - 50697, - 50698, - 50699, - 50700, - 50701, - 50702, - 50703, - 50704, - 50705, - 50706, - 50707, - 50708, - 50709, - 50710, - 50711, - 50712, - 50713, - 50714, - 50715, - 50716, - 50717, - 50718, - 50719, - 50720, - 50721, - 50722, - 50723, - 50724, - 50725, - 50726, - 50727, - 50728, - 50729, - 50730, - 50731, - 50732, - 50733, - 50734, - 50735, - 50736, - 50737, - 50738, - 50739, - 50740, - 50741, - 50742, - 50743, - 50744, - 50745, - 50746, - 50747, - 50748, - 50749, - 50750, - 50751, - 50752, - 50753, - 50754, - 50755, - 50756, - 50757, - 50758, - 50759, - 50760, - 50761, - 50762, - 50763, - 50764, - 50765, - 50766, - 50767, - 50768, - 50769, - 50770, - 50771, - 50772, - 50773, - 50774, - 50775, - 50776, - 50777, - 50778, - 50779, - 50780, - 50781, - 50782, - 50783, - 50784, - 50785, - 50786, - 50787, - 50788, - 50789, - 50790, - 50791, - 50792, - 50793, - 50794, - 50795, - 50796, - 50797, - 50798, - 50799, - 50800, - 50801, - 50802, - 50803, - 50804, - 50805, - 50806, - 50807, - 50808, - 50809, - 50810, - 50811, - 50812, - 50813, - 50814, - 50815, - 50816, - 50817, - 50818, - 50819, - 50820, - 50821, - 50822, - 50823, - 50824, - 50825, - 50826, - 50827, - 50828, - 50829, - 50830, - 50831, - 50832, - 50833, - 50834, - 50835, - 50836, - 50837, - 50838, - 50839, - 50840, - 50841, - 50842, - 50843, - 50844, - 50845, - 50846, - 50847, - 50848, - 50849, - 50850, - 50851, - 50852, - 50853, - 50854, - 50855, - 50856, - 50857, - 50858, - 50859, - 50860, - 50861, - 50862, - 50863, - 50864, - 50865, - 50866, - 50867, - 50868, - 50869, - 50870, - 50871, - 50872, - 50873, - 50874, - 50875, - 50876, - 50877, - 50878, - 50879, - 50880, - 50881, - 50882, - 50883, - 50884, - 50885, - 50886, - 50887, - 50888, - 50889, - 50890, - 50891, - 50892, - 50893, - 50894, - 50895, - 50896, - 50897, - 50898, - 50899, - 50900, - 50901, - 50902, - 50903, - 50904, - 50905, - 50906, - 50907, - 50908, - 50909, - 50910, - 50911, - 50912, - 50913, - 50914, - 50915, - 50916, - 50917, - 50918, - 50919, - 50920, - 50921, - 50922, - 50923, - 50924, - 50925, - 50926, - 50927, - 50928, - 50929, - 50930, - 50931, - 50932, - 50933, - 50934, - 50935, - 50936, - 50937, - 50938, - 50939, - 50940, - 50941, - 50942, - 50943, - 50944, - 50945, - 50946, - 50947, - 50948, - 50949, - 50950, - 50951, - 50952, - 50953, - 50954, - 50955, - 50956, - 50957, - 50958, - 50959, - 50960, - 50961, - 50962, - 50963, - 50964, - 50965, - 50966, - 50967, - 50968, - 50969, - 50970, - 50971, - 50972, - 50973, - 50974, - 50975, - 50976, - 50977, - 50978, - 50979, - 50980, - 50981, - 50982, - 50983, - 50984, - 50985, - 50986, - 50987, - 50988, - 50989, - 50990, - 50991, - 50992, - 50993, - 50994, - 50995, - 50996, - 50997, - 50998, - 50999, - 51000, - 51001, - 51002, - 51003, - 51004, - 51005, - 51006, - 51007, - 51008, - 51009, - 51010, - 51011, - 51012, - 51013, - 51014, - 51015, - 51016, - 51017, - 51018, - 51019, - 51020, - 51021, - 51022, - 51023, - 51024, - 51025, - 51026, - 51027, - 51028, - 51029, - 51030, - 51031, - 51032, - 51033, - 51034, - 51035, - 51036, - 51037, - 51038, - 51039, - 51040, - 51041, - 51042, - 51043, - 51044, - 51045, - 51046, - 51047, - 51048, - 51049, - 51050, - 51051, - 51052, - 51053, - 51054, - 51055, - 51056, - 51057, - 51058, - 51059, - 51060, - 51061, - 51062, - 51063, - 51064, - 51065, - 51066, - 51067, - 51068, - 51069, - 51070, - 51071, - 51072, - 51073, - 51074, - 51075, - 51076, - 51077, - 51078, - 51079, - 51080, - 51081, - 51082, - 51083, - 51084, - 51085, - 51086, - 51087, - 51088, - 51089, - 51090, - 51091, - 51092, - 51093, - 51094, - 51095, - 51096, - 51097, - 51098, - 51099, - 51100, - 51101, - 51102, - 51103, - 51104, - 51105, - 51106, - 51107, - 51108, - 51109, - 51110, - 51111, - 51112, - 51113, - 51114, - 51115, - 51116, - 51117, - 51118, - 51119, - 51120, - 51121, - 51122, - 51123, - 51124, - 51125, - 51126, - 51127, - 51128, - 51129, - 51130, - 51131, - 51132, - 51133, - 51134, - 51135, - 51136, - 51137, - 51138, - 51139, - 51140, - 51141, - 51142, - 51143, - 51144, - 51145, - 51146, - 51147, - 51148, - 51149, - 51150, - 51151, - 51152, - 51153, - 51154, - 51155, - 51156, - 51157, - 51158, - 51159, - 51160, - 51161, - 51162, - 51163, - 51164, - 51165, - 51166, - 51167, - 51168, - 51169, - 51170, - 51171, - 51172, - 51173, - 51174, - 51175, - 51176, - 51177, - 51178, - 51179, - 51180, - 51181, - 51182, - 51183, - 51184, - 51185, - 51186, - 51187, - 51188, - 51189, - 51190, - 51191, - 51192, - 51193, - 51194, - 51195, - 51196, - 51197, - 51198, - 51199, - 51200, - 51201, - 51202, - 51203, - 51204, - 51205, - 51206, - 51207, - 51208, - 51209, - 51210, - 51211, - 51212, - 51213, - 51214, - 51215, - 51216, - 51217, - 51218, - 51219, - 51220, - 51221, - 51222, - 51223, - 51224, - 51225, - 51226, - 51227, - 51228, - 51229, - 51230, - 51231, - 51232, - 51233, - 51234, - 51235, - 51236, - 51237, - 51238, - 51239, - 51240, - 51241, - 51242, - 51243, - 51244, - 51245, - 51246, - 51247, - 51248, - 51249, - 51250, - 51251, - 51252, - 51253, - 51254, - 51255, - 51256, - 51257, - 51258, - 51259, - 51260, - 51261, - 51262, - 51263, - 51264, - 51265, - 51266, - 51267, - 51268, - 51269, - 51270, - 51271, - 51272, - 51273, - 51274, - 51275, - 51276, - 51277, - 51278, - 51279, - 51280, - 51281, - 51282, - 51283, - 51284, - 51285, - 51286, - 51287, - 51288, - 51289, - 51290, - 51291, - 51292, - 51293, - 51294, - 51295, - 51296, - 51297, - 51298, - 51299, - 51300, - 51301, - 51302, - 51303, - 51304, - 51305, - 51306, - 51307, - 51308, - 51309, - 51310, - 51311, - 51312, - 51313, - 51314, - 51315, - 51316, - 51317, - 51318, - 51319, - 51320, - 51321, - 51322, - 51323, - 51324, - 51325, - 51326, - 51327, - 51328, - 51329, - 51330, - 51331, - 51332, - 51333, - 51334, - 51335, - 51336, - 51337, - 51338, - 51339, - 51340, - 51341, - 51342, - 51343, - 51344, - 51345, - 51346, - 51347, - 51348, - 51349, - 51350, - 51351, - 51352, - 51353, - 51354, - 51355, - 51356, - 51357, - 51358, - 51359, - 51360, - 51361, - 51362, - 51363, - 51364, - 51365, - 51366, - 51367, - 51368, - 51369, - 51370, - 51371, - 51372, - 51373, - 51374, - 51375, - 51376, - 51377, - 51378, - 51379, - 51380, - 51381, - 51382, - 51383, - 51384, - 51385, - 51386, - 51387, - 51388, - 51389, - 51390, - 51391, - 51392, - 51393, - 51394, - 51395, - 51396, - 51397, - 51398, - 51399, - 51400, - 51401, - 51402, - 51403, - 51404, - 51405, - 51406, - 51407, - 51408, - 51409, - 51410, - 51411, - 51412, - 51413, - 51414, - 51415, - 51416, - 51417, - 51418, - 51419, - 51420, - 51421, - 51422, - 51423, - 51424, - 51425, - 51426, - 51427, - 51428, - 51429, - 51430, - 51431, - 51432, - 51433, - 51434, - 51435, - 51436, - 51437, - 51438, - 51439, - 51440, - 51441, - 51442, - 51443, - 51444, - 51445, - 51446, - 51447, - 51448, - 51449, - 51450, - 51451, - 51452, - 51453, - 51454, - 51455, - 51456, - 51457, - 51458, - 51459, - 51460, - 51461, - 51462, - 51463, - 51464, - 51465, - 51466, - 51467, - 51468, - 51469, - 51470, - 51471, - 51472, - 51473, - 51474, - 51475, - 51476, - 51477, - 51478, - 51479, - 51480, - 51481, - 51482, - 51483, - 51484, - 51485, - 51486, - 51487, - 51488, - 51489, - 51490, - 51491, - 51492, - 51493, - 51494, - 51495, - 51496, - 51497, - 51498, - 51499, - 51500, - 51501, - 51502, - 51503, - 51504, - 51505, - 51506, - 51507, - 51508, - 51509, - 51510, - 51511, - 51512, - 51513, - 51514, - 51515, - 51516, - 51517, - 51518, - 51519, - 51520, - 51521, - 51522, - 51523, - 51524, - 51525, - 51526, - 51527, - 51528, - 51529, - 51530, - 51531, - 51532, - 51533, - 51534, - 51535, - 51536, - 51537, - 51538, - 51539, - 51540, - 51541, - 51542, - 51543, - 51544, - 51545, - 51546, - 51547, - 51548, - 51549, - 51550, - 51551, - 51552, - 51553, - 51554, - 51555, - 51556, - 51557, - 51558, - 51559, - 51560, - 51561, - 51562, - 51563, - 51564, - 51565, - 51566, - 51567, - 51568, - 51569, - 51570, - 51571, - 51572, - 51573, - 51574, - 51575, - 51576, - 51577, - 51578, - 51579, - 51580, - 51581, - 51582, - 51583, - 51584, - 51585, - 51586, - 51587, - 51588, - 51589, - 51590, - 51591, - 51592, - 51593, - 51594, - 51595, - 51596, - 51597, - 51598, - 51599, - 51600, - 51601, - 51602, - 51603, - 51604, - 51605, - 51606, - 51607, - 51608, - 51609, - 51610, - 51611, - 51612, - 51613, - 51614, - 51615, - 51616, - 51617, - 51618, - 51619, - 51620, - 51621, - 51622, - 51623, - 51624, - 51625, - 51626, - 51627, - 51628, - 51629, - 51630, - 51631, - 51632, - 51633, - 51634, - 51635, - 51636, - 51637, - 51638, - 51639, - 51640, - 51641, - 51642, - 51643, - 51644, - 51645, - 51646, - 51647, - 51648, - 51649, - 51650, - 51651, - 51652, - 51653, - 51654, - 51655, - 51656, - 51657, - 51658, - 51659, - 51660, - 51661, - 51662, - 51663, - 51664, - 51665, - 51666, - 51667, - 51668, - 51669, - 51670, - 51671, - 51672, - 51673, - 51674, - 51675, - 51676, - 51677, - 51678, - 51679, - 51680, - 51681, - 51682, - 51683, - 51684, - 51685, - 51686, - 51687, - 51688, - 51689, - 51690, - 51691, - 51692, - 51693, - 51694, - 51695, - 51696, - 51697, - 51698, - 51699, - 51700, - 51701, - 51702, - 51703, - 51704, - 51705, - 51706, - 51707, - 51708, - 51709, - 51710, - 51711, - 51712, - 51713, - 51714, - 51715, - 51716, - 51717, - 51718, - 51719, - 51720, - 51721, - 51722, - 51723, - 51724, - 51725, - 51726, - 51727, - 51728, - 51729, - 51730, - 51731, - 51732, - 51733, - 51734, - 51735, - 51736, - 51737, - 51738, - 51739, - 51740, - 51741, - 51742, - 51743, - 51744, - 51745, - 51746, - 51747, - 51748, - 51749, - 51750, - 51751, - 51752, - 51753, - 51754, - 51755, - 51756, - 51757, - 51758, - 51759, - 51760, - 51761, - 51762, - 51763, - 51764, - 51765, - 51766, - 51767, - 51768, - 51769, - 51770, - 51771, - 51772, - 51773, - 51774, - 51775, - 51776, - 51777, - 51778, - 51779, - 51780, - 51781, - 51782, - 51783, - 51784, - 51785, - 51786, - 51787, - 51788, - 51789, - 51790, - 51791, - 51792, - 51793, - 51794, - 51795, - 51796, - 51797, - 51798, - 51799, - 51800, - 51801, - 51802, - 51803, - 51804, - 51805, - 51806, - 51807, - 51808, - 51809, - 51810, - 51811, - 51812, - 51813, - 51814, - 51815, - 51816, - 51817, - 51818, - 51819, - 51820, - 51821, - 51822, - 51823, - 51824, - 51825, - 51826, - 51827, - 51828, - 51829, - 51830, - 51831, - 51832, - 51833, - 51834, - 51835, - 51836, - 51837, - 51838, - 51839, - 51840, - 51841, - 51842, - 51843, - 51844, - 51845, - 51846, - 51847, - 51848, - 51849, - 51850, - 51851, - 51852, - 51853, - 51854, - 51855, - 51856, - 51857, - 51858, - 51859, - 51860, - 51861, - 51862, - 51863, - 51864, - 51865, - 51866, - 51867, - 51868, - 51869, - 51870, - 51871, - 51872, - 51873, - 51874, - 51875, - 51876, - 51877, - 51878, - 51879, - 51880, - 51881, - 51882, - 51883, - 51884, - 51885, - 51886, - 51887, - 51888, - 51889, - 51890, - 51891, - 51892, - 51893, - 51894, - 51895, - 51896, - 51897, - 51898, - 51899, - 51900, - 51901, - 51902, - 51903, - 51904, - 51905, - 51906, - 51907, - 51908, - 51909, - 51910, - 51911, - 51912, - 51913, - 51914, - 51915, - 51916, - 51917, - 51918, - 51919, - 51920, - 51921, - 51922, - 51923, - 51924, - 51925, - 51926, - 51927, - 51928, - 51929, - 51930, - 51931, - 51932, - 51933, - 51934, - 51935, - 51936, - 51937, - 51938, - 51939, - 51940, - 51941, - 51942, - 51943, - 51944, - 51945, - 51946, - 51947, - 51948, - 51949, - 51950, - 51951, - 51952, - 51953, - 51954, - 51955, - 51956, - 51957, - 51958, - 51959, - 51960, - 51961, - 51962, - 51963, - 51964, - 51965, - 51966, - 51967, - 51968, - 51969, - 51970, - 51971, - 51972, - 51973, - 51974, - 51975, - 51976, - 51977, - 51978, - 51979, - 51980, - 51981, - 51982, - 51983, - 51984, - 51985, - 51986, - 51987, - 51988, - 51989, - 51990, - 51991, - 51992, - 51993, - 51994, - 51995, - 51996, - 51997, - 51998, - 51999, - 52000, - 52001, - 52002, - 52003, - 52004, - 52005, - 52006, - 52007, - 52008, - 52009, - 52010, - 52011, - 52012, - 52013, - 52014, - 52015, - 52016, - 52017, - 52018, - 52019, - 52020, - 52021, - 52022, - 52023, - 52024, - 52025, - 52026, - 52027, - 52028, - 52029, - 52030, - 52031, - 52032, - 52033, - 52034, - 52035, - 52036, - 52037, - 52038, - 52039, - 52040, - 52041, - 52042, - 52043, - 52044, - 52045, - 52046, - 52047, - 52048, - 52049, - 52050, - 52051, - 52052, - 52053, - 52054, - 52055, - 52056, - 52057, - 52058, - 52059, - 52060, - 52061, - 52062, - 52063, - 52064, - 52065, - 52066, - 52067, - 52068, - 52069, - 52070, - 52071, - 52072, - 52073, - 52074, - 52075, - 52076, - 52077, - 52078, - 52079, - 52080, - 52081, - 52082, - 52083, - 52084, - 52085, - 52086, - 52087, - 52088, - 52089, - 52090, - 52091, - 52092, - 52093, - 52094, - 52095, - 52096, - 52097, - 52098, - 52099, - 52100, - 52101, - 52102, - 52103, - 52104, - 52105, - 52106, - 52107, - 52108, - 52109, - 52110, - 52111, - 52112, - 52113, - 52114, - 52115, - 52116, - 52117, - 52118, - 52119, - 52120, - 52121, - 52122, - 52123, - 52124, - 52125, - 52126, - 52127, - 52128, - 52129, - 52130, - 52131, - 52132, - 52133, - 52134, - 52135, - 52136, - 52137, - 52138, - 52139, - 52140, - 52141, - 52142, - 52143, - 52144, - 52145, - 52146, - 52147, - 52148, - 52149, - 52150, - 52151, - 52152, - 52153, - 52154, - 52155, - 52156, - 52157, - 52158, - 52159, - 52160, - 52161, - 52162, - 52163, - 52164, - 52165, - 52166, - 52167, - 52168, - 52169, - 52170, - 52171, - 52172, - 52173, - 52174, - 52175, - 52176, - 52177, - 52178, - 52179, - 52180, - 52181, - 52182, - 52183, - 52184, - 52185, - 52186, - 52187, - 52188, - 52189, - 52190, - 52191, - 52192, - 52193, - 52194, - 52195, - 52196, - 52197, - 52198, - 52199, - 52200, - 52201, - 52202, - 52203, - 52204, - 52205, - 52206, - 52207, - 52208, - 52209, - 52210, - 52211, - 52212, - 52213, - 52214, - 52215, - 52216, - 52217, - 52218, - 52219, - 52220, - 52221, - 52222, - 52223, - 52224, - 52225, - 52226, - 52227, - 52228, - 52229, - 52230, - 52231, - 52232, - 52233, - 52234, - 52235, - 52236, - 52237, - 52238, - 52239, - 52240, - 52241, - 52242, - 52243, - 52244, - 52245, - 52246, - 52247, - 52248, - 52249, - 52250, - 52251, - 52252, - 52253, - 52254, - 52255, - 52256, - 52257, - 52258, - 52259, - 52260, - 52261, - 52262, - 52263, - 52264, - 52265, - 52266, - 52267, - 52268, - 52269, - 52270, - 52271, - 52272, - 52273, - 52274, - 52275, - 52276, - 52277, - 52278, - 52279, - 52280, - 52281, - 52282, - 52283, - 52284, - 52285, - 52286, - 52287, - 52288, - 52289, - 52290, - 52291, - 52292, - 52293, - 52294, - 52295, - 52296, - 52297, - 52298, - 52299, - 52300, - 52301, - 52302, - 52303, - 52304, - 52305, - 52306, - 52307, - 52308, - 52309, - 52310, - 52311, - 52312, - 52313, - 52314, - 52315, - 52316, - 52317, - 52318, - 52319, - 52320, - 52321, - 52322, - 52323, - 52324, - 52325, - 52326, - 52327, - 52328, - 52329, - 52330, - 52331, - 52332, - 52333, - 52334, - 52335, - 52336, - 52337, - 52338, - 52339, - 52340, - 52341, - 52342, - 52343, - 52344, - 52345, - 52346, - 52347, - 52348, - 52349, - 52350, - 52351, - 52352, - 52353, - 52354, - 52355, - 52356, - 52357, - 52358, - 52359, - 52360, - 52361, - 52362, - 52363, - 52364, - 52365, - 52366, - 52367, - 52368, - 52369, - 52370, - 52371, - 52372, - 52373, - 52374, - 52375, - 52376, - 52377, - 52378, - 52379, - 52380, - 52381, - 52382, - 52383, - 52384, - 52385, - 52386, - 52387, - 52388, - 52389, - 52390, - 52391, - 52392, - 52393, - 52394, - 52395, - 52396, - 52397, - 52398, - 52399, - 52400, - 52401, - 52402, - 52403, - 52404, - 52405, - 52406, - 52407, - 52408, - 52409, - 52410, - 52411, - 52412, - 52413, - 52414, - 52415, - 52416, - 52417, - 52418, - 52419, - 52420, - 52421, - 52422, - 52423, - 52424, - 52425, - 52426, - 52427, - 52428, - 52429, - 52430, - 52431, - 52432, - 52433, - 52434, - 52435, - 52436, - 52437, - 52438, - 52439, - 52440, - 52441, - 52442, - 52443, - 52444, - 52445, - 52446, - 52447, - 52448, - 52449, - 52450, - 52451, - 52452, - 52453, - 52454, - 52455, - 52456, - 52457, - 52458, - 52459, - 52460, - 52461, - 52462, - 52463, - 52464, - 52465, - 52466, - 52467, - 52468, - 52469, - 52470, - 52471, - 52472, - 52473, - 52474, - 52475, - 52476, - 52477, - 52478, - 52479, - 52480, - 52481, - 52482, - 52483, - 52484, - 52485, - 52486, - 52487, - 52488, - 52489, - 52490, - 52491, - 52492, - 52493, - 52494, - 52495, - 52496, - 52497, - 52498, - 52499, - 52500, - 52501, - 52502, - 52503, - 52504, - 52505, - 52506, - 52507, - 52508, - 52509, - 52510, - 52511, - 52512, - 52513, - 52514, - 52515, - 52516, - 52517, - 52518, - 52519, - 52520, - 52521, - 52522, - 52523, - 52524, - 52525, - 52526, - 52527, - 52528, - 52529, - 52530, - 52531, - 52532, - 52533, - 52534, - 52535, - 52536, - 52537, - 52538, - 52539, - 52540, - 52541, - 52542, - 52543, - 52544, - 52545, - 52546, - 52547, - 52548, - 52549, - 52550, - 52551, - 52552, - 52553, - 52554, - 52555, - 52556, - 52557, - 52558, - 52559, - 52560, - 52561, - 52562, - 52563, - 52564, - 52565, - 52566, - 52567, - 52568, - 52569, - 52570, - 52571, - 52572, - 52573, - 52574, - 52575, - 52576, - 52577, - 52578, - 52579, - 52580, - 52581, - 52582, - 52583, - 52584, - 52585, - 52586, - 52587, - 52588, - 52589, - 52590, - 52591, - 52592, - 52593, - 52594, - 52595, - 52596, - 52597, - 52598, - 52599, - 52600, - 52601, - 52602, - 52603, - 52604, - 52605, - 52606, - 52607, - 52608, - 52609, - 52610, - 52611, - 52612, - 52613, - 52614, - 52615, - 52616, - 52617, - 52618, - 52619, - 52620, - 52621, - 52622, - 52623, - 52624, - 52625, - 52626, - 52627, - 52628, - 52629, - 52630, - 52631, - 52632, - 52633, - 52634, - 52635, - 52636, - 52637, - 52638, - 52639, - 52640, - 52641, - 52642, - 52643, - 52644, - 52645, - 52646, - 52647, - 52648, - 52649, - 52650, - 52651, - 52652, - 52653, - 52654, - 52655, - 52656, - 52657, - 52658, - 52659, - 52660, - 52661, - 52662, - 52663, - 52664, - 52665, - 52666, - 52667, - 52668, - 52669, - 52670, - 52671, - 52672, - 52673, - 52674, - 52675, - 52676, - 52677, - 52678, - 52679, - 52680, - 52681, - 52682, - 52683, - 52684, - 52685, - 52686, - 52687, - 52688, - 52689, - 52690, - 52691, - 52692, - 52693, - 52694, - 52695, - 52696, - 52697, - 52698, - 52699, - 52700, - 52701, - 52702, - 52703, - 52704, - 52705, - 52706, - 52707, - 52708, - 52709, - 52710, - 52711, - 52712, - 52713, - 52714, - 52715, - 52716, - 52717, - 52718, - 52719, - 52720, - 52721, - 52722, - 52723, - 52724, - 52725, - 52726, - 52727, - 52728, - 52729, - 52730, - 52731, - 52732, - 52733, - 52734, - 52735, - 52736, - 52737, - 52738, - 52739, - 52740, - 52741, - 52742, - 52743, - 52744, - 52745, - 52746, - 52747, - 52748, - 52749, - 52750, - 52751, - 52752, - 52753, - 52754, - 52755, - 52756, - 52757, - 52758, - 52759, - 52760, - 52761, - 52762, - 52763, - 52764, - 52765, - 52766, - 52767, - 52768, - 52769, - 52770, - 52771, - 52772, - 52773, - 52774, - 52775, - 52776, - 52777, - 52778, - 52779, - 52780, - 52781, - 52782, - 52783, - 52784, - 52785, - 52786, - 52787, - 52788, - 52789, - 52790, - 52791, - 52792, - 52793, - 52794, - 52795, - 52796, - 52797, - 52798, - 52799, - 52800, - 52801, - 52802, - 52803, - 52804, - 52805, - 52806, - 52807, - 52808, - 52809, - 52810, - 52811, - 52812, - 52813, - 52814, - 52815, - 52816, - 52817, - 52818, - 52819, - 52820, - 52821, - 52822, - 52823, - 52824, - 52825, - 52826, - 52827, - 52828, - 52829, - 52830, - 52831, - 52832, - 52833, - 52834, - 52835, - 52836, - 52837, - 52838, - 52839, - 52840, - 52841, - 52842, - 52843, - 52844, - 52845, - 52846, - 52847, - 52848, - 52849, - 52850, - 52851, - 52852, - 52853, - 52854, - 52855, - 52856, - 52857, - 52858, - 52859, - 52860, - 52861, - 52862, - 52863, - 52864, - 52865, - 52866, - 52867, - 52868, - 52869, - 52870, - 52871, - 52872, - 52873, - 52874, - 52875, - 52876, - 52877, - 52878, - 52879, - 52880, - 52881, - 52882, - 52883, - 52884, - 52885, - 52886, - 52887, - 52888, - 52889, - 52890, - 52891, - 52892, - 52893, - 52894, - 52895, - 52896, - 52897, - 52898, - 52899, - 52900, - 52901, - 52902, - 52903, - 52904, - 52905, - 52906, - 52907, - 52908, - 52909, - 52910, - 52911, - 52912, - 52913, - 52914, - 52915, - 52916, - 52917, - 52918, - 52919, - 52920, - 52921, - 52922, - 52923, - 52924, - 52925, - 52926, - 52927, - 52928, - 52929, - 52930, - 52931, - 52932, - 52933, - 52934, - 52935, - 52936, - 52937, - 52938, - 52939, - 52940, - 52941, - 52942, - 52943, - 52944, - 52945, - 52946, - 52947, - 52948, - 52949, - 52950, - 52951, - 52952, - 52953, - 52954, - 52955, - 52956, - 52957, - 52958, - 52959, - 52960, - 52961, - 52962, - 52963, - 52964, - 52965, - 52966, - 52967, - 52968, - 52969, - 52970, - 52971, - 52972, - 52973, - 52974, - 52975, - 52976, - 52977, - 52978, - 52979, - 52980, - 52981, - 52982, - 52983, - 52984, - 52985, - 52986, - 52987, - 52988, - 52989, - 52990, - 52991, - 52992, - 52993, - 52994, - 52995, - 52996, - 52997, - 52998, - 52999, - 53000, - 53001, - 53002, - 53003, - 53004, - 53005, - 53006, - 53007, - 53008, - 53009, - 53010, - 53011, - 53012, - 53013, - 53014, - 53015, - 53016, - 53017, - 53018, - 53019, - 53020, - 53021, - 53022, - 53023, - 53024, - 53025, - 53026, - 53027, - 53028, - 53029, - 53030, - 53031, - 53032, - 53033, - 53034, - 53035, - 53036, - 53037, - 53038, - 53039, - 53040, - 53041, - 53042, - 53043, - 53044, - 53045, - 53046, - 53047, - 53048, - 53049, - 53050, - 53051, - 53052, - 53053, - 53054, - 53055, - 53056, - 53057, - 53058, - 53059, - 53060, - 53061, - 53062, - 53063, - 53064, - 53065, - 53066, - 53067, - 53068, - 53069, - 53070, - 53071, - 53072, - 53073, - 53074, - 53075, - 53076, - 53077, - 53078, - 53079, - 53080, - 53081, - 53082, - 53083, - 53084, - 53085, - 53086, - 53087, - 53088, - 53089, - 53090, - 53091, - 53092, - 53093, - 53094, - 53095, - 53096, - 53097, - 53098, - 53099, - 53100, - 53101, - 53102, - 53103, - 53104, - 53105, - 53106, - 53107, - 53108, - 53109, - 53110, - 53111, - 53112, - 53113, - 53114, - 53115, - 53116, - 53117, - 53118, - 53119, - 53120, - 53121, - 53122, - 53123, - 53124, - 53125, - 53126, - 53127, - 53128, - 53129, - 53130, - 53131, - 53132, - 53133, - 53134, - 53135, - 53136, - 53137, - 53138, - 53139, - 53140, - 53141, - 53142, - 53143, - 53144, - 53145, - 53146, - 53147, - 53148, - 53149, - 53150, - 53151, - 53152, - 53153, - 53154, - 53155, - 53156, - 53157, - 53158, - 53159, - 53160, - 53161, - 53162, - 53163, - 53164, - 53165, - 53166, - 53167, - 53168, - 53169, - 53170, - 53171, - 53172, - 53173, - 53174, - 53175, - 53176, - 53177, - 53178, - 53179, - 53180, - 53181, - 53182, - 53183, - 53184, - 53185, - 53186, - 53187, - 53188, - 53189, - 53190, - 53191, - 53192, - 53193, - 53194, - 53195, - 53196, - 53197, - 53198, - 53199, - 53200, - 53201, - 53202, - 53203, - 53204, - 53205, - 53206, - 53207, - 53208, - 53209, - 53210, - 53211, - 53212, - 53213, - 53214, - 53215, - 53216, - 53217, - 53218, - 53219, - 53220, - 53221, - 53222, - 53223, - 53224, - 53225, - 53226, - 53227, - 53228, - 53229, - 53230, - 53231, - 53232, - 53233, - 53234, - 53235, - 53236, - 53237, - 53238, - 53239, - 53240, - 53241, - 53242, - 53243, - 53244, - 53245, - 53246, - 53247, - 53248, - 53249, - 53250, - 53251, - 53252, - 53253, - 53254, - 53255, - 53256, - 53257, - 53258, - 53259, - 53260, - 53261, - 53262, - 53263, - 53264, - 53265, - 53266, - 53267, - 53268, - 53269, - 53270, - 53271, - 53272, - 53273, - 53274, - 53275, - 53276, - 53277, - 53278, - 53279, - 53280, - 53281, - 53282, - 53283, - 53284, - 53285, - 53286, - 53287, - 53288, - 53289, - 53290, - 53291, - 53292, - 53293, - 53294, - 53295, - 53296, - 53297, - 53298, - 53299, - 53300, - 53301, - 53302, - 53303, - 53304, - 53305, - 53306, - 53307, - 53308, - 53309, - 53310, - 53311, - 53312, - 53313, - 53314, - 53315, - 53316, - 53317, - 53318, - 53319, - 53320, - 53321, - 53322, - 53323, - 53324, - 53325, - 53326, - 53327, - 53328, - 53329, - 53330, - 53331, - 53332, - 53333, - 53334, - 53335, - 53336, - 53337, - 53338, - 53339, - 53340, - 53341, - 53342, - 53343, - 53344, - 53345, - 53346, - 53347, - 53348, - 53349, - 53350, - 53351, - 53352, - 53353, - 53354, - 53355, - 53356, - 53357, - 53358, - 53359, - 53360, - 53361, - 53362, - 53363, - 53364, - 53365, - 53366, - 53367, - 53368, - 53369, - 53370, - 53371, - 53372, - 53373, - 53374, - 53375, - 53376, - 53377, - 53378, - 53379, - 53380, - 53381, - 53382, - 53383, - 53384, - 53385, - 53386, - 53387, - 53388, - 53389, - 53390, - 53391, - 53392, - 53393, - 53394, - 53395, - 53396, - 53397, - 53398, - 53399, - 53400, - 53401, - 53402, - 53403, - 53404, - 53405, - 53406, - 53407, - 53408, - 53409, - 53410, - 53411, - 53412, - 53413, - 53414, - 53415, - 53416, - 53417, - 53418, - 53419, - 53420, - 53421, - 53422, - 53423, - 53424, - 53425, - 53426, - 53427, - 53428, - 53429, - 53430, - 53431, - 53432, - 53433, - 53434, - 53435, - 53436, - 53437, - 53438, - 53439, - 53440, - 53441, - 53442, - 53443, - 53444, - 53445, - 53446, - 53447, - 53448, - 53449, - 53450, - 53451, - 53452, - 53453, - 53454, - 53455, - 53456, - 53457, - 53458, - 53459, - 53460, - 53461, - 53462, - 53463, - 53464, - 53465, - 53466, - 53467, - 53468, - 53469, - 53470, - 53471, - 53472, - 53473, - 53474, - 53475, - 53476, - 53477, - 53478, - 53479, - 53480, - 53481, - 53482, - 53483, - 53484, - 53485, - 53486, - 53487, - 53488, - 53489, - 53490, - 53491, - 53492, - 53493, - 53494, - 53495, - 53496, - 53497, - 53498, - 53499, - 53500, - 53501, - 53502, - 53503, - 53504, - 53505, - 53506, - 53507, - 53508, - 53509, - 53510, - 53511, - 53512, - 53513, - 53514, - 53515, - 53516, - 53517, - 53518, - 53519, - 53520, - 53521, - 53522, - 53523, - 53524, - 53525, - 53526, - 53527, - 53528, - 53529, - 53530, - 53531, - 53532, - 53533, - 53534, - 53535, - 53536, - 53537, - 53538, - 53539, - 53540, - 53541, - 53542, - 53543, - 53544, - 53545, - 53546, - 53547, - 53548, - 53549, - 53550, - 53551, - 53552, - 53553, - 53554, - 53555, - 53556, - 53557, - 53558, - 53559, - 53560, - 53561, - 53562, - 53563, - 53564, - 53565, - 53566, - 53567, - 53568, - 53569, - 53570, - 53571, - 53572, - 53573, - 53574, - 53575, - 53576, - 53577, - 53578, - 53579, - 53580, - 53581, - 53582, - 53583, - 53584, - 53585, - 53586, - 53587, - 53588, - 53589, - 53590, - 53591, - 53592, - 53593, - 53594, - 53595, - 53596, - 53597, - 53598, - 53599, - 53600, - 53601, - 53602, - 53603, - 53604, - 53605, - 53606, - 53607, - 53608, - 53609, - 53610, - 53611, - 53612, - 53613, - 53614, - 53615, - 53616, - 53617, - 53618, - 53619, - 53620, - 53621, - 53622, - 53623, - 53624, - 53625, - 53626, - 53627, - 53628, - 53629, - 53630, - 53631, - 53632, - 53633, - 53634, - 53635, - 53636, - 53637, - 53638, - 53639, - 53640, - 53641, - 53642, - 53643, - 53644, - 53645, - 53646, - 53647, - 53648, - 53649, - 53650, - 53651, - 53652, - 53653, - 53654, - 53655, - 53656, - 53657, - 53658, - 53659, - 53660, - 53661, - 53662, - 53663, - 53664, - 53665, - 53666, - 53667, - 53668, - 53669, - 53670, - 53671, - 53672, - 53673, - 53674, - 53675, - 53676, - 53677, - 53678, - 53679, - 53680, - 53681, - 53682, - 53683, - 53684, - 53685, - 53686, - 53687, - 53688, - 53689, - 53690, - 53691, - 53692, - 53693, - 53694, - 53695, - 53696, - 53697, - 53698, - 53699, - 53700, - 53701, - 53702, - 53703, - 53704, - 53705, - 53706, - 53707, - 53708, - 53709, - 53710, - 53711, - 53712, - 53713, - 53714, - 53715, - 53716, - 53717, - 53718, - 53719, - 53720, - 53721, - 53722, - 53723, - 53724, - 53725, - 53726, - 53727, - 53728, - 53729, - 53730, - 53731, - 53732, - 53733, - 53734, - 53735, - 53736, - 53737, - 53738, - 53739, - 53740, - 53741, - 53742, - 53743, - 53744, - 53745, - 53746, - 53747, - 53748, - 53749, - 53750, - 53751, - 53752, - 53753, - 53754, - 53755, - 53756, - 53757, - 53758, - 53759, - 53760, - 53761, - 53762, - 53763, - 53764, - 53765, - 53766, - 53767, - 53768, - 53769, - 53770, - 53771, - 53772, - 53773, - 53774, - 53775, - 53776, - 53777, - 53778, - 53779, - 53780, - 53781, - 53782, - 53783, - 53784, - 53785, - 53786, - 53787, - 53788, - 53789, - 53790, - 53791, - 53792, - 53793, - 53794, - 53795, - 53796, - 53797, - 53798, - 53799, - 53800, - 53801, - 53802, - 53803, - 53804, - 53805, - 53806, - 53807, - 53808, - 53809, - 53810, - 53811, - 53812, - 53813, - 53814, - 53815, - 53816, - 53817, - 53818, - 53819, - 53820, - 53821, - 53822, - 53823, - 53824, - 53825, - 53826, - 53827, - 53828, - 53829, - 53830, - 53831, - 53832, - 53833, - 53834, - 53835, - 53836, - 53837, - 53838, - 53839, - 53840, - 53841, - 53842, - 53843, - 53844, - 53845, - 53846, - 53847, - 53848, - 53849, - 53850, - 53851, - 53852, - 53853, - 53854, - 53855, - 53856, - 53857, - 53858, - 53859, - 53860, - 53861, - 53862, - 53863, - 53864, - 53865, - 53866, - 53867, - 53868, - 53869, - 53870, - 53871, - 53872, - 53873, - 53874, - 53875, - 53876, - 53877, - 53878, - 53879, - 53880, - 53881, - 53882, - 53883, - 53884, - 53885, - 53886, - 53887, - 53888, - 53889, - 53890, - 53891, - 53892, - 53893, - 53894, - 53895, - 53896, - 53897, - 53898, - 53899, - 53900, - 53901, - 53902, - 53903, - 53904, - 53905, - 53906, - 53907, - 53908, - 53909, - 53910, - 53911, - 53912, - 53913, - 53914, - 53915, - 53916, - 53917, - 53918, - 53919, - 53920, - 53921, - 53922, - 53923, - 53924, - 53925, - 53926, - 53927, - 53928, - 53929, - 53930, - 53931, - 53932, - 53933, - 53934, - 53935, - 53936, - 53937, - 53938, - 53939, - 53940, - 53941, - 53942, - 53943, - 53944, - 53945, - 53946, - 53947, - 53948, - 53949, - 53950, - 53951, - 53952, - 53953, - 53954, - 53955, - 53956, - 53957, - 53958, - 53959, - 53960, - 53961, - 53962, - 53963, - 53964, - 53965, - 53966, - 53967, - 53968, - 53969, - 53970, - 53971, - 53972, - 53973, - 53974, - 53975, - 53976, - 53977, - 53978, - 53979, - 53980, - 53981, - 53982, - 53983, - 53984, - 53985, - 53986, - 53987, - 53988, - 53989, - 53990, - 53991, - 53992, - 53993, - 53994, - 53995, - 53996, - 53997, - 53998, - 53999, - 54000, - 54001, - 54002, - 54003, - 54004, - 54005, - 54006, - 54007, - 54008, - 54009, - 54010, - 54011, - 54012, - 54013, - 54014, - 54015, - 54016, - 54017, - 54018, - 54019, - 54020, - 54021, - 54022, - 54023, - 54024, - 54025, - 54026, - 54027, - 54028, - 54029, - 54030, - 54031, - 54032, - 54033, - 54034, - 54035, - 54036, - 54037, - 54038, - 54039, - 54040, - 54041, - 54042, - 54043, - 54044, - 54045, - 54046, - 54047, - 54048, - 54049, - 54050, - 54051, - 54052, - 54053, - 54054, - 54055, - 54056, - 54057, - 54058, - 54059, - 54060, - 54061, - 54062, - 54063, - 54064, - 54065, - 54066, - 54067, - 54068, - 54069, - 54070, - 54071, - 54072, - 54073, - 54074, - 54075, - 54076, - 54077, - 54078, - 54079, - 54080, - 54081, - 54082, - 54083, - 54084, - 54085, - 54086, - 54087, - 54088, - 54089, - 54090, - 54091, - 54092, - 54093, - 54094, - 54095, - 54096, - 54097, - 54098, - 54099, - 54100, - 54101, - 54102, - 54103, - 54104, - 54105, - 54106, - 54107, - 54108, - 54109, - 54110, - 54111, - 54112, - 54113, - 54114, - 54115, - 54116, - 54117, - 54118, - 54119, - 54120, - 54121, - 54122, - 54123, - 54124, - 54125, - 54126, - 54127, - 54128, - 54129, - 54130, - 54131, - 54132, - 54133, - 54134, - 54135, - 54136, - 54137, - 54138, - 54139, - 54140, - 54141, - 54142, - 54143, - 54144, - 54145, - 54146, - 54147, - 54148, - 54149, - 54150, - 54151, - 54152, - 54153, - 54154, - 54155, - 54156, - 54157, - 54158, - 54159, - 54160, - 54161, - 54162, - 54163, - 54164, - 54165, - 54166, - 54167, - 54168, - 54169, - 54170, - 54171, - 54172, - 54173, - 54174, - 54175, - 54176, - 54177, - 54178, - 54179, - 54180, - 54181, - 54182, - 54183, - 54184, - 54185, - 54186, - 54187, - 54188, - 54189, - 54190, - 54191, - 54192, - 54193, - 54194, - 54195, - 54196, - 54197, - 54198, - 54199, - 54200, - 54201, - 54202, - 54203, - 54204, - 54205, - 54206, - 54207, - 54208, - 54209, - 54210, - 54211, - 54212, - 54213, - 54214, - 54215, - 54216, - 54217, - 54218, - 54219, - 54220, - 54221, - 54222, - 54223, - 54224, - 54225, - 54226, - 54227, - 54228, - 54229, - 54230, - 54231, - 54232, - 54233, - 54234, - 54235, - 54236, - 54237, - 54238, - 54239, - 54240, - 54241, - 54242, - 54243, - 54244, - 54245, - 54246, - 54247, - 54248, - 54249, - 54250, - 54251, - 54252, - 54253, - 54254, - 54255, - 54256, - 54257, - 54258, - 54259, - 54260, - 54261, - 54262, - 54263, - 54264, - 54265, - 54266, - 54267, - 54268, - 54269, - 54270, - 54271, - 54272, - 54273, - 54274, - 54275, - 54276, - 54277, - 54278, - 54279, - 54280, - 54281, - 54282, - 54283, - 54284, - 54285, - 54286, - 54287, - 54288, - 54289, - 54290, - 54291, - 54292, - 54293, - 54294, - 54295, - 54296, - 54297, - 54298, - 54299, - 54300, - 54301, - 54302, - 54303, - 54304, - 54305, - 54306, - 54307, - 54308, - 54309, - 54310, - 54311, - 54312, - 54313, - 54314, - 54315, - 54316, - 54317, - 54318, - 54319, - 54320, - 54321, - 54322, - 54323, - 54324, - 54325, - 54326, - 54327, - 54328, - 54329, - 54330, - 54331, - 54332, - 54333, - 54334, - 54335, - 54336, - 54337, - 54338, - 54339, - 54340, - 54341, - 54342, - 54343, - 54344, - 54345, - 54346, - 54347, - 54348, - 54349, - 54350, - 54351, - 54352, - 54353, - 54354, - 54355, - 54356, - 54357, - 54358, - 54359, - 54360, - 54361, - 54362, - 54363, - 54364, - 54365, - 54366, - 54367, - 54368, - 54369, - 54370, - 54371, - 54372, - 54373, - 54374, - 54375, - 54376, - 54377, - 54378, - 54379, - 54380, - 54381, - 54382, - 54383, - 54384, - 54385, - 54386, - 54387, - 54388, - 54389, - 54390, - 54391, - 54392, - 54393, - 54394, - 54395, - 54396, - 54397, - 54398, - 54399, - 54400, - 54401, - 54402, - 54403, - 54404, - 54405, - 54406, - 54407, - 54408, - 54409, - 54410, - 54411, - 54412, - 54413, - 54414, - 54415, - 54416, - 54417, - 54418, - 54419, - 54420, - 54421, - 54422, - 54423, - 54424, - 54425, - 54426, - 54427, - 54428, - 54429, - 54430, - 54431, - 54432, - 54433, - 54434, - 54435, - 54436, - 54437, - 54438, - 54439, - 54440, - 54441, - 54442, - 54443, - 54444, - 54445, - 54446, - 54447, - 54448, - 54449, - 54450, - 54451, - 54452, - 54453, - 54454, - 54455, - 54456, - 54457, - 54458, - 54459, - 54460, - 54461, - 54462, - 54463, - 54464, - 54465, - 54466, - 54467, - 54468, - 54469, - 54470, - 54471, - 54472, - 54473, - 54474, - 54475, - 54476, - 54477, - 54478, - 54479, - 54480, - 54481, - 54482, - 54483, - 54484, - 54485, - 54486, - 54487, - 54488, - 54489, - 54490, - 54491, - 54492, - 54493, - 54494, - 54495, - 54496, - 54497, - 54498, - 54499, - 54500, - 54501, - 54502, - 54503, - 54504, - 54505, - 54506, - 54507, - 54508, - 54509, - 54510, - 54511, - 54512, - 54513, - 54514, - 54515, - 54516, - 54517, - 54518, - 54519, - 54520, - 54521, - 54522, - 54523, - 54524, - 54525, - 54526, - 54527, - 54528, - 54529, - 54530, - 54531, - 54532, - 54533, - 54534, - 54535, - 54536, - 54537, - 54538, - 54539, - 54540, - 54541, - 54542, - 54543, - 54544, - 54545, - 54546, - 54547, - 54548, - 54549, - 54550, - 54551, - 54552, - 54553, - 54554, - 54555, - 54556, - 54557, - 54558, - 54559, - 54560, - 54561, - 54562, - 54563, - 54564, - 54565, - 54566, - 54567, - 54568, - 54569, - 54570, - 54571, - 54572, - 54573, - 54574, - 54575, - 54576, - 54577, - 54578, - 54579, - 54580, - 54581, - 54582, - 54583, - 54584, - 54585, - 54586, - 54587, - 54588, - 54589, - 54590, - 54591, - 54592, - 54593, - 54594, - 54595, - 54596, - 54597, - 54598, - 54599, - 54600, - 54601, - 54602, - 54603, - 54604, - 54605, - 54606, - 54607, - 54608, - 54609, - 54610, - 54611, - 54612, - 54613, - 54614, - 54615, - 54616, - 54617, - 54618, - 54619, - 54620, - 54621, - 54622, - 54623, - 54624, - 54625, - 54626, - 54627, - 54628, - 54629, - 54630, - 54631, - 54632, - 54633, - 54634, - 54635, - 54636, - 54637, - 54638, - 54639, - 54640, - 54641, - 54642, - 54643, - 54644, - 54645, - 54646, - 54647, - 54648, - 54649, - 54650, - 54651, - 54652, - 54653, - 54654, - 54655, - 54656, - 54657, - 54658, - 54659, - 54660, - 54661, - 54662, - 54663, - 54664, - 54665, - 54666, - 54667, - 54668, - 54669, - 54670, - 54671, - 54672, - 54673, - 54674, - 54675, - 54676, - 54677, - 54678, - 54679, - 54680, - 54681, - 54682, - 54683, - 54684, - 54685, - 54686, - 54687, - 54688, - 54689, - 54690, - 54691, - 54692, - 54693, - 54694, - 54695, - 54696, - 54697, - 54698, - 54699, - 54700, - 54701, - 54702, - 54703, - 54704, - 54705, - 54706, - 54707, - 54708, - 54709, - 54710, - 54711, - 54712, - 54713, - 54714, - 54715, - 54716, - 54717, - 54718, - 54719, - 54720, - 54721, - 54722, - 54723, - 54724, - 54725, - 54726, - 54727, - 54728, - 54729, - 54730, - 54731, - 54732, - 54733, - 54734, - 54735, - 54736, - 54737, - 54738, - 54739, - 54740, - 54741, - 54742, - 54743, - 54744, - 54745, - 54746, - 54747, - 54748, - 54749, - 54750, - 54751, - 54752, - 54753, - 54754, - 54755, - 54756, - 54757, - 54758, - 54759, - 54760, - 54761, - 54762, - 54763, - 54764, - 54765, - 54766, - 54767, - 54768, - 54769, - 54770, - 54771, - 54772, - 54773, - 54774, - 54775, - 54776, - 54777, - 54778, - 54779, - 54780, - 54781, - 54782, - 54783, - 54784, - 54785, - 54786, - 54787, - 54788, - 54789, - 54790, - 54791, - 54792, - 54793, - 54794, - 54795, - 54796, - 54797, - 54798, - 54799, - 54800, - 54801, - 54802, - 54803, - 54804, - 54805, - 54806, - 54807, - 54808, - 54809, - 54810, - 54811, - 54812, - 54813, - 54814, - 54815, - 54816, - 54817, - 54818, - 54819, - 54820, - 54821, - 54822, - 54823, - 54824, - 54825, - 54826, - 54827, - 54828, - 54829, - 54830, - 54831, - 54832, - 54833, - 54834, - 54835, - 54836, - 54837, - 54838, - 54839, - 54840, - 54841, - 54842, - 54843, - 54844, - 54845, - 54846, - 54847, - 54848, - 54849, - 54850, - 54851, - 54852, - 54853, - 54854, - 54855, - 54856, - 54857, - 54858, - 54859, - 54860, - 54861, - 54862, - 54863, - 54864, - 54865, - 54866, - 54867, - 54868, - 54869, - 54870, - 54871, - 54872, - 54873, - 54874, - 54875, - 54876, - 54877, - 54878, - 54879, - 54880, - 54881, - 54882, - 54883, - 54884, - 54885, - 54886, - 54887, - 54888, - 54889, - 54890, - 54891, - 54892, - 54893, - 54894, - 54895, - 54896, - 54897, - 54898, - 54899, - 54900, - 54901, - 54902, - 54903, - 54904, - 54905, - 54906, - 54907, - 54908, - 54909, - 54910, - 54911, - 54912, - 54913, - 54914, - 54915, - 54916, - 54917, - 54918, - 54919, - 54920, - 54921, - 54922, - 54923, - 54924, - 54925, - 54926, - 54927, - 54928, - 54929, - 54930, - 54931, - 54932, - 54933, - 54934, - 54935, - 54936, - 54937, - 54938, - 54939, - 54940, - 54941, - 54942, - 54943, - 54944, - 54945, - 54946, - 54947, - 54948, - 54949, - 54950, - 54951, - 54952, - 54953, - 54954, - 54955, - 54956, - 54957, - 54958, - 54959, - 54960, - 54961, - 54962, - 54963, - 54964, - 54965, - 54966, - 54967, - 54968, - 54969, - 54970, - 54971, - 54972, - 54973, - 54974, - 54975, - 54976, - 54977, - 54978, - 54979, - 54980, - 54981, - 54982, - 54983, - 54984, - 54985, - 54986, - 54987, - 54988, - 54989, - 54990, - 54991, - 54992, - 54993, - 54994, - 54995, - 54996, - 54997, - 54998, - 54999, - 55000, - 55001, - 55002, - 55003, - 55004, - 55005, - 55006, - 55007, - 55008, - 55009, - 55010, - 55011, - 55012, - 55013, - 55014, - 55015, - 55016, - 55017, - 55018, - 55019, - 55020, - 55021, - 55022, - 55023, - 55024, - 55025, - 55026, - 55027, - 55028, - 55029, - 55030, - 55031, - 55032, - 55033, - 55034, - 55035, - 55036, - 55037, - 55038, - 55039, - 55040, - 55041, - 55042, - 55043, - 55044, - 55045, - 55046, - 55047, - 55048, - 55049, - 55050, - 55051, - 55052, - 55053, - 55054, - 55055, - 55056, - 55057, - 55058, - 55059, - 55060, - 55061, - 55062, - 55063, - 55064, - 55065, - 55066, - 55067, - 55068, - 55069, - 55070, - 55071, - 55072, - 55073, - 55074, - 55075, - 55076, - 55077, - 55078, - 55079, - 55080, - 55081, - 55082, - 55083, - 55084, - 55085, - 55086, - 55087, - 55088, - 55089, - 55090, - 55091, - 55092, - 55093, - 55094, - 55095, - 55096, - 55097, - 55098, - 55099, - 55100, - 55101, - 55102, - 55103, - 55104, - 55105, - 55106, - 55107, - 55108, - 55109, - 55110, - 55111, - 55112, - 55113, - 55114, - 55115, - 55116, - 55117, - 55118, - 55119, - 55120, - 55121, - 55122, - 55123, - 55124, - 55125, - 55126, - 55127, - 55128, - 55129, - 55130, - 55131, - 55132, - 55133, - 55134, - 55135, - 55136, - 55137, - 55138, - 55139, - 55140, - 55141, - 55142, - 55143, - 55144, - 55145, - 55146, - 55147, - 55148, - 55149, - 55150, - 55151, - 55152, - 55153, - 55154, - 55155, - 55156, - 55157, - 55158, - 55159, - 55160, - 55161, - 55162, - 55163, - 55164, - 55165, - 55166, - 55167, - 55168, - 55169, - 55170, - 55171, - 55172, - 55173, - 55174, - 55175, - 55176, - 55177, - 55178, - 55179, - 55180, - 55181, - 55182, - 55183, - 55184, - 55185, - 55186, - 55187, - 55188, - 55189, - 55190, - 55191, - 55192, - 55193, - 55194, - 55195, - 55196, - 55197, - 55198, - 55199, - 55200, - 55201, - 55202, - 55203, - 55216, - 55217, - 55218, - 55219, - 55220, - 55221, - 55222, - 55223, - 55224, - 55225, - 55226, - 55227, - 55228, - 55229, - 55230, - 55231, - 55232, - 55233, - 55234, - 55235, - 55236, - 55237, - 55238, - 55243, - 55244, - 55245, - 55246, - 55247, - 55248, - 55249, - 55250, - 55251, - 55252, - 55253, - 55254, - 55255, - 55256, - 55257, - 55258, - 55259, - 55260, - 55261, - 55262, - 55263, - 55264, - 55265, - 55266, - 55267, - 55268, - 55269, - 55270, - 55271, - 55272, - 55273, - 55274, - 55275, - 55276, - 55277, - 55278, - 55279, - 55280, - 55281, - 55282, - 55283, - 55284, - 55285, - 55286, - 55287, - 55288, - 55289, - 55290, - 55291, - 63744, - 63745, - 63746, - 63747, - 63748, - 63749, - 63750, - 63751, - 63752, - 63753, - 63754, - 63755, - 63756, - 63757, - 63758, - 63759, - 63760, - 63761, - 63762, - 63763, - 63764, - 63765, - 63766, - 63767, - 63768, - 63769, - 63770, - 63771, - 63772, - 63773, - 63774, - 63775, - 63776, - 63777, - 63778, - 63779, - 63780, - 63781, - 63782, - 63783, - 63784, - 63785, - 63786, - 63787, - 63788, - 63789, - 63790, - 63791, - 63792, - 63793, - 63794, - 63795, - 63796, - 63797, - 63798, - 63799, - 63800, - 63801, - 63802, - 63803, - 63804, - 63805, - 63806, - 63807, - 63808, - 63809, - 63810, - 63811, - 63812, - 63813, - 63814, - 63815, - 63816, - 63817, - 63818, - 63819, - 63820, - 63821, - 63822, - 63823, - 63824, - 63825, - 63826, - 63827, - 63828, - 63829, - 63830, - 63831, - 63832, - 63833, - 63834, - 63835, - 63836, - 63837, - 63838, - 63839, - 63840, - 63841, - 63842, - 63843, - 63844, - 63845, - 63846, - 63847, - 63848, - 63849, - 63850, - 63851, - 63852, - 63853, - 63854, - 63855, - 63856, - 63857, - 63858, - 63859, - 63860, - 63861, - 63862, - 63863, - 63864, - 63865, - 63866, - 63867, - 63868, - 63869, - 63870, - 63871, - 63872, - 63873, - 63874, - 63875, - 63876, - 63877, - 63878, - 63879, - 63880, - 63881, - 63882, - 63883, - 63884, - 63885, - 63886, - 63887, - 63888, - 63889, - 63890, - 63891, - 63892, - 63893, - 63894, - 63895, - 63896, - 63897, - 63898, - 63899, - 63900, - 63901, - 63902, - 63903, - 63904, - 63905, - 63906, - 63907, - 63908, - 63909, - 63910, - 63911, - 63912, - 63913, - 63914, - 63915, - 63916, - 63917, - 63918, - 63919, - 63920, - 63921, - 63922, - 63923, - 63924, - 63925, - 63926, - 63927, - 63928, - 63929, - 63930, - 63931, - 63932, - 63933, - 63934, - 63935, - 63936, - 63937, - 63938, - 63939, - 63940, - 63941, - 63942, - 63943, - 63944, - 63945, - 63946, - 63947, - 63948, - 63949, - 63950, - 63951, - 63952, - 63953, - 63954, - 63955, - 63956, - 63957, - 63958, - 63959, - 63960, - 63961, - 63962, - 63963, - 63964, - 63965, - 63966, - 63967, - 63968, - 63969, - 63970, - 63971, - 63972, - 63973, - 63974, - 63975, - 63976, - 63977, - 63978, - 63979, - 63980, - 63981, - 63982, - 63983, - 63984, - 63985, - 63986, - 63987, - 63988, - 63989, - 63990, - 63991, - 63992, - 63993, - 63994, - 63995, - 63996, - 63997, - 63998, - 63999, - 64000, - 64001, - 64002, - 64003, - 64004, - 64005, - 64006, - 64007, - 64008, - 64009, - 64010, - 64011, - 64012, - 64013, - 64014, - 64015, - 64016, - 64017, - 64018, - 64019, - 64020, - 64021, - 64022, - 64023, - 64024, - 64025, - 64026, - 64027, - 64028, - 64029, - 64030, - 64031, - 64032, - 64033, - 64034, - 64035, - 64036, - 64037, - 64038, - 64039, - 64040, - 64041, - 64042, - 64043, - 64044, - 64045, - 64046, - 64047, - 64048, - 64049, - 64050, - 64051, - 64052, - 64053, - 64054, - 64055, - 64056, - 64057, - 64058, - 64059, - 64060, - 64061, - 64062, - 64063, - 64064, - 64065, - 64066, - 64067, - 64068, - 64069, - 64070, - 64071, - 64072, - 64073, - 64074, - 64075, - 64076, - 64077, - 64078, - 64079, - 64080, - 64081, - 64082, - 64083, - 64084, - 64085, - 64086, - 64087, - 64088, - 64089, - 64090, - 64091, - 64092, - 64093, - 64094, - 64095, - 64096, - 64097, - 64098, - 64099, - 64100, - 64101, - 64102, - 64103, - 64104, - 64105, - 64106, - 64107, - 64108, - 64109, - 64112, - 64113, - 64114, - 64115, - 64116, - 64117, - 64118, - 64119, - 64120, - 64121, - 64122, - 64123, - 64124, - 64125, - 64126, - 64127, - 64128, - 64129, - 64130, - 64131, - 64132, - 64133, - 64134, - 64135, - 64136, - 64137, - 64138, - 64139, - 64140, - 64141, - 64142, - 64143, - 64144, - 64145, - 64146, - 64147, - 64148, - 64149, - 64150, - 64151, - 64152, - 64153, - 64154, - 64155, - 64156, - 64157, - 64158, - 64159, - 64160, - 64161, - 64162, - 64163, - 64164, - 64165, - 64166, - 64167, - 64168, - 64169, - 64170, - 64171, - 64172, - 64173, - 64174, - 64175, - 64176, - 64177, - 64178, - 64179, - 64180, - 64181, - 64182, - 64183, - 64184, - 64185, - 64186, - 64187, - 64188, - 64189, - 64190, - 64191, - 64192, - 64193, - 64194, - 64195, - 64196, - 64197, - 64198, - 64199, - 64200, - 64201, - 64202, - 64203, - 64204, - 64205, - 64206, - 64207, - 64208, - 64209, - 64210, - 64211, - 64212, - 64213, - 64214, - 64215, - 64216, - 64217, - 64256, - 64257, - 64258, - 64259, - 64260, - 64261, - 64262, - 64275, - 64276, - 64277, - 64278, - 64279, - 64285, - 64287, - 64288, - 64289, - 64290, - 64291, - 64292, - 64293, - 64294, - 64295, - 64296, - 64298, - 64299, - 64300, - 64301, - 64302, - 64303, - 64304, - 64305, - 64306, - 64307, - 64308, - 64309, - 64310, - 64312, - 64313, - 64314, - 64315, - 64316, - 64318, - 64320, - 64321, - 64323, - 64324, - 64326, - 64327, - 64328, - 64329, - 64330, - 64331, - 64332, - 64333, - 64334, - 64335, - 64336, - 64337, - 64338, - 64339, - 64340, - 64341, - 64342, - 64343, - 64344, - 64345, - 64346, - 64347, - 64348, - 64349, - 64350, - 64351, - 64352, - 64353, - 64354, - 64355, - 64356, - 64357, - 64358, - 64359, - 64360, - 64361, - 64362, - 64363, - 64364, - 64365, - 64366, - 64367, - 64368, - 64369, - 64370, - 64371, - 64372, - 64373, - 64374, - 64375, - 64376, - 64377, - 64378, - 64379, - 64380, - 64381, - 64382, - 64383, - 64384, - 64385, - 64386, - 64387, - 64388, - 64389, - 64390, - 64391, - 64392, - 64393, - 64394, - 64395, - 64396, - 64397, - 64398, - 64399, - 64400, - 64401, - 64402, - 64403, - 64404, - 64405, - 64406, - 64407, - 64408, - 64409, - 64410, - 64411, - 64412, - 64413, - 64414, - 64415, - 64416, - 64417, - 64418, - 64419, - 64420, - 64421, - 64422, - 64423, - 64424, - 64425, - 64426, - 64427, - 64428, - 64429, - 64430, - 64431, - 64432, - 64433, - 64467, - 64468, - 64469, - 64470, - 64471, - 64472, - 64473, - 64474, - 64475, - 64476, - 64477, - 64478, - 64479, - 64480, - 64481, - 64482, - 64483, - 64484, - 64485, - 64486, - 64487, - 64488, - 64489, - 64490, - 64491, - 64492, - 64493, - 64494, - 64495, - 64496, - 64497, - 64498, - 64499, - 64500, - 64501, - 64502, - 64503, - 64504, - 64505, - 64506, - 64507, - 64508, - 64509, - 64510, - 64511, - 64512, - 64513, - 64514, - 64515, - 64516, - 64517, - 64518, - 64519, - 64520, - 64521, - 64522, - 64523, - 64524, - 64525, - 64526, - 64527, - 64528, - 64529, - 64530, - 64531, - 64532, - 64533, - 64534, - 64535, - 64536, - 64537, - 64538, - 64539, - 64540, - 64541, - 64542, - 64543, - 64544, - 64545, - 64546, - 64547, - 64548, - 64549, - 64550, - 64551, - 64552, - 64553, - 64554, - 64555, - 64556, - 64557, - 64558, - 64559, - 64560, - 64561, - 64562, - 64563, - 64564, - 64565, - 64566, - 64567, - 64568, - 64569, - 64570, - 64571, - 64572, - 64573, - 64574, - 64575, - 64576, - 64577, - 64578, - 64579, - 64580, - 64581, - 64582, - 64583, - 64584, - 64585, - 64586, - 64587, - 64588, - 64589, - 64590, - 64591, - 64592, - 64593, - 64594, - 64595, - 64596, - 64597, - 64598, - 64599, - 64600, - 64601, - 64602, - 64603, - 64604, - 64605, - 64606, - 64607, - 64608, - 64609, - 64610, - 64611, - 64612, - 64613, - 64614, - 64615, - 64616, - 64617, - 64618, - 64619, - 64620, - 64621, - 64622, - 64623, - 64624, - 64625, - 64626, - 64627, - 64628, - 64629, - 64630, - 64631, - 64632, - 64633, - 64634, - 64635, - 64636, - 64637, - 64638, - 64639, - 64640, - 64641, - 64642, - 64643, - 64644, - 64645, - 64646, - 64647, - 64648, - 64649, - 64650, - 64651, - 64652, - 64653, - 64654, - 64655, - 64656, - 64657, - 64658, - 64659, - 64660, - 64661, - 64662, - 64663, - 64664, - 64665, - 64666, - 64667, - 64668, - 64669, - 64670, - 64671, - 64672, - 64673, - 64674, - 64675, - 64676, - 64677, - 64678, - 64679, - 64680, - 64681, - 64682, - 64683, - 64684, - 64685, - 64686, - 64687, - 64688, - 64689, - 64690, - 64691, - 64692, - 64693, - 64694, - 64695, - 64696, - 64697, - 64698, - 64699, - 64700, - 64701, - 64702, - 64703, - 64704, - 64705, - 64706, - 64707, - 64708, - 64709, - 64710, - 64711, - 64712, - 64713, - 64714, - 64715, - 64716, - 64717, - 64718, - 64719, - 64720, - 64721, - 64722, - 64723, - 64724, - 64725, - 64726, - 64727, - 64728, - 64729, - 64730, - 64731, - 64732, - 64733, - 64734, - 64735, - 64736, - 64737, - 64738, - 64739, - 64740, - 64741, - 64742, - 64743, - 64744, - 64745, - 64746, - 64747, - 64748, - 64749, - 64750, - 64751, - 64752, - 64753, - 64754, - 64755, - 64756, - 64757, - 64758, - 64759, - 64760, - 64761, - 64762, - 64763, - 64764, - 64765, - 64766, - 64767, - 64768, - 64769, - 64770, - 64771, - 64772, - 64773, - 64774, - 64775, - 64776, - 64777, - 64778, - 64779, - 64780, - 64781, - 64782, - 64783, - 64784, - 64785, - 64786, - 64787, - 64788, - 64789, - 64790, - 64791, - 64792, - 64793, - 64794, - 64795, - 64796, - 64797, - 64798, - 64799, - 64800, - 64801, - 64802, - 64803, - 64804, - 64805, - 64806, - 64807, - 64808, - 64809, - 64810, - 64811, - 64812, - 64813, - 64814, - 64815, - 64816, - 64817, - 64818, - 64819, - 64820, - 64821, - 64822, - 64823, - 64824, - 64825, - 64826, - 64827, - 64828, - 64829, - 64848, - 64849, - 64850, - 64851, - 64852, - 64853, - 64854, - 64855, - 64856, - 64857, - 64858, - 64859, - 64860, - 64861, - 64862, - 64863, - 64864, - 64865, - 64866, - 64867, - 64868, - 64869, - 64870, - 64871, - 64872, - 64873, - 64874, - 64875, - 64876, - 64877, - 64878, - 64879, - 64880, - 64881, - 64882, - 64883, - 64884, - 64885, - 64886, - 64887, - 64888, - 64889, - 64890, - 64891, - 64892, - 64893, - 64894, - 64895, - 64896, - 64897, - 64898, - 64899, - 64900, - 64901, - 64902, - 64903, - 64904, - 64905, - 64906, - 64907, - 64908, - 64909, - 64910, - 64911, - 64914, - 64915, - 64916, - 64917, - 64918, - 64919, - 64920, - 64921, - 64922, - 64923, - 64924, - 64925, - 64926, - 64927, - 64928, - 64929, - 64930, - 64931, - 64932, - 64933, - 64934, - 64935, - 64936, - 64937, - 64938, - 64939, - 64940, - 64941, - 64942, - 64943, - 64944, - 64945, - 64946, - 64947, - 64948, - 64949, - 64950, - 64951, - 64952, - 64953, - 64954, - 64955, - 64956, - 64957, - 64958, - 64959, - 64960, - 64961, - 64962, - 64963, - 64964, - 64965, - 64966, - 64967, - 65008, - 65009, - 65010, - 65011, - 65012, - 65013, - 65014, - 65015, - 65016, - 65017, - 65018, - 65019, - 65136, - 65137, - 65138, - 65139, - 65140, - 65142, - 65143, - 65144, - 65145, - 65146, - 65147, - 65148, - 65149, - 65150, - 65151, - 65152, - 65153, - 65154, - 65155, - 65156, - 65157, - 65158, - 65159, - 65160, - 65161, - 65162, - 65163, - 65164, - 65165, - 65166, - 65167, - 65168, - 65169, - 65170, - 65171, - 65172, - 65173, - 65174, - 65175, - 65176, - 65177, - 65178, - 65179, - 65180, - 65181, - 65182, - 65183, - 65184, - 65185, - 65186, - 65187, - 65188, - 65189, - 65190, - 65191, - 65192, - 65193, - 65194, - 65195, - 65196, - 65197, - 65198, - 65199, - 65200, - 65201, - 65202, - 65203, - 65204, - 65205, - 65206, - 65207, - 65208, - 65209, - 65210, - 65211, - 65212, - 65213, - 65214, - 65215, - 65216, - 65217, - 65218, - 65219, - 65220, - 65221, - 65222, - 65223, - 65224, - 65225, - 65226, - 65227, - 65228, - 65229, - 65230, - 65231, - 65232, - 65233, - 65234, - 65235, - 65236, - 65237, - 65238, - 65239, - 65240, - 65241, - 65242, - 65243, - 65244, - 65245, - 65246, - 65247, - 65248, - 65249, - 65250, - 65251, - 65252, - 65253, - 65254, - 65255, - 65256, - 65257, - 65258, - 65259, - 65260, - 65261, - 65262, - 65263, - 65264, - 65265, - 65266, - 65267, - 65268, - 65269, - 65270, - 65271, - 65272, - 65273, - 65274, - 65275, - 65276, - 65313, - 65314, - 65315, - 65316, - 65317, - 65318, - 65319, - 65320, - 65321, - 65322, - 65323, - 65324, - 65325, - 65326, - 65327, - 65328, - 65329, - 65330, - 65331, - 65332, - 65333, - 65334, - 65335, - 65336, - 65337, - 65338, - 65345, - 65346, - 65347, - 65348, - 65349, - 65350, - 65351, - 65352, - 65353, - 65354, - 65355, - 65356, - 65357, - 65358, - 65359, - 65360, - 65361, - 65362, - 65363, - 65364, - 65365, - 65366, - 65367, - 65368, - 65369, - 65370, - 65382, - 65383, - 65384, - 65385, - 65386, - 65387, - 65388, - 65389, - 65390, - 65391, - 65392, - 65393, - 65394, - 65395, - 65396, - 65397, - 65398, - 65399, - 65400, - 65401, - 65402, - 65403, - 65404, - 65405, - 65406, - 65407, - 65408, - 65409, - 65410, - 65411, - 65412, - 65413, - 65414, - 65415, - 65416, - 65417, - 65418, - 65419, - 65420, - 65421, - 65422, - 65423, - 65424, - 65425, - 65426, - 65427, - 65428, - 65429, - 65430, - 65431, - 65432, - 65433, - 65434, - 65435, - 65436, - 65437, - 65438, - 65439, - 65440, - 65441, - 65442, - 65443, - 65444, - 65445, - 65446, - 65447, - 65448, - 65449, - 65450, - 65451, - 65452, - 65453, - 65454, - 65455, - 65456, - 65457, - 65458, - 65459, - 65460, - 65461, - 65462, - 65463, - 65464, - 65465, - 65466, - 65467, - 65468, - 65469, - 65470, - 65474, - 65475, - 65476, - 65477, - 65478, - 65479, - 65482, - 65483, - 65484, - 65485, - 65486, - 65487, - 65490, - 65491, - 65492, - 65493, - 65494, - 65495, - 65498, - 65499, - 65500 -]; - +var str = '170,181,186,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431,432,433,434,435,436,437,438,439,440,441,442,443,444,445,446,447,448,449,450,451,452,453,454,455,456,457,458,459,460,461,462,463,464,465,466,467,468,469,470,471,472,473,474,475,476,477,478,479,480,481,482,483,484,485,486,487,488,489,490,491,492,493,494,495,496,497,498,499,500,501,502,503,504,505,506,507,508,509,510,511,512,513,514,515,516,517,518,519,520,521,522,523,524,525,526,527,528,529,530,531,532,533,534,535,536,537,538,539,540,541,542,543,544,545,546,547,548,549,550,551,552,553,554,555,556,557,558,559,560,561,562,563,564,565,566,567,568,569,570,571,572,573,574,575,576,577,578,579,580,581,582,583,584,585,586,587,588,589,590,591,592,593,594,595,596,597,598,599,600,601,602,603,604,605,606,607,608,609,610,611,612,613,614,615,616,617,618,619,620,621,622,623,624,625,626,627,628,629,630,631,632,633,634,635,636,637,638,639,640,641,642,643,644,645,646,647,648,649,650,651,652,653,654,655,656,657,658,659,660,661,662,663,664,665,666,667,668,669,670,671,672,673,674,675,676,677,678,679,680,681,682,683,684,685,686,687,688,689,690,691,692,693,694,695,696,697,698,699,700,701,702,703,704,705,710,711,712,713,714,715,716,717,718,719,720,721,736,737,738,739,740,748,750,880,881,882,883,884,886,887,890,891,892,893,902,904,905,906,908,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,975,976,977,978,979,980,981,982,983,984,985,986,987,988,989,990,991,992,993,994,995,996,997,998,999,1000,1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1011,1012,1013,1015,1016,1017,1018,1019,1020,1021,1022,1023,1024,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1104,1105,1106,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,1117,1118,1119,1120,1121,1122,1123,1124,1125,1126,1127,1128,1129,1130,1131,1132,1133,1134,1135,1136,1137,1138,1139,1140,1141,1142,1143,1144,1145,1146,1147,1148,1149,1150,1151,1152,1153,1162,1163,1164,1165,1166,1167,1168,1169,1170,1171,1172,1173,1174,1175,1176,1177,1178,1179,1180,1181,1182,1183,1184,1185,1186,1187,1188,1189,1190,1191,1192,1193,1194,1195,1196,1197,1198,1199,1200,1201,1202,1203,1204,1205,1206,1207,1208,1209,1210,1211,1212,1213,1214,1215,1216,1217,1218,1219,1220,1221,1222,1223,1224,1225,1226,1227,1228,1229,1230,1231,1232,1233,1234,1235,1236,1237,1238,1239,1240,1241,1242,1243,1244,1245,1246,1247,1248,1249,1250,1251,1252,1253,1254,1255,1256,1257,1258,1259,1260,1261,1262,1263,1264,1265,1266,1267,1268,1269,1270,1271,1272,1273,1274,1275,1276,1277,1278,1279,1280,1281,1282,1283,1284,1285,1286,1287,1288,1289,1290,1291,1292,1293,1294,1295,1296,1297,1298,1299,1300,1301,1302,1303,1304,1305,1306,1307,1308,1309,1310,1311,1312,1313,1314,1315,1316,1317,1318,1319,1329,1330,1331,1332,1333,1334,1335,1336,1337,1338,1339,1340,1341,1342,1343,1344,1345,1346,1347,1348,1349,1350,1351,1352,1353,1354,1355,1356,1357,1358,1359,1360,1361,1362,1363,1364,1365,1366,1369,1377,1378,1379,1380,1381,1382,1383,1384,1385,1386,1387,1388,1389,1390,1391,1392,1393,1394,1395,1396,1397,1398,1399,1400,1401,1402,1403,1404,1405,1406,1407,1408,1409,1410,1411,1412,1413,1414,1415,1488,1489,1490,1491,1492,1493,1494,1495,1496,1497,1498,1499,1500,1501,1502,1503,1504,1505,1506,1507,1508,1509,1510,1511,1512,1513,1514,1520,1521,1522,1568,1569,1570,1571,1572,1573,1574,1575,1576,1577,1578,1579,1580,1581,1582,1583,1584,1585,1586,1587,1588,1589,1590,1591,1592,1593,1594,1595,1596,1597,1598,1599,1600,1601,1602,1603,1604,1605,1606,1607,1608,1609,1610,1646,1647,1649,1650,1651,1652,1653,1654,1655,1656,1657,1658,1659,1660,1661,1662,1663,1664,1665,1666,1667,1668,1669,1670,1671,1672,1673,1674,1675,1676,1677,1678,1679,1680,1681,1682,1683,1684,1685,1686,1687,1688,1689,1690,1691,1692,1693,1694,1695,1696,1697,1698,1699,1700,1701,1702,1703,1704,1705,1706,1707,1708,1709,1710,1711,1712,1713,1714,1715,1716,1717,1718,1719,1720,1721,1722,1723,1724,1725,1726,1727,1728,1729,1730,1731,1732,1733,1734,1735,1736,1737,1738,1739,1740,1741,1742,1743,1744,1745,1746,1747,1749,1765,1766,1774,1775,1786,1787,1788,1791,1808,1810,1811,1812,1813,1814,1815,1816,1817,1818,1819,1820,1821,1822,1823,1824,1825,1826,1827,1828,1829,1830,1831,1832,1833,1834,1835,1836,1837,1838,1839,1869,1870,1871,1872,1873,1874,1875,1876,1877,1878,1879,1880,1881,1882,1883,1884,1885,1886,1887,1888,1889,1890,1891,1892,1893,1894,1895,1896,1897,1898,1899,1900,1901,1902,1903,1904,1905,1906,1907,1908,1909,1910,1911,1912,1913,1914,1915,1916,1917,1918,1919,1920,1921,1922,1923,1924,1925,1926,1927,1928,1929,1930,1931,1932,1933,1934,1935,1936,1937,1938,1939,1940,1941,1942,1943,1944,1945,1946,1947,1948,1949,1950,1951,1952,1953,1954,1955,1956,1957,1969,1994,1995,1996,1997,1998,1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021,2022,2023,2024,2025,2026,2036,2037,2042,2048,2049,2050,2051,2052,2053,2054,2055,2056,2057,2058,2059,2060,2061,2062,2063,2064,2065,2066,2067,2068,2069,2074,2084,2088,2112,2113,2114,2115,2116,2117,2118,2119,2120,2121,2122,2123,2124,2125,2126,2127,2128,2129,2130,2131,2132,2133,2134,2135,2136,2208,2210,2211,2212,2213,2214,2215,2216,2217,2218,2219,2220,2308,2309,2310,2311,2312,2313,2314,2315,2316,2317,2318,2319,2320,2321,2322,2323,2324,2325,2326,2327,2328,2329,2330,2331,2332,2333,2334,2335,2336,2337,2338,2339,2340,2341,2342,2343,2344,2345,2346,2347,2348,2349,2350,2351,2352,2353,2354,2355,2356,2357,2358,2359,2360,2361,2365,2384,2392,2393,2394,2395,2396,2397,2398,2399,2400,2401,2417,2418,2419,2420,2421,2422,2423,2425,2426,2427,2428,2429,2430,2431,2437,2438,2439,2440,2441,2442,2443,2444,2447,2448,2451,2452,2453,2454,2455,2456,2457,2458,2459,2460,2461,2462,2463,2464,2465,2466,2467,2468,2469,2470,2471,2472,2474,2475,2476,2477,2478,2479,2480,2482,2486,2487,2488,2489,2493,2510,2524,2525,2527,2528,2529,2544,2545,2565,2566,2567,2568,2569,2570,2575,2576,2579,2580,2581,2582,2583,2584,2585,2586,2587,2588,2589,2590,2591,2592,2593,2594,2595,2596,2597,2598,2599,2600,2602,2603,2604,2605,2606,2607,2608,2610,2611,2613,2614,2616,2617,2649,2650,2651,2652,2654,2674,2675,2676,2693,2694,2695,2696,2697,2698,2699,2700,2701,2703,2704,2705,2707,2708,2709,2710,2711,2712,2713,2714,2715,2716,2717,2718,2719,2720,2721,2722,2723,2724,2725,2726,2727,2728,2730,2731,2732,2733,2734,2735,2736,2738,2739,2741,2742,2743,2744,2745,2749,2768,2784,2785,2821,2822,2823,2824,2825,2826,2827,2828,2831,2832,2835,2836,2837,2838,2839,2840,2841,2842,2843,2844,2845,2846,2847,2848,2849,2850,2851,2852,2853,2854,2855,2856,2858,2859,2860,2861,2862,2863,2864,2866,2867,2869,2870,2871,2872,2873,2877,2908,2909,2911,2912,2913,2929,2947,2949,2950,2951,2952,2953,2954,2958,2959,2960,2962,2963,2964,2965,2969,2970,2972,2974,2975,2979,2980,2984,2985,2986,2990,2991,2992,2993,2994,2995,2996,2997,2998,2999,3000,3001,3024,3077,3078,3079,3080,3081,3082,3083,3084,3086,3087,3088,3090,3091,3092,3093,3094,3095,3096,3097,3098,3099,3100,3101,3102,3103,3104,3105,3106,3107,3108,3109,3110,3111,3112,3114,3115,3116,3117,3118,3119,3120,3121,3122,3123,3125,3126,3127,3128,3129,3133,3160,3161,3168,3169,3205,3206,3207,3208,3209,3210,3211,3212,3214,3215,3216,3218,3219,3220,3221,3222,3223,3224,3225,3226,3227,3228,3229,3230,3231,3232,3233,3234,3235,3236,3237,3238,3239,3240,3242,3243,3244,3245,3246,3247,3248,3249,3250,3251,3253,3254,3255,3256,3257,3261,3294,3296,3297,3313,3314,3333,3334,3335,3336,3337,3338,3339,3340,3342,3343,3344,3346,3347,3348,3349,3350,3351,3352,3353,3354,3355,3356,3357,3358,3359,3360,3361,3362,3363,3364,3365,3366,3367,3368,3369,3370,3371,3372,3373,3374,3375,3376,3377,3378,3379,3380,3381,3382,3383,3384,3385,3386,3389,3406,3424,3425,3450,3451,3452,3453,3454,3455,3461,3462,3463,3464,3465,3466,3467,3468,3469,3470,3471,3472,3473,3474,3475,3476,3477,3478,3482,3483,3484,3485,3486,3487,3488,3489,3490,3491,3492,3493,3494,3495,3496,3497,3498,3499,3500,3501,3502,3503,3504,3505,3507,3508,3509,3510,3511,3512,3513,3514,3515,3517,3520,3521,3522,3523,3524,3525,3526,3585,3586,3587,3588,3589,3590,3591,3592,3593,3594,3595,3596,3597,3598,3599,3600,3601,3602,3603,3604,3605,3606,3607,3608,3609,3610,3611,3612,3613,3614,3615,3616,3617,3618,3619,3620,3621,3622,3623,3624,3625,3626,3627,3628,3629,3630,3631,3632,3634,3635,3648,3649,3650,3651,3652,3653,3654,3713,3714,3716,3719,3720,3722,3725,3732,3733,3734,3735,3737,3738,3739,3740,3741,3742,3743,3745,3746,3747,3749,3751,3754,3755,3757,3758,3759,3760,3762,3763,3773,3776,3777,3778,3779,3780,3782,3804,3805,3806,3807,3840,3904,3905,3906,3907,3908,3909,3910,3911,3913,3914,3915,3916,3917,3918,3919,3920,3921,3922,3923,3924,3925,3926,3927,3928,3929,3930,3931,3932,3933,3934,3935,3936,3937,3938,3939,3940,3941,3942,3943,3944,3945,3946,3947,3948,3976,3977,3978,3979,3980,4096,4097,4098,4099,4100,4101,4102,4103,4104,4105,4106,4107,4108,4109,4110,4111,4112,4113,4114,4115,4116,4117,4118,4119,4120,4121,4122,4123,4124,4125,4126,4127,4128,4129,4130,4131,4132,4133,4134,4135,4136,4137,4138,4159,4176,4177,4178,4179,4180,4181,4186,4187,4188,4189,4193,4197,4198,4206,4207,4208,4213,4214,4215,4216,4217,4218,4219,4220,4221,4222,4223,4224,4225,4238,4256,4257,4258,4259,4260,4261,4262,4263,4264,4265,4266,4267,4268,4269,4270,4271,4272,4273,4274,4275,4276,4277,4278,4279,4280,4281,4282,4283,4284,4285,4286,4287,4288,4289,4290,4291,4292,4293,4295,4301,4304,4305,4306,4307,4308,4309,4310,4311,4312,4313,4314,4315,4316,4317,4318,4319,4320,4321,4322,4323,4324,4325,4326,4327,4328,4329,4330,4331,4332,4333,4334,4335,4336,4337,4338,4339,4340,4341,4342,4343,4344,4345,4346,4348,4349,4350,4351,4352,4353,4354,4355,4356,4357,4358,4359,4360,4361,4362,4363,4364,4365,4366,4367,4368,4369,4370,4371,4372,4373,4374,4375,4376,4377,4378,4379,4380,4381,4382,4383,4384,4385,4386,4387,4388,4389,4390,4391,4392,4393,4394,4395,4396,4397,4398,4399,4400,4401,4402,4403,4404,4405,4406,4407,4408,4409,4410,4411,4412,4413,4414,4415,4416,4417,4418,4419,4420,4421,4422,4423,4424,4425,4426,4427,4428,4429,4430,4431,4432,4433,4434,4435,4436,4437,4438,4439,4440,4441,4442,4443,4444,4445,4446,4447,4448,4449,4450,4451,4452,4453,4454,4455,4456,4457,4458,4459,4460,4461,4462,4463,4464,4465,4466,4467,4468,4469,4470,4471,4472,4473,4474,4475,4476,4477,4478,4479,4480,4481,4482,4483,4484,4485,4486,4487,4488,4489,4490,4491,4492,4493,4494,4495,4496,4497,4498,4499,4500,4501,4502,4503,4504,4505,4506,4507,4508,4509,4510,4511,4512,4513,4514,4515,4516,4517,4518,4519,4520,4521,4522,4523,4524,4525,4526,4527,4528,4529,4530,4531,4532,4533,4534,4535,4536,4537,4538,4539,4540,4541,4542,4543,4544,4545,4546,4547,4548,4549,4550,4551,4552,4553,4554,4555,4556,4557,4558,4559,4560,4561,4562,4563,4564,4565,4566,4567,4568,4569,4570,4571,4572,4573,4574,4575,4576,4577,4578,4579,4580,4581,4582,4583,4584,4585,4586,4587,4588,4589,4590,4591,4592,4593,4594,4595,4596,4597,4598,4599,4600,4601,4602,4603,4604,4605,4606,4607,4608,4609,4610,4611,4612,4613,4614,4615,4616,4617,4618,4619,4620,4621,4622,4623,4624,4625,4626,4627,4628,4629,4630,4631,4632,4633,4634,4635,4636,4637,4638,4639,4640,4641,4642,4643,4644,4645,4646,4647,4648,4649,4650,4651,4652,4653,4654,4655,4656,4657,4658,4659,4660,4661,4662,4663,4664,4665,4666,4667,4668,4669,4670,4671,4672,4673,4674,4675,4676,4677,4678,4679,4680,4682,4683,4684,4685,4688,4689,4690,4691,4692,4693,4694,4696,4698,4699,4700,4701,4704,4705,4706,4707,4708,4709,4710,4711,4712,4713,4714,4715,4716,4717,4718,4719,4720,4721,4722,4723,4724,4725,4726,4727,4728,4729,4730,4731,4732,4733,4734,4735,4736,4737,4738,4739,4740,4741,4742,4743,4744,4746,4747,4748,4749,4752,4753,4754,4755,4756,4757,4758,4759,4760,4761,4762,4763,4764,4765,4766,4767,4768,4769,4770,4771,4772,4773,4774,4775,4776,4777,4778,4779,4780,4781,4782,4783,4784,4786,4787,4788,4789,4792,4793,4794,4795,4796,4797,4798,4800,4802,4803,4804,4805,4808,4809,4810,4811,4812,4813,4814,4815,4816,4817,4818,4819,4820,4821,4822,4824,4825,4826,4827,4828,4829,4830,4831,4832,4833,4834,4835,4836,4837,4838,4839,4840,4841,4842,4843,4844,4845,4846,4847,4848,4849,4850,4851,4852,4853,4854,4855,4856,4857,4858,4859,4860,4861,4862,4863,4864,4865,4866,4867,4868,4869,4870,4871,4872,4873,4874,4875,4876,4877,4878,4879,4880,4882,4883,4884,4885,4888,4889,4890,4891,4892,4893,4894,4895,4896,4897,4898,4899,4900,4901,4902,4903,4904,4905,4906,4907,4908,4909,4910,4911,4912,4913,4914,4915,4916,4917,4918,4919,4920,4921,4922,4923,4924,4925,4926,4927,4928,4929,4930,4931,4932,4933,4934,4935,4936,4937,4938,4939,4940,4941,4942,4943,4944,4945,4946,4947,4948,4949,4950,4951,4952,4953,4954,4992,4993,4994,4995,4996,4997,4998,4999,5000,5001,5002,5003,5004,5005,5006,5007,5024,5025,5026,5027,5028,5029,5030,5031,5032,5033,5034,5035,5036,5037,5038,5039,5040,5041,5042,5043,5044,5045,5046,5047,5048,5049,5050,5051,5052,5053,5054,5055,5056,5057,5058,5059,5060,5061,5062,5063,5064,5065,5066,5067,5068,5069,5070,5071,5072,5073,5074,5075,5076,5077,5078,5079,5080,5081,5082,5083,5084,5085,5086,5087,5088,5089,5090,5091,5092,5093,5094,5095,5096,5097,5098,5099,5100,5101,5102,5103,5104,5105,5106,5107,5108,5121,5122,5123,5124,5125,5126,5127,5128,5129,5130,5131,5132,5133,5134,5135,5136,5137,5138,5139,5140,5141,5142,5143,5144,5145,5146,5147,5148,5149,5150,5151,5152,5153,5154,5155,5156,5157,5158,5159,5160,5161,5162,5163,5164,5165,5166,5167,5168,5169,5170,5171,5172,5173,5174,5175,5176,5177,5178,5179,5180,5181,5182,5183,5184,5185,5186,5187,5188,5189,5190,5191,5192,5193,5194,5195,5196,5197,5198,5199,5200,5201,5202,5203,5204,5205,5206,5207,5208,5209,5210,5211,5212,5213,5214,5215,5216,5217,5218,5219,5220,5221,5222,5223,5224,5225,5226,5227,5228,5229,5230,5231,5232,5233,5234,5235,5236,5237,5238,5239,5240,5241,5242,5243,5244,5245,5246,5247,5248,5249,5250,5251,5252,5253,5254,5255,5256,5257,5258,5259,5260,5261,5262,5263,5264,5265,5266,5267,5268,5269,5270,5271,5272,5273,5274,5275,5276,5277,5278,5279,5280,5281,5282,5283,5284,5285,5286,5287,5288,5289,5290,5291,5292,5293,5294,5295,5296,5297,5298,5299,5300,5301,5302,5303,5304,5305,5306,5307,5308,5309,5310,5311,5312,5313,5314,5315,5316,5317,5318,5319,5320,5321,5322,5323,5324,5325,5326,5327,5328,5329,5330,5331,5332,5333,5334,5335,5336,5337,5338,5339,5340,5341,5342,5343,5344,5345,5346,5347,5348,5349,5350,5351,5352,5353,5354,5355,5356,5357,5358,5359,5360,5361,5362,5363,5364,5365,5366,5367,5368,5369,5370,5371,5372,5373,5374,5375,5376,5377,5378,5379,5380,5381,5382,5383,5384,5385,5386,5387,5388,5389,5390,5391,5392,5393,5394,5395,5396,5397,5398,5399,5400,5401,5402,5403,5404,5405,5406,5407,5408,5409,5410,5411,5412,5413,5414,5415,5416,5417,5418,5419,5420,5421,5422,5423,5424,5425,5426,5427,5428,5429,5430,5431,5432,5433,5434,5435,5436,5437,5438,5439,5440,5441,5442,5443,5444,5445,5446,5447,5448,5449,5450,5451,5452,5453,5454,5455,5456,5457,5458,5459,5460,5461,5462,5463,5464,5465,5466,5467,5468,5469,5470,5471,5472,5473,5474,5475,5476,5477,5478,5479,5480,5481,5482,5483,5484,5485,5486,5487,5488,5489,5490,5491,5492,5493,5494,5495,5496,5497,5498,5499,5500,5501,5502,5503,5504,5505,5506,5507,5508,5509,5510,5511,5512,5513,5514,5515,5516,5517,5518,5519,5520,5521,5522,5523,5524,5525,5526,5527,5528,5529,5530,5531,5532,5533,5534,5535,5536,5537,5538,5539,5540,5541,5542,5543,5544,5545,5546,5547,5548,5549,5550,5551,5552,5553,5554,5555,5556,5557,5558,5559,5560,5561,5562,5563,5564,5565,5566,5567,5568,5569,5570,5571,5572,5573,5574,5575,5576,5577,5578,5579,5580,5581,5582,5583,5584,5585,5586,5587,5588,5589,5590,5591,5592,5593,5594,5595,5596,5597,5598,5599,5600,5601,5602,5603,5604,5605,5606,5607,5608,5609,5610,5611,5612,5613,5614,5615,5616,5617,5618,5619,5620,5621,5622,5623,5624,5625,5626,5627,5628,5629,5630,5631,5632,5633,5634,5635,5636,5637,5638,5639,5640,5641,5642,5643,5644,5645,5646,5647,5648,5649,5650,5651,5652,5653,5654,5655,5656,5657,5658,5659,5660,5661,5662,5663,5664,5665,5666,5667,5668,5669,5670,5671,5672,5673,5674,5675,5676,5677,5678,5679,5680,5681,5682,5683,5684,5685,5686,5687,5688,5689,5690,5691,5692,5693,5694,5695,5696,5697,5698,5699,5700,5701,5702,5703,5704,5705,5706,5707,5708,5709,5710,5711,5712,5713,5714,5715,5716,5717,5718,5719,5720,5721,5722,5723,5724,5725,5726,5727,5728,5729,5730,5731,5732,5733,5734,5735,5736,5737,5738,5739,5740,5743,5744,5745,5746,5747,5748,5749,5750,5751,5752,5753,5754,5755,5756,5757,5758,5759,5761,5762,5763,5764,5765,5766,5767,5768,5769,5770,5771,5772,5773,5774,5775,5776,5777,5778,5779,5780,5781,5782,5783,5784,5785,5786,5792,5793,5794,5795,5796,5797,5798,5799,5800,5801,5802,5803,5804,5805,5806,5807,5808,5809,5810,5811,5812,5813,5814,5815,5816,5817,5818,5819,5820,5821,5822,5823,5824,5825,5826,5827,5828,5829,5830,5831,5832,5833,5834,5835,5836,5837,5838,5839,5840,5841,5842,5843,5844,5845,5846,5847,5848,5849,5850,5851,5852,5853,5854,5855,5856,5857,5858,5859,5860,5861,5862,5863,5864,5865,5866,5870,5871,5872,5888,5889,5890,5891,5892,5893,5894,5895,5896,5897,5898,5899,5900,5902,5903,5904,5905,5920,5921,5922,5923,5924,5925,5926,5927,5928,5929,5930,5931,5932,5933,5934,5935,5936,5937,5952,5953,5954,5955,5956,5957,5958,5959,5960,5961,5962,5963,5964,5965,5966,5967,5968,5969,5984,5985,5986,5987,5988,5989,5990,5991,5992,5993,5994,5995,5996,5998,5999,6000,6016,6017,6018,6019,6020,6021,6022,6023,6024,6025,6026,6027,6028,6029,6030,6031,6032,6033,6034,6035,6036,6037,6038,6039,6040,6041,6042,6043,6044,6045,6046,6047,6048,6049,6050,6051,6052,6053,6054,6055,6056,6057,6058,6059,6060,6061,6062,6063,6064,6065,6066,6067,6103,6108,6176,6177,6178,6179,6180,6181,6182,6183,6184,6185,6186,6187,6188,6189,6190,6191,6192,6193,6194,6195,6196,6197,6198,6199,6200,6201,6202,6203,6204,6205,6206,6207,6208,6209,6210,6211,6212,6213,6214,6215,6216,6217,6218,6219,6220,6221,6222,6223,6224,6225,6226,6227,6228,6229,6230,6231,6232,6233,6234,6235,6236,6237,6238,6239,6240,6241,6242,6243,6244,6245,6246,6247,6248,6249,6250,6251,6252,6253,6254,6255,6256,6257,6258,6259,6260,6261,6262,6263,6272,6273,6274,6275,6276,6277,6278,6279,6280,6281,6282,6283,6284,6285,6286,6287,6288,6289,6290,6291,6292,6293,6294,6295,6296,6297,6298,6299,6300,6301,6302,6303,6304,6305,6306,6307,6308,6309,6310,6311,6312,6314,6320,6321,6322,6323,6324,6325,6326,6327,6328,6329,6330,6331,6332,6333,6334,6335,6336,6337,6338,6339,6340,6341,6342,6343,6344,6345,6346,6347,6348,6349,6350,6351,6352,6353,6354,6355,6356,6357,6358,6359,6360,6361,6362,6363,6364,6365,6366,6367,6368,6369,6370,6371,6372,6373,6374,6375,6376,6377,6378,6379,6380,6381,6382,6383,6384,6385,6386,6387,6388,6389,6400,6401,6402,6403,6404,6405,6406,6407,6408,6409,6410,6411,6412,6413,6414,6415,6416,6417,6418,6419,6420,6421,6422,6423,6424,6425,6426,6427,6428,6480,6481,6482,6483,6484,6485,6486,6487,6488,6489,6490,6491,6492,6493,6494,6495,6496,6497,6498,6499,6500,6501,6502,6503,6504,6505,6506,6507,6508,6509,6512,6513,6514,6515,6516,6528,6529,6530,6531,6532,6533,6534,6535,6536,6537,6538,6539,6540,6541,6542,6543,6544,6545,6546,6547,6548,6549,6550,6551,6552,6553,6554,6555,6556,6557,6558,6559,6560,6561,6562,6563,6564,6565,6566,6567,6568,6569,6570,6571,6593,6594,6595,6596,6597,6598,6599,6656,6657,6658,6659,6660,6661,6662,6663,6664,6665,6666,6667,6668,6669,6670,6671,6672,6673,6674,6675,6676,6677,6678,6688,6689,6690,6691,6692,6693,6694,6695,6696,6697,6698,6699,6700,6701,6702,6703,6704,6705,6706,6707,6708,6709,6710,6711,6712,6713,6714,6715,6716,6717,6718,6719,6720,6721,6722,6723,6724,6725,6726,6727,6728,6729,6730,6731,6732,6733,6734,6735,6736,6737,6738,6739,6740,6823,6917,6918,6919,6920,6921,6922,6923,6924,6925,6926,6927,6928,6929,6930,6931,6932,6933,6934,6935,6936,6937,6938,6939,6940,6941,6942,6943,6944,6945,6946,6947,6948,6949,6950,6951,6952,6953,6954,6955,6956,6957,6958,6959,6960,6961,6962,6963,6981,6982,6983,6984,6985,6986,6987,7043,7044,7045,7046,7047,7048,7049,7050,7051,7052,7053,7054,7055,7056,7057,7058,7059,7060,7061,7062,7063,7064,7065,7066,7067,7068,7069,7070,7071,7072,7086,7087,7098,7099,7100,7101,7102,7103,7104,7105,7106,7107,7108,7109,7110,7111,7112,7113,7114,7115,7116,7117,7118,7119,7120,7121,7122,7123,7124,7125,7126,7127,7128,7129,7130,7131,7132,7133,7134,7135,7136,7137,7138,7139,7140,7141,7168,7169,7170,7171,7172,7173,7174,7175,7176,7177,7178,7179,7180,7181,7182,7183,7184,7185,7186,7187,7188,7189,7190,7191,7192,7193,7194,7195,7196,7197,7198,7199,7200,7201,7202,7203,7245,7246,7247,7258,7259,7260,7261,7262,7263,7264,7265,7266,7267,7268,7269,7270,7271,7272,7273,7274,7275,7276,7277,7278,7279,7280,7281,7282,7283,7284,7285,7286,7287,7288,7289,7290,7291,7292,7293,7401,7402,7403,7404,7406,7407,7408,7409,7413,7414,7424,7425,7426,7427,7428,7429,7430,7431,7432,7433,7434,7435,7436,7437,7438,7439,7440,7441,7442,7443,7444,7445,7446,7447,7448,7449,7450,7451,7452,7453,7454,7455,7456,7457,7458,7459,7460,7461,7462,7463,7464,7465,7466,7467,7468,7469,7470,7471,7472,7473,7474,7475,7476,7477,7478,7479,7480,7481,7482,7483,7484,7485,7486,7487,7488,7489,7490,7491,7492,7493,7494,7495,7496,7497,7498,7499,7500,7501,7502,7503,7504,7505,7506,7507,7508,7509,7510,7511,7512,7513,7514,7515,7516,7517,7518,7519,7520,7521,7522,7523,7524,7525,7526,7527,7528,7529,7530,7531,7532,7533,7534,7535,7536,7537,7538,7539,7540,7541,7542,7543,7544,7545,7546,7547,7548,7549,7550,7551,7552,7553,7554,7555,7556,7557,7558,7559,7560,7561,7562,7563,7564,7565,7566,7567,7568,7569,7570,7571,7572,7573,7574,7575,7576,7577,7578,7579,7580,7581,7582,7583,7584,7585,7586,7587,7588,7589,7590,7591,7592,7593,7594,7595,7596,7597,7598,7599,7600,7601,7602,7603,7604,7605,7606,7607,7608,7609,7610,7611,7612,7613,7614,7615,7680,7681,7682,7683,7684,7685,7686,7687,7688,7689,7690,7691,7692,7693,7694,7695,7696,7697,7698,7699,7700,7701,7702,7703,7704,7705,7706,7707,7708,7709,7710,7711,7712,7713,7714,7715,7716,7717,7718,7719,7720,7721,7722,7723,7724,7725,7726,7727,7728,7729,7730,7731,7732,7733,7734,7735,7736,7737,7738,7739,7740,7741,7742,7743,7744,7745,7746,7747,7748,7749,7750,7751,7752,7753,7754,7755,7756,7757,7758,7759,7760,7761,7762,7763,7764,7765,7766,7767,7768,7769,7770,7771,7772,7773,7774,7775,7776,7777,7778,7779,7780,7781,7782,7783,7784,7785,7786,7787,7788,7789,7790,7791,7792,7793,7794,7795,7796,7797,7798,7799,7800,7801,7802,7803,7804,7805,7806,7807,7808,7809,7810,7811,7812,7813,7814,7815,7816,7817,7818,7819,7820,7821,7822,7823,7824,7825,7826,7827,7828,7829,7830,7831,7832,7833,7834,7835,7836,7837,7838,7839,7840,7841,7842,7843,7844,7845,7846,7847,7848,7849,7850,7851,7852,7853,7854,7855,7856,7857,7858,7859,7860,7861,7862,7863,7864,7865,7866,7867,7868,7869,7870,7871,7872,7873,7874,7875,7876,7877,7878,7879,7880,7881,7882,7883,7884,7885,7886,7887,7888,7889,7890,7891,7892,7893,7894,7895,7896,7897,7898,7899,7900,7901,7902,7903,7904,7905,7906,7907,7908,7909,7910,7911,7912,7913,7914,7915,7916,7917,7918,7919,7920,7921,7922,7923,7924,7925,7926,7927,7928,7929,7930,7931,7932,7933,7934,7935,7936,7937,7938,7939,7940,7941,7942,7943,7944,7945,7946,7947,7948,7949,7950,7951,7952,7953,7954,7955,7956,7957,7960,7961,7962,7963,7964,7965,7968,7969,7970,7971,7972,7973,7974,7975,7976,7977,7978,7979,7980,7981,7982,7983,7984,7985,7986,7987,7988,7989,7990,7991,7992,7993,7994,7995,7996,7997,7998,7999,8000,8001,8002,8003,8004,8005,8008,8009,8010,8011,8012,8013,8016,8017,8018,8019,8020,8021,8022,8023,8025,8027,8029,8031,8032,8033,8034,8035,8036,8037,8038,8039,8040,8041,8042,8043,8044,8045,8046,8047,8048,8049,8050,8051,8052,8053,8054,8055,8056,8057,8058,8059,8060,8061,8064,8065,8066,8067,8068,8069,8070,8071,8072,8073,8074,8075,8076,8077,8078,8079,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8102,8103,8104,8105,8106,8107,8108,8109,8110,8111,8112,8113,8114,8115,8116,8118,8119,8120,8121,8122,8123,8124,8126,8130,8131,8132,8134,8135,8136,8137,8138,8139,8140,8144,8145,8146,8147,8150,8151,8152,8153,8154,8155,8160,8161,8162,8163,8164,8165,8166,8167,8168,8169,8170,8171,8172,8178,8179,8180,8182,8183,8184,8185,8186,8187,8188,8305,8319,8336,8337,8338,8339,8340,8341,8342,8343,8344,8345,8346,8347,8348,8450,8455,8458,8459,8460,8461,8462,8463,8464,8465,8466,8467,8469,8473,8474,8475,8476,8477,8484,8486,8488,8490,8491,8492,8493,8495,8496,8497,8498,8499,8500,8501,8502,8503,8504,8505,8508,8509,8510,8511,8517,8518,8519,8520,8521,8526,8544,8545,8546,8547,8548,8549,8550,8551,8552,8553,8554,8555,8556,8557,8558,8559,8560,8561,8562,8563,8564,8565,8566,8567,8568,8569,8570,8571,8572,8573,8574,8575,8576,8577,8578,8579,8580,8581,8582,8583,8584,11264,11265,11266,11267,11268,11269,11270,11271,11272,11273,11274,11275,11276,11277,11278,11279,11280,11281,11282,11283,11284,11285,11286,11287,11288,11289,11290,11291,11292,11293,11294,11295,11296,11297,11298,11299,11300,11301,11302,11303,11304,11305,11306,11307,11308,11309,11310,11312,11313,11314,11315,11316,11317,11318,11319,11320,11321,11322,11323,11324,11325,11326,11327,11328,11329,11330,11331,11332,11333,11334,11335,11336,11337,11338,11339,11340,11341,11342,11343,11344,11345,11346,11347,11348,11349,11350,11351,11352,11353,11354,11355,11356,11357,11358,11360,11361,11362,11363,11364,11365,11366,11367,11368,11369,11370,11371,11372,11373,11374,11375,11376,11377,11378,11379,11380,11381,11382,11383,11384,11385,11386,11387,11388,11389,11390,11391,11392,11393,11394,11395,11396,11397,11398,11399,11400,11401,11402,11403,11404,11405,11406,11407,11408,11409,11410,11411,11412,11413,11414,11415,11416,11417,11418,11419,11420,11421,11422,11423,11424,11425,11426,11427,11428,11429,11430,11431,11432,11433,11434,11435,11436,11437,11438,11439,11440,11441,11442,11443,11444,11445,11446,11447,11448,11449,11450,11451,11452,11453,11454,11455,11456,11457,11458,11459,11460,11461,11462,11463,11464,11465,11466,11467,11468,11469,11470,11471,11472,11473,11474,11475,11476,11477,11478,11479,11480,11481,11482,11483,11484,11485,11486,11487,11488,11489,11490,11491,11492,11499,11500,11501,11502,11506,11507,11520,11521,11522,11523,11524,11525,11526,11527,11528,11529,11530,11531,11532,11533,11534,11535,11536,11537,11538,11539,11540,11541,11542,11543,11544,11545,11546,11547,11548,11549,11550,11551,11552,11553,11554,11555,11556,11557,11559,11565,11568,11569,11570,11571,11572,11573,11574,11575,11576,11577,11578,11579,11580,11581,11582,11583,11584,11585,11586,11587,11588,11589,11590,11591,11592,11593,11594,11595,11596,11597,11598,11599,11600,11601,11602,11603,11604,11605,11606,11607,11608,11609,11610,11611,11612,11613,11614,11615,11616,11617,11618,11619,11620,11621,11622,11623,11631,11648,11649,11650,11651,11652,11653,11654,11655,11656,11657,11658,11659,11660,11661,11662,11663,11664,11665,11666,11667,11668,11669,11670,11680,11681,11682,11683,11684,11685,11686,11688,11689,11690,11691,11692,11693,11694,11696,11697,11698,11699,11700,11701,11702,11704,11705,11706,11707,11708,11709,11710,11712,11713,11714,11715,11716,11717,11718,11720,11721,11722,11723,11724,11725,11726,11728,11729,11730,11731,11732,11733,11734,11736,11737,11738,11739,11740,11741,11742,11823,12293,12294,12295,12321,12322,12323,12324,12325,12326,12327,12328,12329,12337,12338,12339,12340,12341,12344,12345,12346,12347,12348,12353,12354,12355,12356,12357,12358,12359,12360,12361,12362,12363,12364,12365,12366,12367,12368,12369,12370,12371,12372,12373,12374,12375,12376,12377,12378,12379,12380,12381,12382,12383,12384,12385,12386,12387,12388,12389,12390,12391,12392,12393,12394,12395,12396,12397,12398,12399,12400,12401,12402,12403,12404,12405,12406,12407,12408,12409,12410,12411,12412,12413,12414,12415,12416,12417,12418,12419,12420,12421,12422,12423,12424,12425,12426,12427,12428,12429,12430,12431,12432,12433,12434,12435,12436,12437,12438,12445,12446,12447,12449,12450,12451,12452,12453,12454,12455,12456,12457,12458,12459,12460,12461,12462,12463,12464,12465,12466,12467,12468,12469,12470,12471,12472,12473,12474,12475,12476,12477,12478,12479,12480,12481,12482,12483,12484,12485,12486,12487,12488,12489,12490,12491,12492,12493,12494,12495,12496,12497,12498,12499,12500,12501,12502,12503,12504,12505,12506,12507,12508,12509,12510,12511,12512,12513,12514,12515,12516,12517,12518,12519,12520,12521,12522,12523,12524,12525,12526,12527,12528,12529,12530,12531,12532,12533,12534,12535,12536,12537,12538,12540,12541,12542,12543,12549,12550,12551,12552,12553,12554,12555,12556,12557,12558,12559,12560,12561,12562,12563,12564,12565,12566,12567,12568,12569,12570,12571,12572,12573,12574,12575,12576,12577,12578,12579,12580,12581,12582,12583,12584,12585,12586,12587,12588,12589,12593,12594,12595,12596,12597,12598,12599,12600,12601,12602,12603,12604,12605,12606,12607,12608,12609,12610,12611,12612,12613,12614,12615,12616,12617,12618,12619,12620,12621,12622,12623,12624,12625,12626,12627,12628,12629,12630,12631,12632,12633,12634,12635,12636,12637,12638,12639,12640,12641,12642,12643,12644,12645,12646,12647,12648,12649,12650,12651,12652,12653,12654,12655,12656,12657,12658,12659,12660,12661,12662,12663,12664,12665,12666,12667,12668,12669,12670,12671,12672,12673,12674,12675,12676,12677,12678,12679,12680,12681,12682,12683,12684,12685,12686,12704,12705,12706,12707,12708,12709,12710,12711,12712,12713,12714,12715,12716,12717,12718,12719,12720,12721,12722,12723,12724,12725,12726,12727,12728,12729,12730,12784,12785,12786,12787,12788,12789,12790,12791,12792,12793,12794,12795,12796,12797,12798,12799,13312,13313,13314,13315,13316,13317,13318,13319,13320,13321,13322,13323,13324,13325,13326,13327,13328,13329,13330,13331,13332,13333,13334,13335,13336,13337,13338,13339,13340,13341,13342,13343,13344,13345,13346,13347,13348,13349,13350,13351,13352,13353,13354,13355,13356,13357,13358,13359,13360,13361,13362,13363,13364,13365,13366,13367,13368,13369,13370,13371,13372,13373,13374,13375,13376,13377,13378,13379,13380,13381,13382,13383,13384,13385,13386,13387,13388,13389,13390,13391,13392,13393,13394,13395,13396,13397,13398,13399,13400,13401,13402,13403,13404,13405,13406,13407,13408,13409,13410,13411,13412,13413,13414,13415,13416,13417,13418,13419,13420,13421,13422,13423,13424,13425,13426,13427,13428,13429,13430,13431,13432,13433,13434,13435,13436,13437,13438,13439,13440,13441,13442,13443,13444,13445,13446,13447,13448,13449,13450,13451,13452,13453,13454,13455,13456,13457,13458,13459,13460,13461,13462,13463,13464,13465,13466,13467,13468,13469,13470,13471,13472,13473,13474,13475,13476,13477,13478,13479,13480,13481,13482,13483,13484,13485,13486,13487,13488,13489,13490,13491,13492,13493,13494,13495,13496,13497,13498,13499,13500,13501,13502,13503,13504,13505,13506,13507,13508,13509,13510,13511,13512,13513,13514,13515,13516,13517,13518,13519,13520,13521,13522,13523,13524,13525,13526,13527,13528,13529,13530,13531,13532,13533,13534,13535,13536,13537,13538,13539,13540,13541,13542,13543,13544,13545,13546,13547,13548,13549,13550,13551,13552,13553,13554,13555,13556,13557,13558,13559,13560,13561,13562,13563,13564,13565,13566,13567,13568,13569,13570,13571,13572,13573,13574,13575,13576,13577,13578,13579,13580,13581,13582,13583,13584,13585,13586,13587,13588,13589,13590,13591,13592,13593,13594,13595,13596,13597,13598,13599,13600,13601,13602,13603,13604,13605,13606,13607,13608,13609,13610,13611,13612,13613,13614,13615,13616,13617,13618,13619,13620,13621,13622,13623,13624,13625,13626,13627,13628,13629,13630,13631,13632,13633,13634,13635,13636,13637,13638,13639,13640,13641,13642,13643,13644,13645,13646,13647,13648,13649,13650,13651,13652,13653,13654,13655,13656,13657,13658,13659,13660,13661,13662,13663,13664,13665,13666,13667,13668,13669,13670,13671,13672,13673,13674,13675,13676,13677,13678,13679,13680,13681,13682,13683,13684,13685,13686,13687,13688,13689,13690,13691,13692,13693,13694,13695,13696,13697,13698,13699,13700,13701,13702,13703,13704,13705,13706,13707,13708,13709,13710,13711,13712,13713,13714,13715,13716,13717,13718,13719,13720,13721,13722,13723,13724,13725,13726,13727,13728,13729,13730,13731,13732,13733,13734,13735,13736,13737,13738,13739,13740,13741,13742,13743,13744,13745,13746,13747,13748,13749,13750,13751,13752,13753,13754,13755,13756,13757,13758,13759,13760,13761,13762,13763,13764,13765,13766,13767,13768,13769,13770,13771,13772,13773,13774,13775,13776,13777,13778,13779,13780,13781,13782,13783,13784,13785,13786,13787,13788,13789,13790,13791,13792,13793,13794,13795,13796,13797,13798,13799,13800,13801,13802,13803,13804,13805,13806,13807,13808,13809,13810,13811,13812,13813,13814,13815,13816,13817,13818,13819,13820,13821,13822,13823,13824,13825,13826,13827,13828,13829,13830,13831,13832,13833,13834,13835,13836,13837,13838,13839,13840,13841,13842,13843,13844,13845,13846,13847,13848,13849,13850,13851,13852,13853,13854,13855,13856,13857,13858,13859,13860,13861,13862,13863,13864,13865,13866,13867,13868,13869,13870,13871,13872,13873,13874,13875,13876,13877,13878,13879,13880,13881,13882,13883,13884,13885,13886,13887,13888,13889,13890,13891,13892,13893,13894,13895,13896,13897,13898,13899,13900,13901,13902,13903,13904,13905,13906,13907,13908,13909,13910,13911,13912,13913,13914,13915,13916,13917,13918,13919,13920,13921,13922,13923,13924,13925,13926,13927,13928,13929,13930,13931,13932,13933,13934,13935,13936,13937,13938,13939,13940,13941,13942,13943,13944,13945,13946,13947,13948,13949,13950,13951,13952,13953,13954,13955,13956,13957,13958,13959,13960,13961,13962,13963,13964,13965,13966,13967,13968,13969,13970,13971,13972,13973,13974,13975,13976,13977,13978,13979,13980,13981,13982,13983,13984,13985,13986,13987,13988,13989,13990,13991,13992,13993,13994,13995,13996,13997,13998,13999,14000,14001,14002,14003,14004,14005,14006,14007,14008,14009,14010,14011,14012,14013,14014,14015,14016,14017,14018,14019,14020,14021,14022,14023,14024,14025,14026,14027,14028,14029,14030,14031,14032,14033,14034,14035,14036,14037,14038,14039,14040,14041,14042,14043,14044,14045,14046,14047,14048,14049,14050,14051,14052,14053,14054,14055,14056,14057,14058,14059,14060,14061,14062,14063,14064,14065,14066,14067,14068,14069,14070,14071,14072,14073,14074,14075,14076,14077,14078,14079,14080,14081,14082,14083,14084,14085,14086,14087,14088,14089,14090,14091,14092,14093,14094,14095,14096,14097,14098,14099,14100,14101,14102,14103,14104,14105,14106,14107,14108,14109,14110,14111,14112,14113,14114,14115,14116,14117,14118,14119,14120,14121,14122,14123,14124,14125,14126,14127,14128,14129,14130,14131,14132,14133,14134,14135,14136,14137,14138,14139,14140,14141,14142,14143,14144,14145,14146,14147,14148,14149,14150,14151,14152,14153,14154,14155,14156,14157,14158,14159,14160,14161,14162,14163,14164,14165,14166,14167,14168,14169,14170,14171,14172,14173,14174,14175,14176,14177,14178,14179,14180,14181,14182,14183,14184,14185,14186,14187,14188,14189,14190,14191,14192,14193,14194,14195,14196,14197,14198,14199,14200,14201,14202,14203,14204,14205,14206,14207,14208,14209,14210,14211,14212,14213,14214,14215,14216,14217,14218,14219,14220,14221,14222,14223,14224,14225,14226,14227,14228,14229,14230,14231,14232,14233,14234,14235,14236,14237,14238,14239,14240,14241,14242,14243,14244,14245,14246,14247,14248,14249,14250,14251,14252,14253,14254,14255,14256,14257,14258,14259,14260,14261,14262,14263,14264,14265,14266,14267,14268,14269,14270,14271,14272,14273,14274,14275,14276,14277,14278,14279,14280,14281,14282,14283,14284,14285,14286,14287,14288,14289,14290,14291,14292,14293,14294,14295,14296,14297,14298,14299,14300,14301,14302,14303,14304,14305,14306,14307,14308,14309,14310,14311,14312,14313,14314,14315,14316,14317,14318,14319,14320,14321,14322,14323,14324,14325,14326,14327,14328,14329,14330,14331,14332,14333,14334,14335,14336,14337,14338,14339,14340,14341,14342,14343,14344,14345,14346,14347,14348,14349,14350,14351,14352,14353,14354,14355,14356,14357,14358,14359,14360,14361,14362,14363,14364,14365,14366,14367,14368,14369,14370,14371,14372,14373,14374,14375,14376,14377,14378,14379,14380,14381,14382,14383,14384,14385,14386,14387,14388,14389,14390,14391,14392,14393,14394,14395,14396,14397,14398,14399,14400,14401,14402,14403,14404,14405,14406,14407,14408,14409,14410,14411,14412,14413,14414,14415,14416,14417,14418,14419,14420,14421,14422,14423,14424,14425,14426,14427,14428,14429,14430,14431,14432,14433,14434,14435,14436,14437,14438,14439,14440,14441,14442,14443,14444,14445,14446,14447,14448,14449,14450,14451,14452,14453,14454,14455,14456,14457,14458,14459,14460,14461,14462,14463,14464,14465,14466,14467,14468,14469,14470,14471,14472,14473,14474,14475,14476,14477,14478,14479,14480,14481,14482,14483,14484,14485,14486,14487,14488,14489,14490,14491,14492,14493,14494,14495,14496,14497,14498,14499,14500,14501,14502,14503,14504,14505,14506,14507,14508,14509,14510,14511,14512,14513,14514,14515,14516,14517,14518,14519,14520,14521,14522,14523,14524,14525,14526,14527,14528,14529,14530,14531,14532,14533,14534,14535,14536,14537,14538,14539,14540,14541,14542,14543,14544,14545,14546,14547,14548,14549,14550,14551,14552,14553,14554,14555,14556,14557,14558,14559,14560,14561,14562,14563,14564,14565,14566,14567,14568,14569,14570,14571,14572,14573,14574,14575,14576,14577,14578,14579,14580,14581,14582,14583,14584,14585,14586,14587,14588,14589,14590,14591,14592,14593,14594,14595,14596,14597,14598,14599,14600,14601,14602,14603,14604,14605,14606,14607,14608,14609,14610,14611,14612,14613,14614,14615,14616,14617,14618,14619,14620,14621,14622,14623,14624,14625,14626,14627,14628,14629,14630,14631,14632,14633,14634,14635,14636,14637,14638,14639,14640,14641,14642,14643,14644,14645,14646,14647,14648,14649,14650,14651,14652,14653,14654,14655,14656,14657,14658,14659,14660,14661,14662,14663,14664,14665,14666,14667,14668,14669,14670,14671,14672,14673,14674,14675,14676,14677,14678,14679,14680,14681,14682,14683,14684,14685,14686,14687,14688,14689,14690,14691,14692,14693,14694,14695,14696,14697,14698,14699,14700,14701,14702,14703,14704,14705,14706,14707,14708,14709,14710,14711,14712,14713,14714,14715,14716,14717,14718,14719,14720,14721,14722,14723,14724,14725,14726,14727,14728,14729,14730,14731,14732,14733,14734,14735,14736,14737,14738,14739,14740,14741,14742,14743,14744,14745,14746,14747,14748,14749,14750,14751,14752,14753,14754,14755,14756,14757,14758,14759,14760,14761,14762,14763,14764,14765,14766,14767,14768,14769,14770,14771,14772,14773,14774,14775,14776,14777,14778,14779,14780,14781,14782,14783,14784,14785,14786,14787,14788,14789,14790,14791,14792,14793,14794,14795,14796,14797,14798,14799,14800,14801,14802,14803,14804,14805,14806,14807,14808,14809,14810,14811,14812,14813,14814,14815,14816,14817,14818,14819,14820,14821,14822,14823,14824,14825,14826,14827,14828,14829,14830,14831,14832,14833,14834,14835,14836,14837,14838,14839,14840,14841,14842,14843,14844,14845,14846,14847,14848,14849,14850,14851,14852,14853,14854,14855,14856,14857,14858,14859,14860,14861,14862,14863,14864,14865,14866,14867,14868,14869,14870,14871,14872,14873,14874,14875,14876,14877,14878,14879,14880,14881,14882,14883,14884,14885,14886,14887,14888,14889,14890,14891,14892,14893,14894,14895,14896,14897,14898,14899,14900,14901,14902,14903,14904,14905,14906,14907,14908,14909,14910,14911,14912,14913,14914,14915,14916,14917,14918,14919,14920,14921,14922,14923,14924,14925,14926,14927,14928,14929,14930,14931,14932,14933,14934,14935,14936,14937,14938,14939,14940,14941,14942,14943,14944,14945,14946,14947,14948,14949,14950,14951,14952,14953,14954,14955,14956,14957,14958,14959,14960,14961,14962,14963,14964,14965,14966,14967,14968,14969,14970,14971,14972,14973,14974,14975,14976,14977,14978,14979,14980,14981,14982,14983,14984,14985,14986,14987,14988,14989,14990,14991,14992,14993,14994,14995,14996,14997,14998,14999,15000,15001,15002,15003,15004,15005,15006,15007,15008,15009,15010,15011,15012,15013,15014,15015,15016,15017,15018,15019,15020,15021,15022,15023,15024,15025,15026,15027,15028,15029,15030,15031,15032,15033,15034,15035,15036,15037,15038,15039,15040,15041,15042,15043,15044,15045,15046,15047,15048,15049,15050,15051,15052,15053,15054,15055,15056,15057,15058,15059,15060,15061,15062,15063,15064,15065,15066,15067,15068,15069,15070,15071,15072,15073,15074,15075,15076,15077,15078,15079,15080,15081,15082,15083,15084,15085,15086,15087,15088,15089,15090,15091,15092,15093,15094,15095,15096,15097,15098,15099,15100,15101,15102,15103,15104,15105,15106,15107,15108,15109,15110,15111,15112,15113,15114,15115,15116,15117,15118,15119,15120,15121,15122,15123,15124,15125,15126,15127,15128,15129,15130,15131,15132,15133,15134,15135,15136,15137,15138,15139,15140,15141,15142,15143,15144,15145,15146,15147,15148,15149,15150,15151,15152,15153,15154,15155,15156,15157,15158,15159,15160,15161,15162,15163,15164,15165,15166,15167,15168,15169,15170,15171,15172,15173,15174,15175,15176,15177,15178,15179,15180,15181,15182,15183,15184,15185,15186,15187,15188,15189,15190,15191,15192,15193,15194,15195,15196,15197,15198,15199,15200,15201,15202,15203,15204,15205,15206,15207,15208,15209,15210,15211,15212,15213,15214,15215,15216,15217,15218,15219,15220,15221,15222,15223,15224,15225,15226,15227,15228,15229,15230,15231,15232,15233,15234,15235,15236,15237,15238,15239,15240,15241,15242,15243,15244,15245,15246,15247,15248,15249,15250,15251,15252,15253,15254,15255,15256,15257,15258,15259,15260,15261,15262,15263,15264,15265,15266,15267,15268,15269,15270,15271,15272,15273,15274,15275,15276,15277,15278,15279,15280,15281,15282,15283,15284,15285,15286,15287,15288,15289,15290,15291,15292,15293,15294,15295,15296,15297,15298,15299,15300,15301,15302,15303,15304,15305,15306,15307,15308,15309,15310,15311,15312,15313,15314,15315,15316,15317,15318,15319,15320,15321,15322,15323,15324,15325,15326,15327,15328,15329,15330,15331,15332,15333,15334,15335,15336,15337,15338,15339,15340,15341,15342,15343,15344,15345,15346,15347,15348,15349,15350,15351,15352,15353,15354,15355,15356,15357,15358,15359,15360,15361,15362,15363,15364,15365,15366,15367,15368,15369,15370,15371,15372,15373,15374,15375,15376,15377,15378,15379,15380,15381,15382,15383,15384,15385,15386,15387,15388,15389,15390,15391,15392,15393,15394,15395,15396,15397,15398,15399,15400,15401,15402,15403,15404,15405,15406,15407,15408,15409,15410,15411,15412,15413,15414,15415,15416,15417,15418,15419,15420,15421,15422,15423,15424,15425,15426,15427,15428,15429,15430,15431,15432,15433,15434,15435,15436,15437,15438,15439,15440,15441,15442,15443,15444,15445,15446,15447,15448,15449,15450,15451,15452,15453,15454,15455,15456,15457,15458,15459,15460,15461,15462,15463,15464,15465,15466,15467,15468,15469,15470,15471,15472,15473,15474,15475,15476,15477,15478,15479,15480,15481,15482,15483,15484,15485,15486,15487,15488,15489,15490,15491,15492,15493,15494,15495,15496,15497,15498,15499,15500,15501,15502,15503,15504,15505,15506,15507,15508,15509,15510,15511,15512,15513,15514,15515,15516,15517,15518,15519,15520,15521,15522,15523,15524,15525,15526,15527,15528,15529,15530,15531,15532,15533,15534,15535,15536,15537,15538,15539,15540,15541,15542,15543,15544,15545,15546,15547,15548,15549,15550,15551,15552,15553,15554,15555,15556,15557,15558,15559,15560,15561,15562,15563,15564,15565,15566,15567,15568,15569,15570,15571,15572,15573,15574,15575,15576,15577,15578,15579,15580,15581,15582,15583,15584,15585,15586,15587,15588,15589,15590,15591,15592,15593,15594,15595,15596,15597,15598,15599,15600,15601,15602,15603,15604,15605,15606,15607,15608,15609,15610,15611,15612,15613,15614,15615,15616,15617,15618,15619,15620,15621,15622,15623,15624,15625,15626,15627,15628,15629,15630,15631,15632,15633,15634,15635,15636,15637,15638,15639,15640,15641,15642,15643,15644,15645,15646,15647,15648,15649,15650,15651,15652,15653,15654,15655,15656,15657,15658,15659,15660,15661,15662,15663,15664,15665,15666,15667,15668,15669,15670,15671,15672,15673,15674,15675,15676,15677,15678,15679,15680,15681,15682,15683,15684,15685,15686,15687,15688,15689,15690,15691,15692,15693,15694,15695,15696,15697,15698,15699,15700,15701,15702,15703,15704,15705,15706,15707,15708,15709,15710,15711,15712,15713,15714,15715,15716,15717,15718,15719,15720,15721,15722,15723,15724,15725,15726,15727,15728,15729,15730,15731,15732,15733,15734,15735,15736,15737,15738,15739,15740,15741,15742,15743,15744,15745,15746,15747,15748,15749,15750,15751,15752,15753,15754,15755,15756,15757,15758,15759,15760,15761,15762,15763,15764,15765,15766,15767,15768,15769,15770,15771,15772,15773,15774,15775,15776,15777,15778,15779,15780,15781,15782,15783,15784,15785,15786,15787,15788,15789,15790,15791,15792,15793,15794,15795,15796,15797,15798,15799,15800,15801,15802,15803,15804,15805,15806,15807,15808,15809,15810,15811,15812,15813,15814,15815,15816,15817,15818,15819,15820,15821,15822,15823,15824,15825,15826,15827,15828,15829,15830,15831,15832,15833,15834,15835,15836,15837,15838,15839,15840,15841,15842,15843,15844,15845,15846,15847,15848,15849,15850,15851,15852,15853,15854,15855,15856,15857,15858,15859,15860,15861,15862,15863,15864,15865,15866,15867,15868,15869,15870,15871,15872,15873,15874,15875,15876,15877,15878,15879,15880,15881,15882,15883,15884,15885,15886,15887,15888,15889,15890,15891,15892,15893,15894,15895,15896,15897,15898,15899,15900,15901,15902,15903,15904,15905,15906,15907,15908,15909,15910,15911,15912,15913,15914,15915,15916,15917,15918,15919,15920,15921,15922,15923,15924,15925,15926,15927,15928,15929,15930,15931,15932,15933,15934,15935,15936,15937,15938,15939,15940,15941,15942,15943,15944,15945,15946,15947,15948,15949,15950,15951,15952,15953,15954,15955,15956,15957,15958,15959,15960,15961,15962,15963,15964,15965,15966,15967,15968,15969,15970,15971,15972,15973,15974,15975,15976,15977,15978,15979,15980,15981,15982,15983,15984,15985,15986,15987,15988,15989,15990,15991,15992,15993,15994,15995,15996,15997,15998,15999,16000,16001,16002,16003,16004,16005,16006,16007,16008,16009,16010,16011,16012,16013,16014,16015,16016,16017,16018,16019,16020,16021,16022,16023,16024,16025,16026,16027,16028,16029,16030,16031,16032,16033,16034,16035,16036,16037,16038,16039,16040,16041,16042,16043,16044,16045,16046,16047,16048,16049,16050,16051,16052,16053,16054,16055,16056,16057,16058,16059,16060,16061,16062,16063,16064,16065,16066,16067,16068,16069,16070,16071,16072,16073,16074,16075,16076,16077,16078,16079,16080,16081,16082,16083,16084,16085,16086,16087,16088,16089,16090,16091,16092,16093,16094,16095,16096,16097,16098,16099,16100,16101,16102,16103,16104,16105,16106,16107,16108,16109,16110,16111,16112,16113,16114,16115,16116,16117,16118,16119,16120,16121,16122,16123,16124,16125,16126,16127,16128,16129,16130,16131,16132,16133,16134,16135,16136,16137,16138,16139,16140,16141,16142,16143,16144,16145,16146,16147,16148,16149,16150,16151,16152,16153,16154,16155,16156,16157,16158,16159,16160,16161,16162,16163,16164,16165,16166,16167,16168,16169,16170,16171,16172,16173,16174,16175,16176,16177,16178,16179,16180,16181,16182,16183,16184,16185,16186,16187,16188,16189,16190,16191,16192,16193,16194,16195,16196,16197,16198,16199,16200,16201,16202,16203,16204,16205,16206,16207,16208,16209,16210,16211,16212,16213,16214,16215,16216,16217,16218,16219,16220,16221,16222,16223,16224,16225,16226,16227,16228,16229,16230,16231,16232,16233,16234,16235,16236,16237,16238,16239,16240,16241,16242,16243,16244,16245,16246,16247,16248,16249,16250,16251,16252,16253,16254,16255,16256,16257,16258,16259,16260,16261,16262,16263,16264,16265,16266,16267,16268,16269,16270,16271,16272,16273,16274,16275,16276,16277,16278,16279,16280,16281,16282,16283,16284,16285,16286,16287,16288,16289,16290,16291,16292,16293,16294,16295,16296,16297,16298,16299,16300,16301,16302,16303,16304,16305,16306,16307,16308,16309,16310,16311,16312,16313,16314,16315,16316,16317,16318,16319,16320,16321,16322,16323,16324,16325,16326,16327,16328,16329,16330,16331,16332,16333,16334,16335,16336,16337,16338,16339,16340,16341,16342,16343,16344,16345,16346,16347,16348,16349,16350,16351,16352,16353,16354,16355,16356,16357,16358,16359,16360,16361,16362,16363,16364,16365,16366,16367,16368,16369,16370,16371,16372,16373,16374,16375,16376,16377,16378,16379,16380,16381,16382,16383,16384,16385,16386,16387,16388,16389,16390,16391,16392,16393,16394,16395,16396,16397,16398,16399,16400,16401,16402,16403,16404,16405,16406,16407,16408,16409,16410,16411,16412,16413,16414,16415,16416,16417,16418,16419,16420,16421,16422,16423,16424,16425,16426,16427,16428,16429,16430,16431,16432,16433,16434,16435,16436,16437,16438,16439,16440,16441,16442,16443,16444,16445,16446,16447,16448,16449,16450,16451,16452,16453,16454,16455,16456,16457,16458,16459,16460,16461,16462,16463,16464,16465,16466,16467,16468,16469,16470,16471,16472,16473,16474,16475,16476,16477,16478,16479,16480,16481,16482,16483,16484,16485,16486,16487,16488,16489,16490,16491,16492,16493,16494,16495,16496,16497,16498,16499,16500,16501,16502,16503,16504,16505,16506,16507,16508,16509,16510,16511,16512,16513,16514,16515,16516,16517,16518,16519,16520,16521,16522,16523,16524,16525,16526,16527,16528,16529,16530,16531,16532,16533,16534,16535,16536,16537,16538,16539,16540,16541,16542,16543,16544,16545,16546,16547,16548,16549,16550,16551,16552,16553,16554,16555,16556,16557,16558,16559,16560,16561,16562,16563,16564,16565,16566,16567,16568,16569,16570,16571,16572,16573,16574,16575,16576,16577,16578,16579,16580,16581,16582,16583,16584,16585,16586,16587,16588,16589,16590,16591,16592,16593,16594,16595,16596,16597,16598,16599,16600,16601,16602,16603,16604,16605,16606,16607,16608,16609,16610,16611,16612,16613,16614,16615,16616,16617,16618,16619,16620,16621,16622,16623,16624,16625,16626,16627,16628,16629,16630,16631,16632,16633,16634,16635,16636,16637,16638,16639,16640,16641,16642,16643,16644,16645,16646,16647,16648,16649,16650,16651,16652,16653,16654,16655,16656,16657,16658,16659,16660,16661,16662,16663,16664,16665,16666,16667,16668,16669,16670,16671,16672,16673,16674,16675,16676,16677,16678,16679,16680,16681,16682,16683,16684,16685,16686,16687,16688,16689,16690,16691,16692,16693,16694,16695,16696,16697,16698,16699,16700,16701,16702,16703,16704,16705,16706,16707,16708,16709,16710,16711,16712,16713,16714,16715,16716,16717,16718,16719,16720,16721,16722,16723,16724,16725,16726,16727,16728,16729,16730,16731,16732,16733,16734,16735,16736,16737,16738,16739,16740,16741,16742,16743,16744,16745,16746,16747,16748,16749,16750,16751,16752,16753,16754,16755,16756,16757,16758,16759,16760,16761,16762,16763,16764,16765,16766,16767,16768,16769,16770,16771,16772,16773,16774,16775,16776,16777,16778,16779,16780,16781,16782,16783,16784,16785,16786,16787,16788,16789,16790,16791,16792,16793,16794,16795,16796,16797,16798,16799,16800,16801,16802,16803,16804,16805,16806,16807,16808,16809,16810,16811,16812,16813,16814,16815,16816,16817,16818,16819,16820,16821,16822,16823,16824,16825,16826,16827,16828,16829,16830,16831,16832,16833,16834,16835,16836,16837,16838,16839,16840,16841,16842,16843,16844,16845,16846,16847,16848,16849,16850,16851,16852,16853,16854,16855,16856,16857,16858,16859,16860,16861,16862,16863,16864,16865,16866,16867,16868,16869,16870,16871,16872,16873,16874,16875,16876,16877,16878,16879,16880,16881,16882,16883,16884,16885,16886,16887,16888,16889,16890,16891,16892,16893,16894,16895,16896,16897,16898,16899,16900,16901,16902,16903,16904,16905,16906,16907,16908,16909,16910,16911,16912,16913,16914,16915,16916,16917,16918,16919,16920,16921,16922,16923,16924,16925,16926,16927,16928,16929,16930,16931,16932,16933,16934,16935,16936,16937,16938,16939,16940,16941,16942,16943,16944,16945,16946,16947,16948,16949,16950,16951,16952,16953,16954,16955,16956,16957,16958,16959,16960,16961,16962,16963,16964,16965,16966,16967,16968,16969,16970,16971,16972,16973,16974,16975,16976,16977,16978,16979,16980,16981,16982,16983,16984,16985,16986,16987,16988,16989,16990,16991,16992,16993,16994,16995,16996,16997,16998,16999,17000,17001,17002,17003,17004,17005,17006,17007,17008,17009,17010,17011,17012,17013,17014,17015,17016,17017,17018,17019,17020,17021,17022,17023,17024,17025,17026,17027,17028,17029,17030,17031,17032,17033,17034,17035,17036,17037,17038,17039,17040,17041,17042,17043,17044,17045,17046,17047,17048,17049,17050,17051,17052,17053,17054,17055,17056,17057,17058,17059,17060,17061,17062,17063,17064,17065,17066,17067,17068,17069,17070,17071,17072,17073,17074,17075,17076,17077,17078,17079,17080,17081,17082,17083,17084,17085,17086,17087,17088,17089,17090,17091,17092,17093,17094,17095,17096,17097,17098,17099,17100,17101,17102,17103,17104,17105,17106,17107,17108,17109,17110,17111,17112,17113,17114,17115,17116,17117,17118,17119,17120,17121,17122,17123,17124,17125,17126,17127,17128,17129,17130,17131,17132,17133,17134,17135,17136,17137,17138,17139,17140,17141,17142,17143,17144,17145,17146,17147,17148,17149,17150,17151,17152,17153,17154,17155,17156,17157,17158,17159,17160,17161,17162,17163,17164,17165,17166,17167,17168,17169,17170,17171,17172,17173,17174,17175,17176,17177,17178,17179,17180,17181,17182,17183,17184,17185,17186,17187,17188,17189,17190,17191,17192,17193,17194,17195,17196,17197,17198,17199,17200,17201,17202,17203,17204,17205,17206,17207,17208,17209,17210,17211,17212,17213,17214,17215,17216,17217,17218,17219,17220,17221,17222,17223,17224,17225,17226,17227,17228,17229,17230,17231,17232,17233,17234,17235,17236,17237,17238,17239,17240,17241,17242,17243,17244,17245,17246,17247,17248,17249,17250,17251,17252,17253,17254,17255,17256,17257,17258,17259,17260,17261,17262,17263,17264,17265,17266,17267,17268,17269,17270,17271,17272,17273,17274,17275,17276,17277,17278,17279,17280,17281,17282,17283,17284,17285,17286,17287,17288,17289,17290,17291,17292,17293,17294,17295,17296,17297,17298,17299,17300,17301,17302,17303,17304,17305,17306,17307,17308,17309,17310,17311,17312,17313,17314,17315,17316,17317,17318,17319,17320,17321,17322,17323,17324,17325,17326,17327,17328,17329,17330,17331,17332,17333,17334,17335,17336,17337,17338,17339,17340,17341,17342,17343,17344,17345,17346,17347,17348,17349,17350,17351,17352,17353,17354,17355,17356,17357,17358,17359,17360,17361,17362,17363,17364,17365,17366,17367,17368,17369,17370,17371,17372,17373,17374,17375,17376,17377,17378,17379,17380,17381,17382,17383,17384,17385,17386,17387,17388,17389,17390,17391,17392,17393,17394,17395,17396,17397,17398,17399,17400,17401,17402,17403,17404,17405,17406,17407,17408,17409,17410,17411,17412,17413,17414,17415,17416,17417,17418,17419,17420,17421,17422,17423,17424,17425,17426,17427,17428,17429,17430,17431,17432,17433,17434,17435,17436,17437,17438,17439,17440,17441,17442,17443,17444,17445,17446,17447,17448,17449,17450,17451,17452,17453,17454,17455,17456,17457,17458,17459,17460,17461,17462,17463,17464,17465,17466,17467,17468,17469,17470,17471,17472,17473,17474,17475,17476,17477,17478,17479,17480,17481,17482,17483,17484,17485,17486,17487,17488,17489,17490,17491,17492,17493,17494,17495,17496,17497,17498,17499,17500,17501,17502,17503,17504,17505,17506,17507,17508,17509,17510,17511,17512,17513,17514,17515,17516,17517,17518,17519,17520,17521,17522,17523,17524,17525,17526,17527,17528,17529,17530,17531,17532,17533,17534,17535,17536,17537,17538,17539,17540,17541,17542,17543,17544,17545,17546,17547,17548,17549,17550,17551,17552,17553,17554,17555,17556,17557,17558,17559,17560,17561,17562,17563,17564,17565,17566,17567,17568,17569,17570,17571,17572,17573,17574,17575,17576,17577,17578,17579,17580,17581,17582,17583,17584,17585,17586,17587,17588,17589,17590,17591,17592,17593,17594,17595,17596,17597,17598,17599,17600,17601,17602,17603,17604,17605,17606,17607,17608,17609,17610,17611,17612,17613,17614,17615,17616,17617,17618,17619,17620,17621,17622,17623,17624,17625,17626,17627,17628,17629,17630,17631,17632,17633,17634,17635,17636,17637,17638,17639,17640,17641,17642,17643,17644,17645,17646,17647,17648,17649,17650,17651,17652,17653,17654,17655,17656,17657,17658,17659,17660,17661,17662,17663,17664,17665,17666,17667,17668,17669,17670,17671,17672,17673,17674,17675,17676,17677,17678,17679,17680,17681,17682,17683,17684,17685,17686,17687,17688,17689,17690,17691,17692,17693,17694,17695,17696,17697,17698,17699,17700,17701,17702,17703,17704,17705,17706,17707,17708,17709,17710,17711,17712,17713,17714,17715,17716,17717,17718,17719,17720,17721,17722,17723,17724,17725,17726,17727,17728,17729,17730,17731,17732,17733,17734,17735,17736,17737,17738,17739,17740,17741,17742,17743,17744,17745,17746,17747,17748,17749,17750,17751,17752,17753,17754,17755,17756,17757,17758,17759,17760,17761,17762,17763,17764,17765,17766,17767,17768,17769,17770,17771,17772,17773,17774,17775,17776,17777,17778,17779,17780,17781,17782,17783,17784,17785,17786,17787,17788,17789,17790,17791,17792,17793,17794,17795,17796,17797,17798,17799,17800,17801,17802,17803,17804,17805,17806,17807,17808,17809,17810,17811,17812,17813,17814,17815,17816,17817,17818,17819,17820,17821,17822,17823,17824,17825,17826,17827,17828,17829,17830,17831,17832,17833,17834,17835,17836,17837,17838,17839,17840,17841,17842,17843,17844,17845,17846,17847,17848,17849,17850,17851,17852,17853,17854,17855,17856,17857,17858,17859,17860,17861,17862,17863,17864,17865,17866,17867,17868,17869,17870,17871,17872,17873,17874,17875,17876,17877,17878,17879,17880,17881,17882,17883,17884,17885,17886,17887,17888,17889,17890,17891,17892,17893,17894,17895,17896,17897,17898,17899,17900,17901,17902,17903,17904,17905,17906,17907,17908,17909,17910,17911,17912,17913,17914,17915,17916,17917,17918,17919,17920,17921,17922,17923,17924,17925,17926,17927,17928,17929,17930,17931,17932,17933,17934,17935,17936,17937,17938,17939,17940,17941,17942,17943,17944,17945,17946,17947,17948,17949,17950,17951,17952,17953,17954,17955,17956,17957,17958,17959,17960,17961,17962,17963,17964,17965,17966,17967,17968,17969,17970,17971,17972,17973,17974,17975,17976,17977,17978,17979,17980,17981,17982,17983,17984,17985,17986,17987,17988,17989,17990,17991,17992,17993,17994,17995,17996,17997,17998,17999,18000,18001,18002,18003,18004,18005,18006,18007,18008,18009,18010,18011,18012,18013,18014,18015,18016,18017,18018,18019,18020,18021,18022,18023,18024,18025,18026,18027,18028,18029,18030,18031,18032,18033,18034,18035,18036,18037,18038,18039,18040,18041,18042,18043,18044,18045,18046,18047,18048,18049,18050,18051,18052,18053,18054,18055,18056,18057,18058,18059,18060,18061,18062,18063,18064,18065,18066,18067,18068,18069,18070,18071,18072,18073,18074,18075,18076,18077,18078,18079,18080,18081,18082,18083,18084,18085,18086,18087,18088,18089,18090,18091,18092,18093,18094,18095,18096,18097,18098,18099,18100,18101,18102,18103,18104,18105,18106,18107,18108,18109,18110,18111,18112,18113,18114,18115,18116,18117,18118,18119,18120,18121,18122,18123,18124,18125,18126,18127,18128,18129,18130,18131,18132,18133,18134,18135,18136,18137,18138,18139,18140,18141,18142,18143,18144,18145,18146,18147,18148,18149,18150,18151,18152,18153,18154,18155,18156,18157,18158,18159,18160,18161,18162,18163,18164,18165,18166,18167,18168,18169,18170,18171,18172,18173,18174,18175,18176,18177,18178,18179,18180,18181,18182,18183,18184,18185,18186,18187,18188,18189,18190,18191,18192,18193,18194,18195,18196,18197,18198,18199,18200,18201,18202,18203,18204,18205,18206,18207,18208,18209,18210,18211,18212,18213,18214,18215,18216,18217,18218,18219,18220,18221,18222,18223,18224,18225,18226,18227,18228,18229,18230,18231,18232,18233,18234,18235,18236,18237,18238,18239,18240,18241,18242,18243,18244,18245,18246,18247,18248,18249,18250,18251,18252,18253,18254,18255,18256,18257,18258,18259,18260,18261,18262,18263,18264,18265,18266,18267,18268,18269,18270,18271,18272,18273,18274,18275,18276,18277,18278,18279,18280,18281,18282,18283,18284,18285,18286,18287,18288,18289,18290,18291,18292,18293,18294,18295,18296,18297,18298,18299,18300,18301,18302,18303,18304,18305,18306,18307,18308,18309,18310,18311,18312,18313,18314,18315,18316,18317,18318,18319,18320,18321,18322,18323,18324,18325,18326,18327,18328,18329,18330,18331,18332,18333,18334,18335,18336,18337,18338,18339,18340,18341,18342,18343,18344,18345,18346,18347,18348,18349,18350,18351,18352,18353,18354,18355,18356,18357,18358,18359,18360,18361,18362,18363,18364,18365,18366,18367,18368,18369,18370,18371,18372,18373,18374,18375,18376,18377,18378,18379,18380,18381,18382,18383,18384,18385,18386,18387,18388,18389,18390,18391,18392,18393,18394,18395,18396,18397,18398,18399,18400,18401,18402,18403,18404,18405,18406,18407,18408,18409,18410,18411,18412,18413,18414,18415,18416,18417,18418,18419,18420,18421,18422,18423,18424,18425,18426,18427,18428,18429,18430,18431,18432,18433,18434,18435,18436,18437,18438,18439,18440,18441,18442,18443,18444,18445,18446,18447,18448,18449,18450,18451,18452,18453,18454,18455,18456,18457,18458,18459,18460,18461,18462,18463,18464,18465,18466,18467,18468,18469,18470,18471,18472,18473,18474,18475,18476,18477,18478,18479,18480,18481,18482,18483,18484,18485,18486,18487,18488,18489,18490,18491,18492,18493,18494,18495,18496,18497,18498,18499,18500,18501,18502,18503,18504,18505,18506,18507,18508,18509,18510,18511,18512,18513,18514,18515,18516,18517,18518,18519,18520,18521,18522,18523,18524,18525,18526,18527,18528,18529,18530,18531,18532,18533,18534,18535,18536,18537,18538,18539,18540,18541,18542,18543,18544,18545,18546,18547,18548,18549,18550,18551,18552,18553,18554,18555,18556,18557,18558,18559,18560,18561,18562,18563,18564,18565,18566,18567,18568,18569,18570,18571,18572,18573,18574,18575,18576,18577,18578,18579,18580,18581,18582,18583,18584,18585,18586,18587,18588,18589,18590,18591,18592,18593,18594,18595,18596,18597,18598,18599,18600,18601,18602,18603,18604,18605,18606,18607,18608,18609,18610,18611,18612,18613,18614,18615,18616,18617,18618,18619,18620,18621,18622,18623,18624,18625,18626,18627,18628,18629,18630,18631,18632,18633,18634,18635,18636,18637,18638,18639,18640,18641,18642,18643,18644,18645,18646,18647,18648,18649,18650,18651,18652,18653,18654,18655,18656,18657,18658,18659,18660,18661,18662,18663,18664,18665,18666,18667,18668,18669,18670,18671,18672,18673,18674,18675,18676,18677,18678,18679,18680,18681,18682,18683,18684,18685,18686,18687,18688,18689,18690,18691,18692,18693,18694,18695,18696,18697,18698,18699,18700,18701,18702,18703,18704,18705,18706,18707,18708,18709,18710,18711,18712,18713,18714,18715,18716,18717,18718,18719,18720,18721,18722,18723,18724,18725,18726,18727,18728,18729,18730,18731,18732,18733,18734,18735,18736,18737,18738,18739,18740,18741,18742,18743,18744,18745,18746,18747,18748,18749,18750,18751,18752,18753,18754,18755,18756,18757,18758,18759,18760,18761,18762,18763,18764,18765,18766,18767,18768,18769,18770,18771,18772,18773,18774,18775,18776,18777,18778,18779,18780,18781,18782,18783,18784,18785,18786,18787,18788,18789,18790,18791,18792,18793,18794,18795,18796,18797,18798,18799,18800,18801,18802,18803,18804,18805,18806,18807,18808,18809,18810,18811,18812,18813,18814,18815,18816,18817,18818,18819,18820,18821,18822,18823,18824,18825,18826,18827,18828,18829,18830,18831,18832,18833,18834,18835,18836,18837,18838,18839,18840,18841,18842,18843,18844,18845,18846,18847,18848,18849,18850,18851,18852,18853,18854,18855,18856,18857,18858,18859,18860,18861,18862,18863,18864,18865,18866,18867,18868,18869,18870,18871,18872,18873,18874,18875,18876,18877,18878,18879,18880,18881,18882,18883,18884,18885,18886,18887,18888,18889,18890,18891,18892,18893,18894,18895,18896,18897,18898,18899,18900,18901,18902,18903,18904,18905,18906,18907,18908,18909,18910,18911,18912,18913,18914,18915,18916,18917,18918,18919,18920,18921,18922,18923,18924,18925,18926,18927,18928,18929,18930,18931,18932,18933,18934,18935,18936,18937,18938,18939,18940,18941,18942,18943,18944,18945,18946,18947,18948,18949,18950,18951,18952,18953,18954,18955,18956,18957,18958,18959,18960,18961,18962,18963,18964,18965,18966,18967,18968,18969,18970,18971,18972,18973,18974,18975,18976,18977,18978,18979,18980,18981,18982,18983,18984,18985,18986,18987,18988,18989,18990,18991,18992,18993,18994,18995,18996,18997,18998,18999,19000,19001,19002,19003,19004,19005,19006,19007,19008,19009,19010,19011,19012,19013,19014,19015,19016,19017,19018,19019,19020,19021,19022,19023,19024,19025,19026,19027,19028,19029,19030,19031,19032,19033,19034,19035,19036,19037,19038,19039,19040,19041,19042,19043,19044,19045,19046,19047,19048,19049,19050,19051,19052,19053,19054,19055,19056,19057,19058,19059,19060,19061,19062,19063,19064,19065,19066,19067,19068,19069,19070,19071,19072,19073,19074,19075,19076,19077,19078,19079,19080,19081,19082,19083,19084,19085,19086,19087,19088,19089,19090,19091,19092,19093,19094,19095,19096,19097,19098,19099,19100,19101,19102,19103,19104,19105,19106,19107,19108,19109,19110,19111,19112,19113,19114,19115,19116,19117,19118,19119,19120,19121,19122,19123,19124,19125,19126,19127,19128,19129,19130,19131,19132,19133,19134,19135,19136,19137,19138,19139,19140,19141,19142,19143,19144,19145,19146,19147,19148,19149,19150,19151,19152,19153,19154,19155,19156,19157,19158,19159,19160,19161,19162,19163,19164,19165,19166,19167,19168,19169,19170,19171,19172,19173,19174,19175,19176,19177,19178,19179,19180,19181,19182,19183,19184,19185,19186,19187,19188,19189,19190,19191,19192,19193,19194,19195,19196,19197,19198,19199,19200,19201,19202,19203,19204,19205,19206,19207,19208,19209,19210,19211,19212,19213,19214,19215,19216,19217,19218,19219,19220,19221,19222,19223,19224,19225,19226,19227,19228,19229,19230,19231,19232,19233,19234,19235,19236,19237,19238,19239,19240,19241,19242,19243,19244,19245,19246,19247,19248,19249,19250,19251,19252,19253,19254,19255,19256,19257,19258,19259,19260,19261,19262,19263,19264,19265,19266,19267,19268,19269,19270,19271,19272,19273,19274,19275,19276,19277,19278,19279,19280,19281,19282,19283,19284,19285,19286,19287,19288,19289,19290,19291,19292,19293,19294,19295,19296,19297,19298,19299,19300,19301,19302,19303,19304,19305,19306,19307,19308,19309,19310,19311,19312,19313,19314,19315,19316,19317,19318,19319,19320,19321,19322,19323,19324,19325,19326,19327,19328,19329,19330,19331,19332,19333,19334,19335,19336,19337,19338,19339,19340,19341,19342,19343,19344,19345,19346,19347,19348,19349,19350,19351,19352,19353,19354,19355,19356,19357,19358,19359,19360,19361,19362,19363,19364,19365,19366,19367,19368,19369,19370,19371,19372,19373,19374,19375,19376,19377,19378,19379,19380,19381,19382,19383,19384,19385,19386,19387,19388,19389,19390,19391,19392,19393,19394,19395,19396,19397,19398,19399,19400,19401,19402,19403,19404,19405,19406,19407,19408,19409,19410,19411,19412,19413,19414,19415,19416,19417,19418,19419,19420,19421,19422,19423,19424,19425,19426,19427,19428,19429,19430,19431,19432,19433,19434,19435,19436,19437,19438,19439,19440,19441,19442,19443,19444,19445,19446,19447,19448,19449,19450,19451,19452,19453,19454,19455,19456,19457,19458,19459,19460,19461,19462,19463,19464,19465,19466,19467,19468,19469,19470,19471,19472,19473,19474,19475,19476,19477,19478,19479,19480,19481,19482,19483,19484,19485,19486,19487,19488,19489,19490,19491,19492,19493,19494,19495,19496,19497,19498,19499,19500,19501,19502,19503,19504,19505,19506,19507,19508,19509,19510,19511,19512,19513,19514,19515,19516,19517,19518,19519,19520,19521,19522,19523,19524,19525,19526,19527,19528,19529,19530,19531,19532,19533,19534,19535,19536,19537,19538,19539,19540,19541,19542,19543,19544,19545,19546,19547,19548,19549,19550,19551,19552,19553,19554,19555,19556,19557,19558,19559,19560,19561,19562,19563,19564,19565,19566,19567,19568,19569,19570,19571,19572,19573,19574,19575,19576,19577,19578,19579,19580,19581,19582,19583,19584,19585,19586,19587,19588,19589,19590,19591,19592,19593,19594,19595,19596,19597,19598,19599,19600,19601,19602,19603,19604,19605,19606,19607,19608,19609,19610,19611,19612,19613,19614,19615,19616,19617,19618,19619,19620,19621,19622,19623,19624,19625,19626,19627,19628,19629,19630,19631,19632,19633,19634,19635,19636,19637,19638,19639,19640,19641,19642,19643,19644,19645,19646,19647,19648,19649,19650,19651,19652,19653,19654,19655,19656,19657,19658,19659,19660,19661,19662,19663,19664,19665,19666,19667,19668,19669,19670,19671,19672,19673,19674,19675,19676,19677,19678,19679,19680,19681,19682,19683,19684,19685,19686,19687,19688,19689,19690,19691,19692,19693,19694,19695,19696,19697,19698,19699,19700,19701,19702,19703,19704,19705,19706,19707,19708,19709,19710,19711,19712,19713,19714,19715,19716,19717,19718,19719,19720,19721,19722,19723,19724,19725,19726,19727,19728,19729,19730,19731,19732,19733,19734,19735,19736,19737,19738,19739,19740,19741,19742,19743,19744,19745,19746,19747,19748,19749,19750,19751,19752,19753,19754,19755,19756,19757,19758,19759,19760,19761,19762,19763,19764,19765,19766,19767,19768,19769,19770,19771,19772,19773,19774,19775,19776,19777,19778,19779,19780,19781,19782,19783,19784,19785,19786,19787,19788,19789,19790,19791,19792,19793,19794,19795,19796,19797,19798,19799,19800,19801,19802,19803,19804,19805,19806,19807,19808,19809,19810,19811,19812,19813,19814,19815,19816,19817,19818,19819,19820,19821,19822,19823,19824,19825,19826,19827,19828,19829,19830,19831,19832,19833,19834,19835,19836,19837,19838,19839,19840,19841,19842,19843,19844,19845,19846,19847,19848,19849,19850,19851,19852,19853,19854,19855,19856,19857,19858,19859,19860,19861,19862,19863,19864,19865,19866,19867,19868,19869,19870,19871,19872,19873,19874,19875,19876,19877,19878,19879,19880,19881,19882,19883,19884,19885,19886,19887,19888,19889,19890,19891,19892,19893,19968,19969,19970,19971,19972,19973,19974,19975,19976,19977,19978,19979,19980,19981,19982,19983,19984,19985,19986,19987,19988,19989,19990,19991,19992,19993,19994,19995,19996,19997,19998,19999,20000,20001,20002,20003,20004,20005,20006,20007,20008,20009,20010,20011,20012,20013,20014,20015,20016,20017,20018,20019,20020,20021,20022,20023,20024,20025,20026,20027,20028,20029,20030,20031,20032,20033,20034,20035,20036,20037,20038,20039,20040,20041,20042,20043,20044,20045,20046,20047,20048,20049,20050,20051,20052,20053,20054,20055,20056,20057,20058,20059,20060,20061,20062,20063,20064,20065,20066,20067,20068,20069,20070,20071,20072,20073,20074,20075,20076,20077,20078,20079,20080,20081,20082,20083,20084,20085,20086,20087,20088,20089,20090,20091,20092,20093,20094,20095,20096,20097,20098,20099,20100,20101,20102,20103,20104,20105,20106,20107,20108,20109,20110,20111,20112,20113,20114,20115,20116,20117,20118,20119,20120,20121,20122,20123,20124,20125,20126,20127,20128,20129,20130,20131,20132,20133,20134,20135,20136,20137,20138,20139,20140,20141,20142,20143,20144,20145,20146,20147,20148,20149,20150,20151,20152,20153,20154,20155,20156,20157,20158,20159,20160,20161,20162,20163,20164,20165,20166,20167,20168,20169,20170,20171,20172,20173,20174,20175,20176,20177,20178,20179,20180,20181,20182,20183,20184,20185,20186,20187,20188,20189,20190,20191,20192,20193,20194,20195,20196,20197,20198,20199,20200,20201,20202,20203,20204,20205,20206,20207,20208,20209,20210,20211,20212,20213,20214,20215,20216,20217,20218,20219,20220,20221,20222,20223,20224,20225,20226,20227,20228,20229,20230,20231,20232,20233,20234,20235,20236,20237,20238,20239,20240,20241,20242,20243,20244,20245,20246,20247,20248,20249,20250,20251,20252,20253,20254,20255,20256,20257,20258,20259,20260,20261,20262,20263,20264,20265,20266,20267,20268,20269,20270,20271,20272,20273,20274,20275,20276,20277,20278,20279,20280,20281,20282,20283,20284,20285,20286,20287,20288,20289,20290,20291,20292,20293,20294,20295,20296,20297,20298,20299,20300,20301,20302,20303,20304,20305,20306,20307,20308,20309,20310,20311,20312,20313,20314,20315,20316,20317,20318,20319,20320,20321,20322,20323,20324,20325,20326,20327,20328,20329,20330,20331,20332,20333,20334,20335,20336,20337,20338,20339,20340,20341,20342,20343,20344,20345,20346,20347,20348,20349,20350,20351,20352,20353,20354,20355,20356,20357,20358,20359,20360,20361,20362,20363,20364,20365,20366,20367,20368,20369,20370,20371,20372,20373,20374,20375,20376,20377,20378,20379,20380,20381,20382,20383,20384,20385,20386,20387,20388,20389,20390,20391,20392,20393,20394,20395,20396,20397,20398,20399,20400,20401,20402,20403,20404,20405,20406,20407,20408,20409,20410,20411,20412,20413,20414,20415,20416,20417,20418,20419,20420,20421,20422,20423,20424,20425,20426,20427,20428,20429,20430,20431,20432,20433,20434,20435,20436,20437,20438,20439,20440,20441,20442,20443,20444,20445,20446,20447,20448,20449,20450,20451,20452,20453,20454,20455,20456,20457,20458,20459,20460,20461,20462,20463,20464,20465,20466,20467,20468,20469,20470,20471,20472,20473,20474,20475,20476,20477,20478,20479,20480,20481,20482,20483,20484,20485,20486,20487,20488,20489,20490,20491,20492,20493,20494,20495,20496,20497,20498,20499,20500,20501,20502,20503,20504,20505,20506,20507,20508,20509,20510,20511,20512,20513,20514,20515,20516,20517,20518,20519,20520,20521,20522,20523,20524,20525,20526,20527,20528,20529,20530,20531,20532,20533,20534,20535,20536,20537,20538,20539,20540,20541,20542,20543,20544,20545,20546,20547,20548,20549,20550,20551,20552,20553,20554,20555,20556,20557,20558,20559,20560,20561,20562,20563,20564,20565,20566,20567,20568,20569,20570,20571,20572,20573,20574,20575,20576,20577,20578,20579,20580,20581,20582,20583,20584,20585,20586,20587,20588,20589,20590,20591,20592,20593,20594,20595,20596,20597,20598,20599,20600,20601,20602,20603,20604,20605,20606,20607,20608,20609,20610,20611,20612,20613,20614,20615,20616,20617,20618,20619,20620,20621,20622,20623,20624,20625,20626,20627,20628,20629,20630,20631,20632,20633,20634,20635,20636,20637,20638,20639,20640,20641,20642,20643,20644,20645,20646,20647,20648,20649,20650,20651,20652,20653,20654,20655,20656,20657,20658,20659,20660,20661,20662,20663,20664,20665,20666,20667,20668,20669,20670,20671,20672,20673,20674,20675,20676,20677,20678,20679,20680,20681,20682,20683,20684,20685,20686,20687,20688,20689,20690,20691,20692,20693,20694,20695,20696,20697,20698,20699,20700,20701,20702,20703,20704,20705,20706,20707,20708,20709,20710,20711,20712,20713,20714,20715,20716,20717,20718,20719,20720,20721,20722,20723,20724,20725,20726,20727,20728,20729,20730,20731,20732,20733,20734,20735,20736,20737,20738,20739,20740,20741,20742,20743,20744,20745,20746,20747,20748,20749,20750,20751,20752,20753,20754,20755,20756,20757,20758,20759,20760,20761,20762,20763,20764,20765,20766,20767,20768,20769,20770,20771,20772,20773,20774,20775,20776,20777,20778,20779,20780,20781,20782,20783,20784,20785,20786,20787,20788,20789,20790,20791,20792,20793,20794,20795,20796,20797,20798,20799,20800,20801,20802,20803,20804,20805,20806,20807,20808,20809,20810,20811,20812,20813,20814,20815,20816,20817,20818,20819,20820,20821,20822,20823,20824,20825,20826,20827,20828,20829,20830,20831,20832,20833,20834,20835,20836,20837,20838,20839,20840,20841,20842,20843,20844,20845,20846,20847,20848,20849,20850,20851,20852,20853,20854,20855,20856,20857,20858,20859,20860,20861,20862,20863,20864,20865,20866,20867,20868,20869,20870,20871,20872,20873,20874,20875,20876,20877,20878,20879,20880,20881,20882,20883,20884,20885,20886,20887,20888,20889,20890,20891,20892,20893,20894,20895,20896,20897,20898,20899,20900,20901,20902,20903,20904,20905,20906,20907,20908,20909,20910,20911,20912,20913,20914,20915,20916,20917,20918,20919,20920,20921,20922,20923,20924,20925,20926,20927,20928,20929,20930,20931,20932,20933,20934,20935,20936,20937,20938,20939,20940,20941,20942,20943,20944,20945,20946,20947,20948,20949,20950,20951,20952,20953,20954,20955,20956,20957,20958,20959,20960,20961,20962,20963,20964,20965,20966,20967,20968,20969,20970,20971,20972,20973,20974,20975,20976,20977,20978,20979,20980,20981,20982,20983,20984,20985,20986,20987,20988,20989,20990,20991,20992,20993,20994,20995,20996,20997,20998,20999,21000,21001,21002,21003,21004,21005,21006,21007,21008,21009,21010,21011,21012,21013,21014,21015,21016,21017,21018,21019,21020,21021,21022,21023,21024,21025,21026,21027,21028,21029,21030,21031,21032,21033,21034,21035,21036,21037,21038,21039,21040,21041,21042,21043,21044,21045,21046,21047,21048,21049,21050,21051,21052,21053,21054,21055,21056,21057,21058,21059,21060,21061,21062,21063,21064,21065,21066,21067,21068,21069,21070,21071,21072,21073,21074,21075,21076,21077,21078,21079,21080,21081,21082,21083,21084,21085,21086,21087,21088,21089,21090,21091,21092,21093,21094,21095,21096,21097,21098,21099,21100,21101,21102,21103,21104,21105,21106,21107,21108,21109,21110,21111,21112,21113,21114,21115,21116,21117,21118,21119,21120,21121,21122,21123,21124,21125,21126,21127,21128,21129,21130,21131,21132,21133,21134,21135,21136,21137,21138,21139,21140,21141,21142,21143,21144,21145,21146,21147,21148,21149,21150,21151,21152,21153,21154,21155,21156,21157,21158,21159,21160,21161,21162,21163,21164,21165,21166,21167,21168,21169,21170,21171,21172,21173,21174,21175,21176,21177,21178,21179,21180,21181,21182,21183,21184,21185,21186,21187,21188,21189,21190,21191,21192,21193,21194,21195,21196,21197,21198,21199,21200,21201,21202,21203,21204,21205,21206,21207,21208,21209,21210,21211,21212,21213,21214,21215,21216,21217,21218,21219,21220,21221,21222,21223,21224,21225,21226,21227,21228,21229,21230,21231,21232,21233,21234,21235,21236,21237,21238,21239,21240,21241,21242,21243,21244,21245,21246,21247,21248,21249,21250,21251,21252,21253,21254,21255,21256,21257,21258,21259,21260,21261,21262,21263,21264,21265,21266,21267,21268,21269,21270,21271,21272,21273,21274,21275,21276,21277,21278,21279,21280,21281,21282,21283,21284,21285,21286,21287,21288,21289,21290,21291,21292,21293,21294,21295,21296,21297,21298,21299,21300,21301,21302,21303,21304,21305,21306,21307,21308,21309,21310,21311,21312,21313,21314,21315,21316,21317,21318,21319,21320,21321,21322,21323,21324,21325,21326,21327,21328,21329,21330,21331,21332,21333,21334,21335,21336,21337,21338,21339,21340,21341,21342,21343,21344,21345,21346,21347,21348,21349,21350,21351,21352,21353,21354,21355,21356,21357,21358,21359,21360,21361,21362,21363,21364,21365,21366,21367,21368,21369,21370,21371,21372,21373,21374,21375,21376,21377,21378,21379,21380,21381,21382,21383,21384,21385,21386,21387,21388,21389,21390,21391,21392,21393,21394,21395,21396,21397,21398,21399,21400,21401,21402,21403,21404,21405,21406,21407,21408,21409,21410,21411,21412,21413,21414,21415,21416,21417,21418,21419,21420,21421,21422,21423,21424,21425,21426,21427,21428,21429,21430,21431,21432,21433,21434,21435,21436,21437,21438,21439,21440,21441,21442,21443,21444,21445,21446,21447,21448,21449,21450,21451,21452,21453,21454,21455,21456,21457,21458,21459,21460,21461,21462,21463,21464,21465,21466,21467,21468,21469,21470,21471,21472,21473,21474,21475,21476,21477,21478,21479,21480,21481,21482,21483,21484,21485,21486,21487,21488,21489,21490,21491,21492,21493,21494,21495,21496,21497,21498,21499,21500,21501,21502,21503,21504,21505,21506,21507,21508,21509,21510,21511,21512,21513,21514,21515,21516,21517,21518,21519,21520,21521,21522,21523,21524,21525,21526,21527,21528,21529,21530,21531,21532,21533,21534,21535,21536,21537,21538,21539,21540,21541,21542,21543,21544,21545,21546,21547,21548,21549,21550,21551,21552,21553,21554,21555,21556,21557,21558,21559,21560,21561,21562,21563,21564,21565,21566,21567,21568,21569,21570,21571,21572,21573,21574,21575,21576,21577,21578,21579,21580,21581,21582,21583,21584,21585,21586,21587,21588,21589,21590,21591,21592,21593,21594,21595,21596,21597,21598,21599,21600,21601,21602,21603,21604,21605,21606,21607,21608,21609,21610,21611,21612,21613,21614,21615,21616,21617,21618,21619,21620,21621,21622,21623,21624,21625,21626,21627,21628,21629,21630,21631,21632,21633,21634,21635,21636,21637,21638,21639,21640,21641,21642,21643,21644,21645,21646,21647,21648,21649,21650,21651,21652,21653,21654,21655,21656,21657,21658,21659,21660,21661,21662,21663,21664,21665,21666,21667,21668,21669,21670,21671,21672,21673,21674,21675,21676,21677,21678,21679,21680,21681,21682,21683,21684,21685,21686,21687,21688,21689,21690,21691,21692,21693,21694,21695,21696,21697,21698,21699,21700,21701,21702,21703,21704,21705,21706,21707,21708,21709,21710,21711,21712,21713,21714,21715,21716,21717,21718,21719,21720,21721,21722,21723,21724,21725,21726,21727,21728,21729,21730,21731,21732,21733,21734,21735,21736,21737,21738,21739,21740,21741,21742,21743,21744,21745,21746,21747,21748,21749,21750,21751,21752,21753,21754,21755,21756,21757,21758,21759,21760,21761,21762,21763,21764,21765,21766,21767,21768,21769,21770,21771,21772,21773,21774,21775,21776,21777,21778,21779,21780,21781,21782,21783,21784,21785,21786,21787,21788,21789,21790,21791,21792,21793,21794,21795,21796,21797,21798,21799,21800,21801,21802,21803,21804,21805,21806,21807,21808,21809,21810,21811,21812,21813,21814,21815,21816,21817,21818,21819,21820,21821,21822,21823,21824,21825,21826,21827,21828,21829,21830,21831,21832,21833,21834,21835,21836,21837,21838,21839,21840,21841,21842,21843,21844,21845,21846,21847,21848,21849,21850,21851,21852,21853,21854,21855,21856,21857,21858,21859,21860,21861,21862,21863,21864,21865,21866,21867,21868,21869,21870,21871,21872,21873,21874,21875,21876,21877,21878,21879,21880,21881,21882,21883,21884,21885,21886,21887,21888,21889,21890,21891,21892,21893,21894,21895,21896,21897,21898,21899,21900,21901,21902,21903,21904,21905,21906,21907,21908,21909,21910,21911,21912,21913,21914,21915,21916,21917,21918,21919,21920,21921,21922,21923,21924,21925,21926,21927,21928,21929,21930,21931,21932,21933,21934,21935,21936,21937,21938,21939,21940,21941,21942,21943,21944,21945,21946,21947,21948,21949,21950,21951,21952,21953,21954,21955,21956,21957,21958,21959,21960,21961,21962,21963,21964,21965,21966,21967,21968,21969,21970,21971,21972,21973,21974,21975,21976,21977,21978,21979,21980,21981,21982,21983,21984,21985,21986,21987,21988,21989,21990,21991,21992,21993,21994,21995,21996,21997,21998,21999,22000,22001,22002,22003,22004,22005,22006,22007,22008,22009,22010,22011,22012,22013,22014,22015,22016,22017,22018,22019,22020,22021,22022,22023,22024,22025,22026,22027,22028,22029,22030,22031,22032,22033,22034,22035,22036,22037,22038,22039,22040,22041,22042,22043,22044,22045,22046,22047,22048,22049,22050,22051,22052,22053,22054,22055,22056,22057,22058,22059,22060,22061,22062,22063,22064,22065,22066,22067,22068,22069,22070,22071,22072,22073,22074,22075,22076,22077,22078,22079,22080,22081,22082,22083,22084,22085,22086,22087,22088,22089,22090,22091,22092,22093,22094,22095,22096,22097,22098,22099,22100,22101,22102,22103,22104,22105,22106,22107,22108,22109,22110,22111,22112,22113,22114,22115,22116,22117,22118,22119,22120,22121,22122,22123,22124,22125,22126,22127,22128,22129,22130,22131,22132,22133,22134,22135,22136,22137,22138,22139,22140,22141,22142,22143,22144,22145,22146,22147,22148,22149,22150,22151,22152,22153,22154,22155,22156,22157,22158,22159,22160,22161,22162,22163,22164,22165,22166,22167,22168,22169,22170,22171,22172,22173,22174,22175,22176,22177,22178,22179,22180,22181,22182,22183,22184,22185,22186,22187,22188,22189,22190,22191,22192,22193,22194,22195,22196,22197,22198,22199,22200,22201,22202,22203,22204,22205,22206,22207,22208,22209,22210,22211,22212,22213,22214,22215,22216,22217,22218,22219,22220,22221,22222,22223,22224,22225,22226,22227,22228,22229,22230,22231,22232,22233,22234,22235,22236,22237,22238,22239,22240,22241,22242,22243,22244,22245,22246,22247,22248,22249,22250,22251,22252,22253,22254,22255,22256,22257,22258,22259,22260,22261,22262,22263,22264,22265,22266,22267,22268,22269,22270,22271,22272,22273,22274,22275,22276,22277,22278,22279,22280,22281,22282,22283,22284,22285,22286,22287,22288,22289,22290,22291,22292,22293,22294,22295,22296,22297,22298,22299,22300,22301,22302,22303,22304,22305,22306,22307,22308,22309,22310,22311,22312,22313,22314,22315,22316,22317,22318,22319,22320,22321,22322,22323,22324,22325,22326,22327,22328,22329,22330,22331,22332,22333,22334,22335,22336,22337,22338,22339,22340,22341,22342,22343,22344,22345,22346,22347,22348,22349,22350,22351,22352,22353,22354,22355,22356,22357,22358,22359,22360,22361,22362,22363,22364,22365,22366,22367,22368,22369,22370,22371,22372,22373,22374,22375,22376,22377,22378,22379,22380,22381,22382,22383,22384,22385,22386,22387,22388,22389,22390,22391,22392,22393,22394,22395,22396,22397,22398,22399,22400,22401,22402,22403,22404,22405,22406,22407,22408,22409,22410,22411,22412,22413,22414,22415,22416,22417,22418,22419,22420,22421,22422,22423,22424,22425,22426,22427,22428,22429,22430,22431,22432,22433,22434,22435,22436,22437,22438,22439,22440,22441,22442,22443,22444,22445,22446,22447,22448,22449,22450,22451,22452,22453,22454,22455,22456,22457,22458,22459,22460,22461,22462,22463,22464,22465,22466,22467,22468,22469,22470,22471,22472,22473,22474,22475,22476,22477,22478,22479,22480,22481,22482,22483,22484,22485,22486,22487,22488,22489,22490,22491,22492,22493,22494,22495,22496,22497,22498,22499,22500,22501,22502,22503,22504,22505,22506,22507,22508,22509,22510,22511,22512,22513,22514,22515,22516,22517,22518,22519,22520,22521,22522,22523,22524,22525,22526,22527,22528,22529,22530,22531,22532,22533,22534,22535,22536,22537,22538,22539,22540,22541,22542,22543,22544,22545,22546,22547,22548,22549,22550,22551,22552,22553,22554,22555,22556,22557,22558,22559,22560,22561,22562,22563,22564,22565,22566,22567,22568,22569,22570,22571,22572,22573,22574,22575,22576,22577,22578,22579,22580,22581,22582,22583,22584,22585,22586,22587,22588,22589,22590,22591,22592,22593,22594,22595,22596,22597,22598,22599,22600,22601,22602,22603,22604,22605,22606,22607,22608,22609,22610,22611,22612,22613,22614,22615,22616,22617,22618,22619,22620,22621,22622,22623,22624,22625,22626,22627,22628,22629,22630,22631,22632,22633,22634,22635,22636,22637,22638,22639,22640,22641,22642,22643,22644,22645,22646,22647,22648,22649,22650,22651,22652,22653,22654,22655,22656,22657,22658,22659,22660,22661,22662,22663,22664,22665,22666,22667,22668,22669,22670,22671,22672,22673,22674,22675,22676,22677,22678,22679,22680,22681,22682,22683,22684,22685,22686,22687,22688,22689,22690,22691,22692,22693,22694,22695,22696,22697,22698,22699,22700,22701,22702,22703,22704,22705,22706,22707,22708,22709,22710,22711,22712,22713,22714,22715,22716,22717,22718,22719,22720,22721,22722,22723,22724,22725,22726,22727,22728,22729,22730,22731,22732,22733,22734,22735,22736,22737,22738,22739,22740,22741,22742,22743,22744,22745,22746,22747,22748,22749,22750,22751,22752,22753,22754,22755,22756,22757,22758,22759,22760,22761,22762,22763,22764,22765,22766,22767,22768,22769,22770,22771,22772,22773,22774,22775,22776,22777,22778,22779,22780,22781,22782,22783,22784,22785,22786,22787,22788,22789,22790,22791,22792,22793,22794,22795,22796,22797,22798,22799,22800,22801,22802,22803,22804,22805,22806,22807,22808,22809,22810,22811,22812,22813,22814,22815,22816,22817,22818,22819,22820,22821,22822,22823,22824,22825,22826,22827,22828,22829,22830,22831,22832,22833,22834,22835,22836,22837,22838,22839,22840,22841,22842,22843,22844,22845,22846,22847,22848,22849,22850,22851,22852,22853,22854,22855,22856,22857,22858,22859,22860,22861,22862,22863,22864,22865,22866,22867,22868,22869,22870,22871,22872,22873,22874,22875,22876,22877,22878,22879,22880,22881,22882,22883,22884,22885,22886,22887,22888,22889,22890,22891,22892,22893,22894,22895,22896,22897,22898,22899,22900,22901,22902,22903,22904,22905,22906,22907,22908,22909,22910,22911,22912,22913,22914,22915,22916,22917,22918,22919,22920,22921,22922,22923,22924,22925,22926,22927,22928,22929,22930,22931,22932,22933,22934,22935,22936,22937,22938,22939,22940,22941,22942,22943,22944,22945,22946,22947,22948,22949,22950,22951,22952,22953,22954,22955,22956,22957,22958,22959,22960,22961,22962,22963,22964,22965,22966,22967,22968,22969,22970,22971,22972,22973,22974,22975,22976,22977,22978,22979,22980,22981,22982,22983,22984,22985,22986,22987,22988,22989,22990,22991,22992,22993,22994,22995,22996,22997,22998,22999,23000,23001,23002,23003,23004,23005,23006,23007,23008,23009,23010,23011,23012,23013,23014,23015,23016,23017,23018,23019,23020,23021,23022,23023,23024,23025,23026,23027,23028,23029,23030,23031,23032,23033,23034,23035,23036,23037,23038,23039,23040,23041,23042,23043,23044,23045,23046,23047,23048,23049,23050,23051,23052,23053,23054,23055,23056,23057,23058,23059,23060,23061,23062,23063,23064,23065,23066,23067,23068,23069,23070,23071,23072,23073,23074,23075,23076,23077,23078,23079,23080,23081,23082,23083,23084,23085,23086,23087,23088,23089,23090,23091,23092,23093,23094,23095,23096,23097,23098,23099,23100,23101,23102,23103,23104,23105,23106,23107,23108,23109,23110,23111,23112,23113,23114,23115,23116,23117,23118,23119,23120,23121,23122,23123,23124,23125,23126,23127,23128,23129,23130,23131,23132,23133,23134,23135,23136,23137,23138,23139,23140,23141,23142,23143,23144,23145,23146,23147,23148,23149,23150,23151,23152,23153,23154,23155,23156,23157,23158,23159,23160,23161,23162,23163,23164,23165,23166,23167,23168,23169,23170,23171,23172,23173,23174,23175,23176,23177,23178,23179,23180,23181,23182,23183,23184,23185,23186,23187,23188,23189,23190,23191,23192,23193,23194,23195,23196,23197,23198,23199,23200,23201,23202,23203,23204,23205,23206,23207,23208,23209,23210,23211,23212,23213,23214,23215,23216,23217,23218,23219,23220,23221,23222,23223,23224,23225,23226,23227,23228,23229,23230,23231,23232,23233,23234,23235,23236,23237,23238,23239,23240,23241,23242,23243,23244,23245,23246,23247,23248,23249,23250,23251,23252,23253,23254,23255,23256,23257,23258,23259,23260,23261,23262,23263,23264,23265,23266,23267,23268,23269,23270,23271,23272,23273,23274,23275,23276,23277,23278,23279,23280,23281,23282,23283,23284,23285,23286,23287,23288,23289,23290,23291,23292,23293,23294,23295,23296,23297,23298,23299,23300,23301,23302,23303,23304,23305,23306,23307,23308,23309,23310,23311,23312,23313,23314,23315,23316,23317,23318,23319,23320,23321,23322,23323,23324,23325,23326,23327,23328,23329,23330,23331,23332,23333,23334,23335,23336,23337,23338,23339,23340,23341,23342,23343,23344,23345,23346,23347,23348,23349,23350,23351,23352,23353,23354,23355,23356,23357,23358,23359,23360,23361,23362,23363,23364,23365,23366,23367,23368,23369,23370,23371,23372,23373,23374,23375,23376,23377,23378,23379,23380,23381,23382,23383,23384,23385,23386,23387,23388,23389,23390,23391,23392,23393,23394,23395,23396,23397,23398,23399,23400,23401,23402,23403,23404,23405,23406,23407,23408,23409,23410,23411,23412,23413,23414,23415,23416,23417,23418,23419,23420,23421,23422,23423,23424,23425,23426,23427,23428,23429,23430,23431,23432,23433,23434,23435,23436,23437,23438,23439,23440,23441,23442,23443,23444,23445,23446,23447,23448,23449,23450,23451,23452,23453,23454,23455,23456,23457,23458,23459,23460,23461,23462,23463,23464,23465,23466,23467,23468,23469,23470,23471,23472,23473,23474,23475,23476,23477,23478,23479,23480,23481,23482,23483,23484,23485,23486,23487,23488,23489,23490,23491,23492,23493,23494,23495,23496,23497,23498,23499,23500,23501,23502,23503,23504,23505,23506,23507,23508,23509,23510,23511,23512,23513,23514,23515,23516,23517,23518,23519,23520,23521,23522,23523,23524,23525,23526,23527,23528,23529,23530,23531,23532,23533,23534,23535,23536,23537,23538,23539,23540,23541,23542,23543,23544,23545,23546,23547,23548,23549,23550,23551,23552,23553,23554,23555,23556,23557,23558,23559,23560,23561,23562,23563,23564,23565,23566,23567,23568,23569,23570,23571,23572,23573,23574,23575,23576,23577,23578,23579,23580,23581,23582,23583,23584,23585,23586,23587,23588,23589,23590,23591,23592,23593,23594,23595,23596,23597,23598,23599,23600,23601,23602,23603,23604,23605,23606,23607,23608,23609,23610,23611,23612,23613,23614,23615,23616,23617,23618,23619,23620,23621,23622,23623,23624,23625,23626,23627,23628,23629,23630,23631,23632,23633,23634,23635,23636,23637,23638,23639,23640,23641,23642,23643,23644,23645,23646,23647,23648,23649,23650,23651,23652,23653,23654,23655,23656,23657,23658,23659,23660,23661,23662,23663,23664,23665,23666,23667,23668,23669,23670,23671,23672,23673,23674,23675,23676,23677,23678,23679,23680,23681,23682,23683,23684,23685,23686,23687,23688,23689,23690,23691,23692,23693,23694,23695,23696,23697,23698,23699,23700,23701,23702,23703,23704,23705,23706,23707,23708,23709,23710,23711,23712,23713,23714,23715,23716,23717,23718,23719,23720,23721,23722,23723,23724,23725,23726,23727,23728,23729,23730,23731,23732,23733,23734,23735,23736,23737,23738,23739,23740,23741,23742,23743,23744,23745,23746,23747,23748,23749,23750,23751,23752,23753,23754,23755,23756,23757,23758,23759,23760,23761,23762,23763,23764,23765,23766,23767,23768,23769,23770,23771,23772,23773,23774,23775,23776,23777,23778,23779,23780,23781,23782,23783,23784,23785,23786,23787,23788,23789,23790,23791,23792,23793,23794,23795,23796,23797,23798,23799,23800,23801,23802,23803,23804,23805,23806,23807,23808,23809,23810,23811,23812,23813,23814,23815,23816,23817,23818,23819,23820,23821,23822,23823,23824,23825,23826,23827,23828,23829,23830,23831,23832,23833,23834,23835,23836,23837,23838,23839,23840,23841,23842,23843,23844,23845,23846,23847,23848,23849,23850,23851,23852,23853,23854,23855,23856,23857,23858,23859,23860,23861,23862,23863,23864,23865,23866,23867,23868,23869,23870,23871,23872,23873,23874,23875,23876,23877,23878,23879,23880,23881,23882,23883,23884,23885,23886,23887,23888,23889,23890,23891,23892,23893,23894,23895,23896,23897,23898,23899,23900,23901,23902,23903,23904,23905,23906,23907,23908,23909,23910,23911,23912,23913,23914,23915,23916,23917,23918,23919,23920,23921,23922,23923,23924,23925,23926,23927,23928,23929,23930,23931,23932,23933,23934,23935,23936,23937,23938,23939,23940,23941,23942,23943,23944,23945,23946,23947,23948,23949,23950,23951,23952,23953,23954,23955,23956,23957,23958,23959,23960,23961,23962,23963,23964,23965,23966,23967,23968,23969,23970,23971,23972,23973,23974,23975,23976,23977,23978,23979,23980,23981,23982,23983,23984,23985,23986,23987,23988,23989,23990,23991,23992,23993,23994,23995,23996,23997,23998,23999,24000,24001,24002,24003,24004,24005,24006,24007,24008,24009,24010,24011,24012,24013,24014,24015,24016,24017,24018,24019,24020,24021,24022,24023,24024,24025,24026,24027,24028,24029,24030,24031,24032,24033,24034,24035,24036,24037,24038,24039,24040,24041,24042,24043,24044,24045,24046,24047,24048,24049,24050,24051,24052,24053,24054,24055,24056,24057,24058,24059,24060,24061,24062,24063,24064,24065,24066,24067,24068,24069,24070,24071,24072,24073,24074,24075,24076,24077,24078,24079,24080,24081,24082,24083,24084,24085,24086,24087,24088,24089,24090,24091,24092,24093,24094,24095,24096,24097,24098,24099,24100,24101,24102,24103,24104,24105,24106,24107,24108,24109,24110,24111,24112,24113,24114,24115,24116,24117,24118,24119,24120,24121,24122,24123,24124,24125,24126,24127,24128,24129,24130,24131,24132,24133,24134,24135,24136,24137,24138,24139,24140,24141,24142,24143,24144,24145,24146,24147,24148,24149,24150,24151,24152,24153,24154,24155,24156,24157,24158,24159,24160,24161,24162,24163,24164,24165,24166,24167,24168,24169,24170,24171,24172,24173,24174,24175,24176,24177,24178,24179,24180,24181,24182,24183,24184,24185,24186,24187,24188,24189,24190,24191,24192,24193,24194,24195,24196,24197,24198,24199,24200,24201,24202,24203,24204,24205,24206,24207,24208,24209,24210,24211,24212,24213,24214,24215,24216,24217,24218,24219,24220,24221,24222,24223,24224,24225,24226,24227,24228,24229,24230,24231,24232,24233,24234,24235,24236,24237,24238,24239,24240,24241,24242,24243,24244,24245,24246,24247,24248,24249,24250,24251,24252,24253,24254,24255,24256,24257,24258,24259,24260,24261,24262,24263,24264,24265,24266,24267,24268,24269,24270,24271,24272,24273,24274,24275,24276,24277,24278,24279,24280,24281,24282,24283,24284,24285,24286,24287,24288,24289,24290,24291,24292,24293,24294,24295,24296,24297,24298,24299,24300,24301,24302,24303,24304,24305,24306,24307,24308,24309,24310,24311,24312,24313,24314,24315,24316,24317,24318,24319,24320,24321,24322,24323,24324,24325,24326,24327,24328,24329,24330,24331,24332,24333,24334,24335,24336,24337,24338,24339,24340,24341,24342,24343,24344,24345,24346,24347,24348,24349,24350,24351,24352,24353,24354,24355,24356,24357,24358,24359,24360,24361,24362,24363,24364,24365,24366,24367,24368,24369,24370,24371,24372,24373,24374,24375,24376,24377,24378,24379,24380,24381,24382,24383,24384,24385,24386,24387,24388,24389,24390,24391,24392,24393,24394,24395,24396,24397,24398,24399,24400,24401,24402,24403,24404,24405,24406,24407,24408,24409,24410,24411,24412,24413,24414,24415,24416,24417,24418,24419,24420,24421,24422,24423,24424,24425,24426,24427,24428,24429,24430,24431,24432,24433,24434,24435,24436,24437,24438,24439,24440,24441,24442,24443,24444,24445,24446,24447,24448,24449,24450,24451,24452,24453,24454,24455,24456,24457,24458,24459,24460,24461,24462,24463,24464,24465,24466,24467,24468,24469,24470,24471,24472,24473,24474,24475,24476,24477,24478,24479,24480,24481,24482,24483,24484,24485,24486,24487,24488,24489,24490,24491,24492,24493,24494,24495,24496,24497,24498,24499,24500,24501,24502,24503,24504,24505,24506,24507,24508,24509,24510,24511,24512,24513,24514,24515,24516,24517,24518,24519,24520,24521,24522,24523,24524,24525,24526,24527,24528,24529,24530,24531,24532,24533,24534,24535,24536,24537,24538,24539,24540,24541,24542,24543,24544,24545,24546,24547,24548,24549,24550,24551,24552,24553,24554,24555,24556,24557,24558,24559,24560,24561,24562,24563,24564,24565,24566,24567,24568,24569,24570,24571,24572,24573,24574,24575,24576,24577,24578,24579,24580,24581,24582,24583,24584,24585,24586,24587,24588,24589,24590,24591,24592,24593,24594,24595,24596,24597,24598,24599,24600,24601,24602,24603,24604,24605,24606,24607,24608,24609,24610,24611,24612,24613,24614,24615,24616,24617,24618,24619,24620,24621,24622,24623,24624,24625,24626,24627,24628,24629,24630,24631,24632,24633,24634,24635,24636,24637,24638,24639,24640,24641,24642,24643,24644,24645,24646,24647,24648,24649,24650,24651,24652,24653,24654,24655,24656,24657,24658,24659,24660,24661,24662,24663,24664,24665,24666,24667,24668,24669,24670,24671,24672,24673,24674,24675,24676,24677,24678,24679,24680,24681,24682,24683,24684,24685,24686,24687,24688,24689,24690,24691,24692,24693,24694,24695,24696,24697,24698,24699,24700,24701,24702,24703,24704,24705,24706,24707,24708,24709,24710,24711,24712,24713,24714,24715,24716,24717,24718,24719,24720,24721,24722,24723,24724,24725,24726,24727,24728,24729,24730,24731,24732,24733,24734,24735,24736,24737,24738,24739,24740,24741,24742,24743,24744,24745,24746,24747,24748,24749,24750,24751,24752,24753,24754,24755,24756,24757,24758,24759,24760,24761,24762,24763,24764,24765,24766,24767,24768,24769,24770,24771,24772,24773,24774,24775,24776,24777,24778,24779,24780,24781,24782,24783,24784,24785,24786,24787,24788,24789,24790,24791,24792,24793,24794,24795,24796,24797,24798,24799,24800,24801,24802,24803,24804,24805,24806,24807,24808,24809,24810,24811,24812,24813,24814,24815,24816,24817,24818,24819,24820,24821,24822,24823,24824,24825,24826,24827,24828,24829,24830,24831,24832,24833,24834,24835,24836,24837,24838,24839,24840,24841,24842,24843,24844,24845,24846,24847,24848,24849,24850,24851,24852,24853,24854,24855,24856,24857,24858,24859,24860,24861,24862,24863,24864,24865,24866,24867,24868,24869,24870,24871,24872,24873,24874,24875,24876,24877,24878,24879,24880,24881,24882,24883,24884,24885,24886,24887,24888,24889,24890,24891,24892,24893,24894,24895,24896,24897,24898,24899,24900,24901,24902,24903,24904,24905,24906,24907,24908,24909,24910,24911,24912,24913,24914,24915,24916,24917,24918,24919,24920,24921,24922,24923,24924,24925,24926,24927,24928,24929,24930,24931,24932,24933,24934,24935,24936,24937,24938,24939,24940,24941,24942,24943,24944,24945,24946,24947,24948,24949,24950,24951,24952,24953,24954,24955,24956,24957,24958,24959,24960,24961,24962,24963,24964,24965,24966,24967,24968,24969,24970,24971,24972,24973,24974,24975,24976,24977,24978,24979,24980,24981,24982,24983,24984,24985,24986,24987,24988,24989,24990,24991,24992,24993,24994,24995,24996,24997,24998,24999,25000,25001,25002,25003,25004,25005,25006,25007,25008,25009,25010,25011,25012,25013,25014,25015,25016,25017,25018,25019,25020,25021,25022,25023,25024,25025,25026,25027,25028,25029,25030,25031,25032,25033,25034,25035,25036,25037,25038,25039,25040,25041,25042,25043,25044,25045,25046,25047,25048,25049,25050,25051,25052,25053,25054,25055,25056,25057,25058,25059,25060,25061,25062,25063,25064,25065,25066,25067,25068,25069,25070,25071,25072,25073,25074,25075,25076,25077,25078,25079,25080,25081,25082,25083,25084,25085,25086,25087,25088,25089,25090,25091,25092,25093,25094,25095,25096,25097,25098,25099,25100,25101,25102,25103,25104,25105,25106,25107,25108,25109,25110,25111,25112,25113,25114,25115,25116,25117,25118,25119,25120,25121,25122,25123,25124,25125,25126,25127,25128,25129,25130,25131,25132,25133,25134,25135,25136,25137,25138,25139,25140,25141,25142,25143,25144,25145,25146,25147,25148,25149,25150,25151,25152,25153,25154,25155,25156,25157,25158,25159,25160,25161,25162,25163,25164,25165,25166,25167,25168,25169,25170,25171,25172,25173,25174,25175,25176,25177,25178,25179,25180,25181,25182,25183,25184,25185,25186,25187,25188,25189,25190,25191,25192,25193,25194,25195,25196,25197,25198,25199,25200,25201,25202,25203,25204,25205,25206,25207,25208,25209,25210,25211,25212,25213,25214,25215,25216,25217,25218,25219,25220,25221,25222,25223,25224,25225,25226,25227,25228,25229,25230,25231,25232,25233,25234,25235,25236,25237,25238,25239,25240,25241,25242,25243,25244,25245,25246,25247,25248,25249,25250,25251,25252,25253,25254,25255,25256,25257,25258,25259,25260,25261,25262,25263,25264,25265,25266,25267,25268,25269,25270,25271,25272,25273,25274,25275,25276,25277,25278,25279,25280,25281,25282,25283,25284,25285,25286,25287,25288,25289,25290,25291,25292,25293,25294,25295,25296,25297,25298,25299,25300,25301,25302,25303,25304,25305,25306,25307,25308,25309,25310,25311,25312,25313,25314,25315,25316,25317,25318,25319,25320,25321,25322,25323,25324,25325,25326,25327,25328,25329,25330,25331,25332,25333,25334,25335,25336,25337,25338,25339,25340,25341,25342,25343,25344,25345,25346,25347,25348,25349,25350,25351,25352,25353,25354,25355,25356,25357,25358,25359,25360,25361,25362,25363,25364,25365,25366,25367,25368,25369,25370,25371,25372,25373,25374,25375,25376,25377,25378,25379,25380,25381,25382,25383,25384,25385,25386,25387,25388,25389,25390,25391,25392,25393,25394,25395,25396,25397,25398,25399,25400,25401,25402,25403,25404,25405,25406,25407,25408,25409,25410,25411,25412,25413,25414,25415,25416,25417,25418,25419,25420,25421,25422,25423,25424,25425,25426,25427,25428,25429,25430,25431,25432,25433,25434,25435,25436,25437,25438,25439,25440,25441,25442,25443,25444,25445,25446,25447,25448,25449,25450,25451,25452,25453,25454,25455,25456,25457,25458,25459,25460,25461,25462,25463,25464,25465,25466,25467,25468,25469,25470,25471,25472,25473,25474,25475,25476,25477,25478,25479,25480,25481,25482,25483,25484,25485,25486,25487,25488,25489,25490,25491,25492,25493,25494,25495,25496,25497,25498,25499,25500,25501,25502,25503,25504,25505,25506,25507,25508,25509,25510,25511,25512,25513,25514,25515,25516,25517,25518,25519,25520,25521,25522,25523,25524,25525,25526,25527,25528,25529,25530,25531,25532,25533,25534,25535,25536,25537,25538,25539,25540,25541,25542,25543,25544,25545,25546,25547,25548,25549,25550,25551,25552,25553,25554,25555,25556,25557,25558,25559,25560,25561,25562,25563,25564,25565,25566,25567,25568,25569,25570,25571,25572,25573,25574,25575,25576,25577,25578,25579,25580,25581,25582,25583,25584,25585,25586,25587,25588,25589,25590,25591,25592,25593,25594,25595,25596,25597,25598,25599,25600,25601,25602,25603,25604,25605,25606,25607,25608,25609,25610,25611,25612,25613,25614,25615,25616,25617,25618,25619,25620,25621,25622,25623,25624,25625,25626,25627,25628,25629,25630,25631,25632,25633,25634,25635,25636,25637,25638,25639,25640,25641,25642,25643,25644,25645,25646,25647,25648,25649,25650,25651,25652,25653,25654,25655,25656,25657,25658,25659,25660,25661,25662,25663,25664,25665,25666,25667,25668,25669,25670,25671,25672,25673,25674,25675,25676,25677,25678,25679,25680,25681,25682,25683,25684,25685,25686,25687,25688,25689,25690,25691,25692,25693,25694,25695,25696,25697,25698,25699,25700,25701,25702,25703,25704,25705,25706,25707,25708,25709,25710,25711,25712,25713,25714,25715,25716,25717,25718,25719,25720,25721,25722,25723,25724,25725,25726,25727,25728,25729,25730,25731,25732,25733,25734,25735,25736,25737,25738,25739,25740,25741,25742,25743,25744,25745,25746,25747,25748,25749,25750,25751,25752,25753,25754,25755,25756,25757,25758,25759,25760,25761,25762,25763,25764,25765,25766,25767,25768,25769,25770,25771,25772,25773,25774,25775,25776,25777,25778,25779,25780,25781,25782,25783,25784,25785,25786,25787,25788,25789,25790,25791,25792,25793,25794,25795,25796,25797,25798,25799,25800,25801,25802,25803,25804,25805,25806,25807,25808,25809,25810,25811,25812,25813,25814,25815,25816,25817,25818,25819,25820,25821,25822,25823,25824,25825,25826,25827,25828,25829,25830,25831,25832,25833,25834,25835,25836,25837,25838,25839,25840,25841,25842,25843,25844,25845,25846,25847,25848,25849,25850,25851,25852,25853,25854,25855,25856,25857,25858,25859,25860,25861,25862,25863,25864,25865,25866,25867,25868,25869,25870,25871,25872,25873,25874,25875,25876,25877,25878,25879,25880,25881,25882,25883,25884,25885,25886,25887,25888,25889,25890,25891,25892,25893,25894,25895,25896,25897,25898,25899,25900,25901,25902,25903,25904,25905,25906,25907,25908,25909,25910,25911,25912,25913,25914,25915,25916,25917,25918,25919,25920,25921,25922,25923,25924,25925,25926,25927,25928,25929,25930,25931,25932,25933,25934,25935,25936,25937,25938,25939,25940,25941,25942,25943,25944,25945,25946,25947,25948,25949,25950,25951,25952,25953,25954,25955,25956,25957,25958,25959,25960,25961,25962,25963,25964,25965,25966,25967,25968,25969,25970,25971,25972,25973,25974,25975,25976,25977,25978,25979,25980,25981,25982,25983,25984,25985,25986,25987,25988,25989,25990,25991,25992,25993,25994,25995,25996,25997,25998,25999,26000,26001,26002,26003,26004,26005,26006,26007,26008,26009,26010,26011,26012,26013,26014,26015,26016,26017,26018,26019,26020,26021,26022,26023,26024,26025,26026,26027,26028,26029,26030,26031,26032,26033,26034,26035,26036,26037,26038,26039,26040,26041,26042,26043,26044,26045,26046,26047,26048,26049,26050,26051,26052,26053,26054,26055,26056,26057,26058,26059,26060,26061,26062,26063,26064,26065,26066,26067,26068,26069,26070,26071,26072,26073,26074,26075,26076,26077,26078,26079,26080,26081,26082,26083,26084,26085,26086,26087,26088,26089,26090,26091,26092,26093,26094,26095,26096,26097,26098,26099,26100,26101,26102,26103,26104,26105,26106,26107,26108,26109,26110,26111,26112,26113,26114,26115,26116,26117,26118,26119,26120,26121,26122,26123,26124,26125,26126,26127,26128,26129,26130,26131,26132,26133,26134,26135,26136,26137,26138,26139,26140,26141,26142,26143,26144,26145,26146,26147,26148,26149,26150,26151,26152,26153,26154,26155,26156,26157,26158,26159,26160,26161,26162,26163,26164,26165,26166,26167,26168,26169,26170,26171,26172,26173,26174,26175,26176,26177,26178,26179,26180,26181,26182,26183,26184,26185,26186,26187,26188,26189,26190,26191,26192,26193,26194,26195,26196,26197,26198,26199,26200,26201,26202,26203,26204,26205,26206,26207,26208,26209,26210,26211,26212,26213,26214,26215,26216,26217,26218,26219,26220,26221,26222,26223,26224,26225,26226,26227,26228,26229,26230,26231,26232,26233,26234,26235,26236,26237,26238,26239,26240,26241,26242,26243,26244,26245,26246,26247,26248,26249,26250,26251,26252,26253,26254,26255,26256,26257,26258,26259,26260,26261,26262,26263,26264,26265,26266,26267,26268,26269,26270,26271,26272,26273,26274,26275,26276,26277,26278,26279,26280,26281,26282,26283,26284,26285,26286,26287,26288,26289,26290,26291,26292,26293,26294,26295,26296,26297,26298,26299,26300,26301,26302,26303,26304,26305,26306,26307,26308,26309,26310,26311,26312,26313,26314,26315,26316,26317,26318,26319,26320,26321,26322,26323,26324,26325,26326,26327,26328,26329,26330,26331,26332,26333,26334,26335,26336,26337,26338,26339,26340,26341,26342,26343,26344,26345,26346,26347,26348,26349,26350,26351,26352,26353,26354,26355,26356,26357,26358,26359,26360,26361,26362,26363,26364,26365,26366,26367,26368,26369,26370,26371,26372,26373,26374,26375,26376,26377,26378,26379,26380,26381,26382,26383,26384,26385,26386,26387,26388,26389,26390,26391,26392,26393,26394,26395,26396,26397,26398,26399,26400,26401,26402,26403,26404,26405,26406,26407,26408,26409,26410,26411,26412,26413,26414,26415,26416,26417,26418,26419,26420,26421,26422,26423,26424,26425,26426,26427,26428,26429,26430,26431,26432,26433,26434,26435,26436,26437,26438,26439,26440,26441,26442,26443,26444,26445,26446,26447,26448,26449,26450,26451,26452,26453,26454,26455,26456,26457,26458,26459,26460,26461,26462,26463,26464,26465,26466,26467,26468,26469,26470,26471,26472,26473,26474,26475,26476,26477,26478,26479,26480,26481,26482,26483,26484,26485,26486,26487,26488,26489,26490,26491,26492,26493,26494,26495,26496,26497,26498,26499,26500,26501,26502,26503,26504,26505,26506,26507,26508,26509,26510,26511,26512,26513,26514,26515,26516,26517,26518,26519,26520,26521,26522,26523,26524,26525,26526,26527,26528,26529,26530,26531,26532,26533,26534,26535,26536,26537,26538,26539,26540,26541,26542,26543,26544,26545,26546,26547,26548,26549,26550,26551,26552,26553,26554,26555,26556,26557,26558,26559,26560,26561,26562,26563,26564,26565,26566,26567,26568,26569,26570,26571,26572,26573,26574,26575,26576,26577,26578,26579,26580,26581,26582,26583,26584,26585,26586,26587,26588,26589,26590,26591,26592,26593,26594,26595,26596,26597,26598,26599,26600,26601,26602,26603,26604,26605,26606,26607,26608,26609,26610,26611,26612,26613,26614,26615,26616,26617,26618,26619,26620,26621,26622,26623,26624,26625,26626,26627,26628,26629,26630,26631,26632,26633,26634,26635,26636,26637,26638,26639,26640,26641,26642,26643,26644,26645,26646,26647,26648,26649,26650,26651,26652,26653,26654,26655,26656,26657,26658,26659,26660,26661,26662,26663,26664,26665,26666,26667,26668,26669,26670,26671,26672,26673,26674,26675,26676,26677,26678,26679,26680,26681,26682,26683,26684,26685,26686,26687,26688,26689,26690,26691,26692,26693,26694,26695,26696,26697,26698,26699,26700,26701,26702,26703,26704,26705,26706,26707,26708,26709,26710,26711,26712,26713,26714,26715,26716,26717,26718,26719,26720,26721,26722,26723,26724,26725,26726,26727,26728,26729,26730,26731,26732,26733,26734,26735,26736,26737,26738,26739,26740,26741,26742,26743,26744,26745,26746,26747,26748,26749,26750,26751,26752,26753,26754,26755,26756,26757,26758,26759,26760,26761,26762,26763,26764,26765,26766,26767,26768,26769,26770,26771,26772,26773,26774,26775,26776,26777,26778,26779,26780,26781,26782,26783,26784,26785,26786,26787,26788,26789,26790,26791,26792,26793,26794,26795,26796,26797,26798,26799,26800,26801,26802,26803,26804,26805,26806,26807,26808,26809,26810,26811,26812,26813,26814,26815,26816,26817,26818,26819,26820,26821,26822,26823,26824,26825,26826,26827,26828,26829,26830,26831,26832,26833,26834,26835,26836,26837,26838,26839,26840,26841,26842,26843,26844,26845,26846,26847,26848,26849,26850,26851,26852,26853,26854,26855,26856,26857,26858,26859,26860,26861,26862,26863,26864,26865,26866,26867,26868,26869,26870,26871,26872,26873,26874,26875,26876,26877,26878,26879,26880,26881,26882,26883,26884,26885,26886,26887,26888,26889,26890,26891,26892,26893,26894,26895,26896,26897,26898,26899,26900,26901,26902,26903,26904,26905,26906,26907,26908,26909,26910,26911,26912,26913,26914,26915,26916,26917,26918,26919,26920,26921,26922,26923,26924,26925,26926,26927,26928,26929,26930,26931,26932,26933,26934,26935,26936,26937,26938,26939,26940,26941,26942,26943,26944,26945,26946,26947,26948,26949,26950,26951,26952,26953,26954,26955,26956,26957,26958,26959,26960,26961,26962,26963,26964,26965,26966,26967,26968,26969,26970,26971,26972,26973,26974,26975,26976,26977,26978,26979,26980,26981,26982,26983,26984,26985,26986,26987,26988,26989,26990,26991,26992,26993,26994,26995,26996,26997,26998,26999,27000,27001,27002,27003,27004,27005,27006,27007,27008,27009,27010,27011,27012,27013,27014,27015,27016,27017,27018,27019,27020,27021,27022,27023,27024,27025,27026,27027,27028,27029,27030,27031,27032,27033,27034,27035,27036,27037,27038,27039,27040,27041,27042,27043,27044,27045,27046,27047,27048,27049,27050,27051,27052,27053,27054,27055,27056,27057,27058,27059,27060,27061,27062,27063,27064,27065,27066,27067,27068,27069,27070,27071,27072,27073,27074,27075,27076,27077,27078,27079,27080,27081,27082,27083,27084,27085,27086,27087,27088,27089,27090,27091,27092,27093,27094,27095,27096,27097,27098,27099,27100,27101,27102,27103,27104,27105,27106,27107,27108,27109,27110,27111,27112,27113,27114,27115,27116,27117,27118,27119,27120,27121,27122,27123,27124,27125,27126,27127,27128,27129,27130,27131,27132,27133,27134,27135,27136,27137,27138,27139,27140,27141,27142,27143,27144,27145,27146,27147,27148,27149,27150,27151,27152,27153,27154,27155,27156,27157,27158,27159,27160,27161,27162,27163,27164,27165,27166,27167,27168,27169,27170,27171,27172,27173,27174,27175,27176,27177,27178,27179,27180,27181,27182,27183,27184,27185,27186,27187,27188,27189,27190,27191,27192,27193,27194,27195,27196,27197,27198,27199,27200,27201,27202,27203,27204,27205,27206,27207,27208,27209,27210,27211,27212,27213,27214,27215,27216,27217,27218,27219,27220,27221,27222,27223,27224,27225,27226,27227,27228,27229,27230,27231,27232,27233,27234,27235,27236,27237,27238,27239,27240,27241,27242,27243,27244,27245,27246,27247,27248,27249,27250,27251,27252,27253,27254,27255,27256,27257,27258,27259,27260,27261,27262,27263,27264,27265,27266,27267,27268,27269,27270,27271,27272,27273,27274,27275,27276,27277,27278,27279,27280,27281,27282,27283,27284,27285,27286,27287,27288,27289,27290,27291,27292,27293,27294,27295,27296,27297,27298,27299,27300,27301,27302,27303,27304,27305,27306,27307,27308,27309,27310,27311,27312,27313,27314,27315,27316,27317,27318,27319,27320,27321,27322,27323,27324,27325,27326,27327,27328,27329,27330,27331,27332,27333,27334,27335,27336,27337,27338,27339,27340,27341,27342,27343,27344,27345,27346,27347,27348,27349,27350,27351,27352,27353,27354,27355,27356,27357,27358,27359,27360,27361,27362,27363,27364,27365,27366,27367,27368,27369,27370,27371,27372,27373,27374,27375,27376,27377,27378,27379,27380,27381,27382,27383,27384,27385,27386,27387,27388,27389,27390,27391,27392,27393,27394,27395,27396,27397,27398,27399,27400,27401,27402,27403,27404,27405,27406,27407,27408,27409,27410,27411,27412,27413,27414,27415,27416,27417,27418,27419,27420,27421,27422,27423,27424,27425,27426,27427,27428,27429,27430,27431,27432,27433,27434,27435,27436,27437,27438,27439,27440,27441,27442,27443,27444,27445,27446,27447,27448,27449,27450,27451,27452,27453,27454,27455,27456,27457,27458,27459,27460,27461,27462,27463,27464,27465,27466,27467,27468,27469,27470,27471,27472,27473,27474,27475,27476,27477,27478,27479,27480,27481,27482,27483,27484,27485,27486,27487,27488,27489,27490,27491,27492,27493,27494,27495,27496,27497,27498,27499,27500,27501,27502,27503,27504,27505,27506,27507,27508,27509,27510,27511,27512,27513,27514,27515,27516,27517,27518,27519,27520,27521,27522,27523,27524,27525,27526,27527,27528,27529,27530,27531,27532,27533,27534,27535,27536,27537,27538,27539,27540,27541,27542,27543,27544,27545,27546,27547,27548,27549,27550,27551,27552,27553,27554,27555,27556,27557,27558,27559,27560,27561,27562,27563,27564,27565,27566,27567,27568,27569,27570,27571,27572,27573,27574,27575,27576,27577,27578,27579,27580,27581,27582,27583,27584,27585,27586,27587,27588,27589,27590,27591,27592,27593,27594,27595,27596,27597,27598,27599,27600,27601,27602,27603,27604,27605,27606,27607,27608,27609,27610,27611,27612,27613,27614,27615,27616,27617,27618,27619,27620,27621,27622,27623,27624,27625,27626,27627,27628,27629,27630,27631,27632,27633,27634,27635,27636,27637,27638,27639,27640,27641,27642,27643,27644,27645,27646,27647,27648,27649,27650,27651,27652,27653,27654,27655,27656,27657,27658,27659,27660,27661,27662,27663,27664,27665,27666,27667,27668,27669,27670,27671,27672,27673,27674,27675,27676,27677,27678,27679,27680,27681,27682,27683,27684,27685,27686,27687,27688,27689,27690,27691,27692,27693,27694,27695,27696,27697,27698,27699,27700,27701,27702,27703,27704,27705,27706,27707,27708,27709,27710,27711,27712,27713,27714,27715,27716,27717,27718,27719,27720,27721,27722,27723,27724,27725,27726,27727,27728,27729,27730,27731,27732,27733,27734,27735,27736,27737,27738,27739,27740,27741,27742,27743,27744,27745,27746,27747,27748,27749,27750,27751,27752,27753,27754,27755,27756,27757,27758,27759,27760,27761,27762,27763,27764,27765,27766,27767,27768,27769,27770,27771,27772,27773,27774,27775,27776,27777,27778,27779,27780,27781,27782,27783,27784,27785,27786,27787,27788,27789,27790,27791,27792,27793,27794,27795,27796,27797,27798,27799,27800,27801,27802,27803,27804,27805,27806,27807,27808,27809,27810,27811,27812,27813,27814,27815,27816,27817,27818,27819,27820,27821,27822,27823,27824,27825,27826,27827,27828,27829,27830,27831,27832,27833,27834,27835,27836,27837,27838,27839,27840,27841,27842,27843,27844,27845,27846,27847,27848,27849,27850,27851,27852,27853,27854,27855,27856,27857,27858,27859,27860,27861,27862,27863,27864,27865,27866,27867,27868,27869,27870,27871,27872,27873,27874,27875,27876,27877,27878,27879,27880,27881,27882,27883,27884,27885,27886,27887,27888,27889,27890,27891,27892,27893,27894,27895,27896,27897,27898,27899,27900,27901,27902,27903,27904,27905,27906,27907,27908,27909,27910,27911,27912,27913,27914,27915,27916,27917,27918,27919,27920,27921,27922,27923,27924,27925,27926,27927,27928,27929,27930,27931,27932,27933,27934,27935,27936,27937,27938,27939,27940,27941,27942,27943,27944,27945,27946,27947,27948,27949,27950,27951,27952,27953,27954,27955,27956,27957,27958,27959,27960,27961,27962,27963,27964,27965,27966,27967,27968,27969,27970,27971,27972,27973,27974,27975,27976,27977,27978,27979,27980,27981,27982,27983,27984,27985,27986,27987,27988,27989,27990,27991,27992,27993,27994,27995,27996,27997,27998,27999,28000,28001,28002,28003,28004,28005,28006,28007,28008,28009,28010,28011,28012,28013,28014,28015,28016,28017,28018,28019,28020,28021,28022,28023,28024,28025,28026,28027,28028,28029,28030,28031,28032,28033,28034,28035,28036,28037,28038,28039,28040,28041,28042,28043,28044,28045,28046,28047,28048,28049,28050,28051,28052,28053,28054,28055,28056,28057,28058,28059,28060,28061,28062,28063,28064,28065,28066,28067,28068,28069,28070,28071,28072,28073,28074,28075,28076,28077,28078,28079,28080,28081,28082,28083,28084,28085,28086,28087,28088,28089,28090,28091,28092,28093,28094,28095,28096,28097,28098,28099,28100,28101,28102,28103,28104,28105,28106,28107,28108,28109,28110,28111,28112,28113,28114,28115,28116,28117,28118,28119,28120,28121,28122,28123,28124,28125,28126,28127,28128,28129,28130,28131,28132,28133,28134,28135,28136,28137,28138,28139,28140,28141,28142,28143,28144,28145,28146,28147,28148,28149,28150,28151,28152,28153,28154,28155,28156,28157,28158,28159,28160,28161,28162,28163,28164,28165,28166,28167,28168,28169,28170,28171,28172,28173,28174,28175,28176,28177,28178,28179,28180,28181,28182,28183,28184,28185,28186,28187,28188,28189,28190,28191,28192,28193,28194,28195,28196,28197,28198,28199,28200,28201,28202,28203,28204,28205,28206,28207,28208,28209,28210,28211,28212,28213,28214,28215,28216,28217,28218,28219,28220,28221,28222,28223,28224,28225,28226,28227,28228,28229,28230,28231,28232,28233,28234,28235,28236,28237,28238,28239,28240,28241,28242,28243,28244,28245,28246,28247,28248,28249,28250,28251,28252,28253,28254,28255,28256,28257,28258,28259,28260,28261,28262,28263,28264,28265,28266,28267,28268,28269,28270,28271,28272,28273,28274,28275,28276,28277,28278,28279,28280,28281,28282,28283,28284,28285,28286,28287,28288,28289,28290,28291,28292,28293,28294,28295,28296,28297,28298,28299,28300,28301,28302,28303,28304,28305,28306,28307,28308,28309,28310,28311,28312,28313,28314,28315,28316,28317,28318,28319,28320,28321,28322,28323,28324,28325,28326,28327,28328,28329,28330,28331,28332,28333,28334,28335,28336,28337,28338,28339,28340,28341,28342,28343,28344,28345,28346,28347,28348,28349,28350,28351,28352,28353,28354,28355,28356,28357,28358,28359,28360,28361,28362,28363,28364,28365,28366,28367,28368,28369,28370,28371,28372,28373,28374,28375,28376,28377,28378,28379,28380,28381,28382,28383,28384,28385,28386,28387,28388,28389,28390,28391,28392,28393,28394,28395,28396,28397,28398,28399,28400,28401,28402,28403,28404,28405,28406,28407,28408,28409,28410,28411,28412,28413,28414,28415,28416,28417,28418,28419,28420,28421,28422,28423,28424,28425,28426,28427,28428,28429,28430,28431,28432,28433,28434,28435,28436,28437,28438,28439,28440,28441,28442,28443,28444,28445,28446,28447,28448,28449,28450,28451,28452,28453,28454,28455,28456,28457,28458,28459,28460,28461,28462,28463,28464,28465,28466,28467,28468,28469,28470,28471,28472,28473,28474,28475,28476,28477,28478,28479,28480,28481,28482,28483,28484,28485,28486,28487,28488,28489,28490,28491,28492,28493,28494,28495,28496,28497,28498,28499,28500,28501,28502,28503,28504,28505,28506,28507,28508,28509,28510,28511,28512,28513,28514,28515,28516,28517,28518,28519,28520,28521,28522,28523,28524,28525,28526,28527,28528,28529,28530,28531,28532,28533,28534,28535,28536,28537,28538,28539,28540,28541,28542,28543,28544,28545,28546,28547,28548,28549,28550,28551,28552,28553,28554,28555,28556,28557,28558,28559,28560,28561,28562,28563,28564,28565,28566,28567,28568,28569,28570,28571,28572,28573,28574,28575,28576,28577,28578,28579,28580,28581,28582,28583,28584,28585,28586,28587,28588,28589,28590,28591,28592,28593,28594,28595,28596,28597,28598,28599,28600,28601,28602,28603,28604,28605,28606,28607,28608,28609,28610,28611,28612,28613,28614,28615,28616,28617,28618,28619,28620,28621,28622,28623,28624,28625,28626,28627,28628,28629,28630,28631,28632,28633,28634,28635,28636,28637,28638,28639,28640,28641,28642,28643,28644,28645,28646,28647,28648,28649,28650,28651,28652,28653,28654,28655,28656,28657,28658,28659,28660,28661,28662,28663,28664,28665,28666,28667,28668,28669,28670,28671,28672,28673,28674,28675,28676,28677,28678,28679,28680,28681,28682,28683,28684,28685,28686,28687,28688,28689,28690,28691,28692,28693,28694,28695,28696,28697,28698,28699,28700,28701,28702,28703,28704,28705,28706,28707,28708,28709,28710,28711,28712,28713,28714,28715,28716,28717,28718,28719,28720,28721,28722,28723,28724,28725,28726,28727,28728,28729,28730,28731,28732,28733,28734,28735,28736,28737,28738,28739,28740,28741,28742,28743,28744,28745,28746,28747,28748,28749,28750,28751,28752,28753,28754,28755,28756,28757,28758,28759,28760,28761,28762,28763,28764,28765,28766,28767,28768,28769,28770,28771,28772,28773,28774,28775,28776,28777,28778,28779,28780,28781,28782,28783,28784,28785,28786,28787,28788,28789,28790,28791,28792,28793,28794,28795,28796,28797,28798,28799,28800,28801,28802,28803,28804,28805,28806,28807,28808,28809,28810,28811,28812,28813,28814,28815,28816,28817,28818,28819,28820,28821,28822,28823,28824,28825,28826,28827,28828,28829,28830,28831,28832,28833,28834,28835,28836,28837,28838,28839,28840,28841,28842,28843,28844,28845,28846,28847,28848,28849,28850,28851,28852,28853,28854,28855,28856,28857,28858,28859,28860,28861,28862,28863,28864,28865,28866,28867,28868,28869,28870,28871,28872,28873,28874,28875,28876,28877,28878,28879,28880,28881,28882,28883,28884,28885,28886,28887,28888,28889,28890,28891,28892,28893,28894,28895,28896,28897,28898,28899,28900,28901,28902,28903,28904,28905,28906,28907,28908,28909,28910,28911,28912,28913,28914,28915,28916,28917,28918,28919,28920,28921,28922,28923,28924,28925,28926,28927,28928,28929,28930,28931,28932,28933,28934,28935,28936,28937,28938,28939,28940,28941,28942,28943,28944,28945,28946,28947,28948,28949,28950,28951,28952,28953,28954,28955,28956,28957,28958,28959,28960,28961,28962,28963,28964,28965,28966,28967,28968,28969,28970,28971,28972,28973,28974,28975,28976,28977,28978,28979,28980,28981,28982,28983,28984,28985,28986,28987,28988,28989,28990,28991,28992,28993,28994,28995,28996,28997,28998,28999,29000,29001,29002,29003,29004,29005,29006,29007,29008,29009,29010,29011,29012,29013,29014,29015,29016,29017,29018,29019,29020,29021,29022,29023,29024,29025,29026,29027,29028,29029,29030,29031,29032,29033,29034,29035,29036,29037,29038,29039,29040,29041,29042,29043,29044,29045,29046,29047,29048,29049,29050,29051,29052,29053,29054,29055,29056,29057,29058,29059,29060,29061,29062,29063,29064,29065,29066,29067,29068,29069,29070,29071,29072,29073,29074,29075,29076,29077,29078,29079,29080,29081,29082,29083,29084,29085,29086,29087,29088,29089,29090,29091,29092,29093,29094,29095,29096,29097,29098,29099,29100,29101,29102,29103,29104,29105,29106,29107,29108,29109,29110,29111,29112,29113,29114,29115,29116,29117,29118,29119,29120,29121,29122,29123,29124,29125,29126,29127,29128,29129,29130,29131,29132,29133,29134,29135,29136,29137,29138,29139,29140,29141,29142,29143,29144,29145,29146,29147,29148,29149,29150,29151,29152,29153,29154,29155,29156,29157,29158,29159,29160,29161,29162,29163,29164,29165,29166,29167,29168,29169,29170,29171,29172,29173,29174,29175,29176,29177,29178,29179,29180,29181,29182,29183,29184,29185,29186,29187,29188,29189,29190,29191,29192,29193,29194,29195,29196,29197,29198,29199,29200,29201,29202,29203,29204,29205,29206,29207,29208,29209,29210,29211,29212,29213,29214,29215,29216,29217,29218,29219,29220,29221,29222,29223,29224,29225,29226,29227,29228,29229,29230,29231,29232,29233,29234,29235,29236,29237,29238,29239,29240,29241,29242,29243,29244,29245,29246,29247,29248,29249,29250,29251,29252,29253,29254,29255,29256,29257,29258,29259,29260,29261,29262,29263,29264,29265,29266,29267,29268,29269,29270,29271,29272,29273,29274,29275,29276,29277,29278,29279,29280,29281,29282,29283,29284,29285,29286,29287,29288,29289,29290,29291,29292,29293,29294,29295,29296,29297,29298,29299,29300,29301,29302,29303,29304,29305,29306,29307,29308,29309,29310,29311,29312,29313,29314,29315,29316,29317,29318,29319,29320,29321,29322,29323,29324,29325,29326,29327,29328,29329,29330,29331,29332,29333,29334,29335,29336,29337,29338,29339,29340,29341,29342,29343,29344,29345,29346,29347,29348,29349,29350,29351,29352,29353,29354,29355,29356,29357,29358,29359,29360,29361,29362,29363,29364,29365,29366,29367,29368,29369,29370,29371,29372,29373,29374,29375,29376,29377,29378,29379,29380,29381,29382,29383,29384,29385,29386,29387,29388,29389,29390,29391,29392,29393,29394,29395,29396,29397,29398,29399,29400,29401,29402,29403,29404,29405,29406,29407,29408,29409,29410,29411,29412,29413,29414,29415,29416,29417,29418,29419,29420,29421,29422,29423,29424,29425,29426,29427,29428,29429,29430,29431,29432,29433,29434,29435,29436,29437,29438,29439,29440,29441,29442,29443,29444,29445,29446,29447,29448,29449,29450,29451,29452,29453,29454,29455,29456,29457,29458,29459,29460,29461,29462,29463,29464,29465,29466,29467,29468,29469,29470,29471,29472,29473,29474,29475,29476,29477,29478,29479,29480,29481,29482,29483,29484,29485,29486,29487,29488,29489,29490,29491,29492,29493,29494,29495,29496,29497,29498,29499,29500,29501,29502,29503,29504,29505,29506,29507,29508,29509,29510,29511,29512,29513,29514,29515,29516,29517,29518,29519,29520,29521,29522,29523,29524,29525,29526,29527,29528,29529,29530,29531,29532,29533,29534,29535,29536,29537,29538,29539,29540,29541,29542,29543,29544,29545,29546,29547,29548,29549,29550,29551,29552,29553,29554,29555,29556,29557,29558,29559,29560,29561,29562,29563,29564,29565,29566,29567,29568,29569,29570,29571,29572,29573,29574,29575,29576,29577,29578,29579,29580,29581,29582,29583,29584,29585,29586,29587,29588,29589,29590,29591,29592,29593,29594,29595,29596,29597,29598,29599,29600,29601,29602,29603,29604,29605,29606,29607,29608,29609,29610,29611,29612,29613,29614,29615,29616,29617,29618,29619,29620,29621,29622,29623,29624,29625,29626,29627,29628,29629,29630,29631,29632,29633,29634,29635,29636,29637,29638,29639,29640,29641,29642,29643,29644,29645,29646,29647,29648,29649,29650,29651,29652,29653,29654,29655,29656,29657,29658,29659,29660,29661,29662,29663,29664,29665,29666,29667,29668,29669,29670,29671,29672,29673,29674,29675,29676,29677,29678,29679,29680,29681,29682,29683,29684,29685,29686,29687,29688,29689,29690,29691,29692,29693,29694,29695,29696,29697,29698,29699,29700,29701,29702,29703,29704,29705,29706,29707,29708,29709,29710,29711,29712,29713,29714,29715,29716,29717,29718,29719,29720,29721,29722,29723,29724,29725,29726,29727,29728,29729,29730,29731,29732,29733,29734,29735,29736,29737,29738,29739,29740,29741,29742,29743,29744,29745,29746,29747,29748,29749,29750,29751,29752,29753,29754,29755,29756,29757,29758,29759,29760,29761,29762,29763,29764,29765,29766,29767,29768,29769,29770,29771,29772,29773,29774,29775,29776,29777,29778,29779,29780,29781,29782,29783,29784,29785,29786,29787,29788,29789,29790,29791,29792,29793,29794,29795,29796,29797,29798,29799,29800,29801,29802,29803,29804,29805,29806,29807,29808,29809,29810,29811,29812,29813,29814,29815,29816,29817,29818,29819,29820,29821,29822,29823,29824,29825,29826,29827,29828,29829,29830,29831,29832,29833,29834,29835,29836,29837,29838,29839,29840,29841,29842,29843,29844,29845,29846,29847,29848,29849,29850,29851,29852,29853,29854,29855,29856,29857,29858,29859,29860,29861,29862,29863,29864,29865,29866,29867,29868,29869,29870,29871,29872,29873,29874,29875,29876,29877,29878,29879,29880,29881,29882,29883,29884,29885,29886,29887,29888,29889,29890,29891,29892,29893,29894,29895,29896,29897,29898,29899,29900,29901,29902,29903,29904,29905,29906,29907,29908,29909,29910,29911,29912,29913,29914,29915,29916,29917,29918,29919,29920,29921,29922,29923,29924,29925,29926,29927,29928,29929,29930,29931,29932,29933,29934,29935,29936,29937,29938,29939,29940,29941,29942,29943,29944,29945,29946,29947,29948,29949,29950,29951,29952,29953,29954,29955,29956,29957,29958,29959,29960,29961,29962,29963,29964,29965,29966,29967,29968,29969,29970,29971,29972,29973,29974,29975,29976,29977,29978,29979,29980,29981,29982,29983,29984,29985,29986,29987,29988,29989,29990,29991,29992,29993,29994,29995,29996,29997,29998,29999,30000,30001,30002,30003,30004,30005,30006,30007,30008,30009,30010,30011,30012,30013,30014,30015,30016,30017,30018,30019,30020,30021,30022,30023,30024,30025,30026,30027,30028,30029,30030,30031,30032,30033,30034,30035,30036,30037,30038,30039,30040,30041,30042,30043,30044,30045,30046,30047,30048,30049,30050,30051,30052,30053,30054,30055,30056,30057,30058,30059,30060,30061,30062,30063,30064,30065,30066,30067,30068,30069,30070,30071,30072,30073,30074,30075,30076,30077,30078,30079,30080,30081,30082,30083,30084,30085,30086,30087,30088,30089,30090,30091,30092,30093,30094,30095,30096,30097,30098,30099,30100,30101,30102,30103,30104,30105,30106,30107,30108,30109,30110,30111,30112,30113,30114,30115,30116,30117,30118,30119,30120,30121,30122,30123,30124,30125,30126,30127,30128,30129,30130,30131,30132,30133,30134,30135,30136,30137,30138,30139,30140,30141,30142,30143,30144,30145,30146,30147,30148,30149,30150,30151,30152,30153,30154,30155,30156,30157,30158,30159,30160,30161,30162,30163,30164,30165,30166,30167,30168,30169,30170,30171,30172,30173,30174,30175,30176,30177,30178,30179,30180,30181,30182,30183,30184,30185,30186,30187,30188,30189,30190,30191,30192,30193,30194,30195,30196,30197,30198,30199,30200,30201,30202,30203,30204,30205,30206,30207,30208,30209,30210,30211,30212,30213,30214,30215,30216,30217,30218,30219,30220,30221,30222,30223,30224,30225,30226,30227,30228,30229,30230,30231,30232,30233,30234,30235,30236,30237,30238,30239,30240,30241,30242,30243,30244,30245,30246,30247,30248,30249,30250,30251,30252,30253,30254,30255,30256,30257,30258,30259,30260,30261,30262,30263,30264,30265,30266,30267,30268,30269,30270,30271,30272,30273,30274,30275,30276,30277,30278,30279,30280,30281,30282,30283,30284,30285,30286,30287,30288,30289,30290,30291,30292,30293,30294,30295,30296,30297,30298,30299,30300,30301,30302,30303,30304,30305,30306,30307,30308,30309,30310,30311,30312,30313,30314,30315,30316,30317,30318,30319,30320,30321,30322,30323,30324,30325,30326,30327,30328,30329,30330,30331,30332,30333,30334,30335,30336,30337,30338,30339,30340,30341,30342,30343,30344,30345,30346,30347,30348,30349,30350,30351,30352,30353,30354,30355,30356,30357,30358,30359,30360,30361,30362,30363,30364,30365,30366,30367,30368,30369,30370,30371,30372,30373,30374,30375,30376,30377,30378,30379,30380,30381,30382,30383,30384,30385,30386,30387,30388,30389,30390,30391,30392,30393,30394,30395,30396,30397,30398,30399,30400,30401,30402,30403,30404,30405,30406,30407,30408,30409,30410,30411,30412,30413,30414,30415,30416,30417,30418,30419,30420,30421,30422,30423,30424,30425,30426,30427,30428,30429,30430,30431,30432,30433,30434,30435,30436,30437,30438,30439,30440,30441,30442,30443,30444,30445,30446,30447,30448,30449,30450,30451,30452,30453,30454,30455,30456,30457,30458,30459,30460,30461,30462,30463,30464,30465,30466,30467,30468,30469,30470,30471,30472,30473,30474,30475,30476,30477,30478,30479,30480,30481,30482,30483,30484,30485,30486,30487,30488,30489,30490,30491,30492,30493,30494,30495,30496,30497,30498,30499,30500,30501,30502,30503,30504,30505,30506,30507,30508,30509,30510,30511,30512,30513,30514,30515,30516,30517,30518,30519,30520,30521,30522,30523,30524,30525,30526,30527,30528,30529,30530,30531,30532,30533,30534,30535,30536,30537,30538,30539,30540,30541,30542,30543,30544,30545,30546,30547,30548,30549,30550,30551,30552,30553,30554,30555,30556,30557,30558,30559,30560,30561,30562,30563,30564,30565,30566,30567,30568,30569,30570,30571,30572,30573,30574,30575,30576,30577,30578,30579,30580,30581,30582,30583,30584,30585,30586,30587,30588,30589,30590,30591,30592,30593,30594,30595,30596,30597,30598,30599,30600,30601,30602,30603,30604,30605,30606,30607,30608,30609,30610,30611,30612,30613,30614,30615,30616,30617,30618,30619,30620,30621,30622,30623,30624,30625,30626,30627,30628,30629,30630,30631,30632,30633,30634,30635,30636,30637,30638,30639,30640,30641,30642,30643,30644,30645,30646,30647,30648,30649,30650,30651,30652,30653,30654,30655,30656,30657,30658,30659,30660,30661,30662,30663,30664,30665,30666,30667,30668,30669,30670,30671,30672,30673,30674,30675,30676,30677,30678,30679,30680,30681,30682,30683,30684,30685,30686,30687,30688,30689,30690,30691,30692,30693,30694,30695,30696,30697,30698,30699,30700,30701,30702,30703,30704,30705,30706,30707,30708,30709,30710,30711,30712,30713,30714,30715,30716,30717,30718,30719,30720,30721,30722,30723,30724,30725,30726,30727,30728,30729,30730,30731,30732,30733,30734,30735,30736,30737,30738,30739,30740,30741,30742,30743,30744,30745,30746,30747,30748,30749,30750,30751,30752,30753,30754,30755,30756,30757,30758,30759,30760,30761,30762,30763,30764,30765,30766,30767,30768,30769,30770,30771,30772,30773,30774,30775,30776,30777,30778,30779,30780,30781,30782,30783,30784,30785,30786,30787,30788,30789,30790,30791,30792,30793,30794,30795,30796,30797,30798,30799,30800,30801,30802,30803,30804,30805,30806,30807,30808,30809,30810,30811,30812,30813,30814,30815,30816,30817,30818,30819,30820,30821,30822,30823,30824,30825,30826,30827,30828,30829,30830,30831,30832,30833,30834,30835,30836,30837,30838,30839,30840,30841,30842,30843,30844,30845,30846,30847,30848,30849,30850,30851,30852,30853,30854,30855,30856,30857,30858,30859,30860,30861,30862,30863,30864,30865,30866,30867,30868,30869,30870,30871,30872,30873,30874,30875,30876,30877,30878,30879,30880,30881,30882,30883,30884,30885,30886,30887,30888,30889,30890,30891,30892,30893,30894,30895,30896,30897,30898,30899,30900,30901,30902,30903,30904,30905,30906,30907,30908,30909,30910,30911,30912,30913,30914,30915,30916,30917,30918,30919,30920,30921,30922,30923,30924,30925,30926,30927,30928,30929,30930,30931,30932,30933,30934,30935,30936,30937,30938,30939,30940,30941,30942,30943,30944,30945,30946,30947,30948,30949,30950,30951,30952,30953,30954,30955,30956,30957,30958,30959,30960,30961,30962,30963,30964,30965,30966,30967,30968,30969,30970,30971,30972,30973,30974,30975,30976,30977,30978,30979,30980,30981,30982,30983,30984,30985,30986,30987,30988,30989,30990,30991,30992,30993,30994,30995,30996,30997,30998,30999,31000,31001,31002,31003,31004,31005,31006,31007,31008,31009,31010,31011,31012,31013,31014,31015,31016,31017,31018,31019,31020,31021,31022,31023,31024,31025,31026,31027,31028,31029,31030,31031,31032,31033,31034,31035,31036,31037,31038,31039,31040,31041,31042,31043,31044,31045,31046,31047,31048,31049,31050,31051,31052,31053,31054,31055,31056,31057,31058,31059,31060,31061,31062,31063,31064,31065,31066,31067,31068,31069,31070,31071,31072,31073,31074,31075,31076,31077,31078,31079,31080,31081,31082,31083,31084,31085,31086,31087,31088,31089,31090,31091,31092,31093,31094,31095,31096,31097,31098,31099,31100,31101,31102,31103,31104,31105,31106,31107,31108,31109,31110,31111,31112,31113,31114,31115,31116,31117,31118,31119,31120,31121,31122,31123,31124,31125,31126,31127,31128,31129,31130,31131,31132,31133,31134,31135,31136,31137,31138,31139,31140,31141,31142,31143,31144,31145,31146,31147,31148,31149,31150,31151,31152,31153,31154,31155,31156,31157,31158,31159,31160,31161,31162,31163,31164,31165,31166,31167,31168,31169,31170,31171,31172,31173,31174,31175,31176,31177,31178,31179,31180,31181,31182,31183,31184,31185,31186,31187,31188,31189,31190,31191,31192,31193,31194,31195,31196,31197,31198,31199,31200,31201,31202,31203,31204,31205,31206,31207,31208,31209,31210,31211,31212,31213,31214,31215,31216,31217,31218,31219,31220,31221,31222,31223,31224,31225,31226,31227,31228,31229,31230,31231,31232,31233,31234,31235,31236,31237,31238,31239,31240,31241,31242,31243,31244,31245,31246,31247,31248,31249,31250,31251,31252,31253,31254,31255,31256,31257,31258,31259,31260,31261,31262,31263,31264,31265,31266,31267,31268,31269,31270,31271,31272,31273,31274,31275,31276,31277,31278,31279,31280,31281,31282,31283,31284,31285,31286,31287,31288,31289,31290,31291,31292,31293,31294,31295,31296,31297,31298,31299,31300,31301,31302,31303,31304,31305,31306,31307,31308,31309,31310,31311,31312,31313,31314,31315,31316,31317,31318,31319,31320,31321,31322,31323,31324,31325,31326,31327,31328,31329,31330,31331,31332,31333,31334,31335,31336,31337,31338,31339,31340,31341,31342,31343,31344,31345,31346,31347,31348,31349,31350,31351,31352,31353,31354,31355,31356,31357,31358,31359,31360,31361,31362,31363,31364,31365,31366,31367,31368,31369,31370,31371,31372,31373,31374,31375,31376,31377,31378,31379,31380,31381,31382,31383,31384,31385,31386,31387,31388,31389,31390,31391,31392,31393,31394,31395,31396,31397,31398,31399,31400,31401,31402,31403,31404,31405,31406,31407,31408,31409,31410,31411,31412,31413,31414,31415,31416,31417,31418,31419,31420,31421,31422,31423,31424,31425,31426,31427,31428,31429,31430,31431,31432,31433,31434,31435,31436,31437,31438,31439,31440,31441,31442,31443,31444,31445,31446,31447,31448,31449,31450,31451,31452,31453,31454,31455,31456,31457,31458,31459,31460,31461,31462,31463,31464,31465,31466,31467,31468,31469,31470,31471,31472,31473,31474,31475,31476,31477,31478,31479,31480,31481,31482,31483,31484,31485,31486,31487,31488,31489,31490,31491,31492,31493,31494,31495,31496,31497,31498,31499,31500,31501,31502,31503,31504,31505,31506,31507,31508,31509,31510,31511,31512,31513,31514,31515,31516,31517,31518,31519,31520,31521,31522,31523,31524,31525,31526,31527,31528,31529,31530,31531,31532,31533,31534,31535,31536,31537,31538,31539,31540,31541,31542,31543,31544,31545,31546,31547,31548,31549,31550,31551,31552,31553,31554,31555,31556,31557,31558,31559,31560,31561,31562,31563,31564,31565,31566,31567,31568,31569,31570,31571,31572,31573,31574,31575,31576,31577,31578,31579,31580,31581,31582,31583,31584,31585,31586,31587,31588,31589,31590,31591,31592,31593,31594,31595,31596,31597,31598,31599,31600,31601,31602,31603,31604,31605,31606,31607,31608,31609,31610,31611,31612,31613,31614,31615,31616,31617,31618,31619,31620,31621,31622,31623,31624,31625,31626,31627,31628,31629,31630,31631,31632,31633,31634,31635,31636,31637,31638,31639,31640,31641,31642,31643,31644,31645,31646,31647,31648,31649,31650,31651,31652,31653,31654,31655,31656,31657,31658,31659,31660,31661,31662,31663,31664,31665,31666,31667,31668,31669,31670,31671,31672,31673,31674,31675,31676,31677,31678,31679,31680,31681,31682,31683,31684,31685,31686,31687,31688,31689,31690,31691,31692,31693,31694,31695,31696,31697,31698,31699,31700,31701,31702,31703,31704,31705,31706,31707,31708,31709,31710,31711,31712,31713,31714,31715,31716,31717,31718,31719,31720,31721,31722,31723,31724,31725,31726,31727,31728,31729,31730,31731,31732,31733,31734,31735,31736,31737,31738,31739,31740,31741,31742,31743,31744,31745,31746,31747,31748,31749,31750,31751,31752,31753,31754,31755,31756,31757,31758,31759,31760,31761,31762,31763,31764,31765,31766,31767,31768,31769,31770,31771,31772,31773,31774,31775,31776,31777,31778,31779,31780,31781,31782,31783,31784,31785,31786,31787,31788,31789,31790,31791,31792,31793,31794,31795,31796,31797,31798,31799,31800,31801,31802,31803,31804,31805,31806,31807,31808,31809,31810,31811,31812,31813,31814,31815,31816,31817,31818,31819,31820,31821,31822,31823,31824,31825,31826,31827,31828,31829,31830,31831,31832,31833,31834,31835,31836,31837,31838,31839,31840,31841,31842,31843,31844,31845,31846,31847,31848,31849,31850,31851,31852,31853,31854,31855,31856,31857,31858,31859,31860,31861,31862,31863,31864,31865,31866,31867,31868,31869,31870,31871,31872,31873,31874,31875,31876,31877,31878,31879,31880,31881,31882,31883,31884,31885,31886,31887,31888,31889,31890,31891,31892,31893,31894,31895,31896,31897,31898,31899,31900,31901,31902,31903,31904,31905,31906,31907,31908,31909,31910,31911,31912,31913,31914,31915,31916,31917,31918,31919,31920,31921,31922,31923,31924,31925,31926,31927,31928,31929,31930,31931,31932,31933,31934,31935,31936,31937,31938,31939,31940,31941,31942,31943,31944,31945,31946,31947,31948,31949,31950,31951,31952,31953,31954,31955,31956,31957,31958,31959,31960,31961,31962,31963,31964,31965,31966,31967,31968,31969,31970,31971,31972,31973,31974,31975,31976,31977,31978,31979,31980,31981,31982,31983,31984,31985,31986,31987,31988,31989,31990,31991,31992,31993,31994,31995,31996,31997,31998,31999,32000,32001,32002,32003,32004,32005,32006,32007,32008,32009,32010,32011,32012,32013,32014,32015,32016,32017,32018,32019,32020,32021,32022,32023,32024,32025,32026,32027,32028,32029,32030,32031,32032,32033,32034,32035,32036,32037,32038,32039,32040,32041,32042,32043,32044,32045,32046,32047,32048,32049,32050,32051,32052,32053,32054,32055,32056,32057,32058,32059,32060,32061,32062,32063,32064,32065,32066,32067,32068,32069,32070,32071,32072,32073,32074,32075,32076,32077,32078,32079,32080,32081,32082,32083,32084,32085,32086,32087,32088,32089,32090,32091,32092,32093,32094,32095,32096,32097,32098,32099,32100,32101,32102,32103,32104,32105,32106,32107,32108,32109,32110,32111,32112,32113,32114,32115,32116,32117,32118,32119,32120,32121,32122,32123,32124,32125,32126,32127,32128,32129,32130,32131,32132,32133,32134,32135,32136,32137,32138,32139,32140,32141,32142,32143,32144,32145,32146,32147,32148,32149,32150,32151,32152,32153,32154,32155,32156,32157,32158,32159,32160,32161,32162,32163,32164,32165,32166,32167,32168,32169,32170,32171,32172,32173,32174,32175,32176,32177,32178,32179,32180,32181,32182,32183,32184,32185,32186,32187,32188,32189,32190,32191,32192,32193,32194,32195,32196,32197,32198,32199,32200,32201,32202,32203,32204,32205,32206,32207,32208,32209,32210,32211,32212,32213,32214,32215,32216,32217,32218,32219,32220,32221,32222,32223,32224,32225,32226,32227,32228,32229,32230,32231,32232,32233,32234,32235,32236,32237,32238,32239,32240,32241,32242,32243,32244,32245,32246,32247,32248,32249,32250,32251,32252,32253,32254,32255,32256,32257,32258,32259,32260,32261,32262,32263,32264,32265,32266,32267,32268,32269,32270,32271,32272,32273,32274,32275,32276,32277,32278,32279,32280,32281,32282,32283,32284,32285,32286,32287,32288,32289,32290,32291,32292,32293,32294,32295,32296,32297,32298,32299,32300,32301,32302,32303,32304,32305,32306,32307,32308,32309,32310,32311,32312,32313,32314,32315,32316,32317,32318,32319,32320,32321,32322,32323,32324,32325,32326,32327,32328,32329,32330,32331,32332,32333,32334,32335,32336,32337,32338,32339,32340,32341,32342,32343,32344,32345,32346,32347,32348,32349,32350,32351,32352,32353,32354,32355,32356,32357,32358,32359,32360,32361,32362,32363,32364,32365,32366,32367,32368,32369,32370,32371,32372,32373,32374,32375,32376,32377,32378,32379,32380,32381,32382,32383,32384,32385,32386,32387,32388,32389,32390,32391,32392,32393,32394,32395,32396,32397,32398,32399,32400,32401,32402,32403,32404,32405,32406,32407,32408,32409,32410,32411,32412,32413,32414,32415,32416,32417,32418,32419,32420,32421,32422,32423,32424,32425,32426,32427,32428,32429,32430,32431,32432,32433,32434,32435,32436,32437,32438,32439,32440,32441,32442,32443,32444,32445,32446,32447,32448,32449,32450,32451,32452,32453,32454,32455,32456,32457,32458,32459,32460,32461,32462,32463,32464,32465,32466,32467,32468,32469,32470,32471,32472,32473,32474,32475,32476,32477,32478,32479,32480,32481,32482,32483,32484,32485,32486,32487,32488,32489,32490,32491,32492,32493,32494,32495,32496,32497,32498,32499,32500,32501,32502,32503,32504,32505,32506,32507,32508,32509,32510,32511,32512,32513,32514,32515,32516,32517,32518,32519,32520,32521,32522,32523,32524,32525,32526,32527,32528,32529,32530,32531,32532,32533,32534,32535,32536,32537,32538,32539,32540,32541,32542,32543,32544,32545,32546,32547,32548,32549,32550,32551,32552,32553,32554,32555,32556,32557,32558,32559,32560,32561,32562,32563,32564,32565,32566,32567,32568,32569,32570,32571,32572,32573,32574,32575,32576,32577,32578,32579,32580,32581,32582,32583,32584,32585,32586,32587,32588,32589,32590,32591,32592,32593,32594,32595,32596,32597,32598,32599,32600,32601,32602,32603,32604,32605,32606,32607,32608,32609,32610,32611,32612,32613,32614,32615,32616,32617,32618,32619,32620,32621,32622,32623,32624,32625,32626,32627,32628,32629,32630,32631,32632,32633,32634,32635,32636,32637,32638,32639,32640,32641,32642,32643,32644,32645,32646,32647,32648,32649,32650,32651,32652,32653,32654,32655,32656,32657,32658,32659,32660,32661,32662,32663,32664,32665,32666,32667,32668,32669,32670,32671,32672,32673,32674,32675,32676,32677,32678,32679,32680,32681,32682,32683,32684,32685,32686,32687,32688,32689,32690,32691,32692,32693,32694,32695,32696,32697,32698,32699,32700,32701,32702,32703,32704,32705,32706,32707,32708,32709,32710,32711,32712,32713,32714,32715,32716,32717,32718,32719,32720,32721,32722,32723,32724,32725,32726,32727,32728,32729,32730,32731,32732,32733,32734,32735,32736,32737,32738,32739,32740,32741,32742,32743,32744,32745,32746,32747,32748,32749,32750,32751,32752,32753,32754,32755,32756,32757,32758,32759,32760,32761,32762,32763,32764,32765,32766,32767,32768,32769,32770,32771,32772,32773,32774,32775,32776,32777,32778,32779,32780,32781,32782,32783,32784,32785,32786,32787,32788,32789,32790,32791,32792,32793,32794,32795,32796,32797,32798,32799,32800,32801,32802,32803,32804,32805,32806,32807,32808,32809,32810,32811,32812,32813,32814,32815,32816,32817,32818,32819,32820,32821,32822,32823,32824,32825,32826,32827,32828,32829,32830,32831,32832,32833,32834,32835,32836,32837,32838,32839,32840,32841,32842,32843,32844,32845,32846,32847,32848,32849,32850,32851,32852,32853,32854,32855,32856,32857,32858,32859,32860,32861,32862,32863,32864,32865,32866,32867,32868,32869,32870,32871,32872,32873,32874,32875,32876,32877,32878,32879,32880,32881,32882,32883,32884,32885,32886,32887,32888,32889,32890,32891,32892,32893,32894,32895,32896,32897,32898,32899,32900,32901,32902,32903,32904,32905,32906,32907,32908,32909,32910,32911,32912,32913,32914,32915,32916,32917,32918,32919,32920,32921,32922,32923,32924,32925,32926,32927,32928,32929,32930,32931,32932,32933,32934,32935,32936,32937,32938,32939,32940,32941,32942,32943,32944,32945,32946,32947,32948,32949,32950,32951,32952,32953,32954,32955,32956,32957,32958,32959,32960,32961,32962,32963,32964,32965,32966,32967,32968,32969,32970,32971,32972,32973,32974,32975,32976,32977,32978,32979,32980,32981,32982,32983,32984,32985,32986,32987,32988,32989,32990,32991,32992,32993,32994,32995,32996,32997,32998,32999,33000,33001,33002,33003,33004,33005,33006,33007,33008,33009,33010,33011,33012,33013,33014,33015,33016,33017,33018,33019,33020,33021,33022,33023,33024,33025,33026,33027,33028,33029,33030,33031,33032,33033,33034,33035,33036,33037,33038,33039,33040,33041,33042,33043,33044,33045,33046,33047,33048,33049,33050,33051,33052,33053,33054,33055,33056,33057,33058,33059,33060,33061,33062,33063,33064,33065,33066,33067,33068,33069,33070,33071,33072,33073,33074,33075,33076,33077,33078,33079,33080,33081,33082,33083,33084,33085,33086,33087,33088,33089,33090,33091,33092,33093,33094,33095,33096,33097,33098,33099,33100,33101,33102,33103,33104,33105,33106,33107,33108,33109,33110,33111,33112,33113,33114,33115,33116,33117,33118,33119,33120,33121,33122,33123,33124,33125,33126,33127,33128,33129,33130,33131,33132,33133,33134,33135,33136,33137,33138,33139,33140,33141,33142,33143,33144,33145,33146,33147,33148,33149,33150,33151,33152,33153,33154,33155,33156,33157,33158,33159,33160,33161,33162,33163,33164,33165,33166,33167,33168,33169,33170,33171,33172,33173,33174,33175,33176,33177,33178,33179,33180,33181,33182,33183,33184,33185,33186,33187,33188,33189,33190,33191,33192,33193,33194,33195,33196,33197,33198,33199,33200,33201,33202,33203,33204,33205,33206,33207,33208,33209,33210,33211,33212,33213,33214,33215,33216,33217,33218,33219,33220,33221,33222,33223,33224,33225,33226,33227,33228,33229,33230,33231,33232,33233,33234,33235,33236,33237,33238,33239,33240,33241,33242,33243,33244,33245,33246,33247,33248,33249,33250,33251,33252,33253,33254,33255,33256,33257,33258,33259,33260,33261,33262,33263,33264,33265,33266,33267,33268,33269,33270,33271,33272,33273,33274,33275,33276,33277,33278,33279,33280,33281,33282,33283,33284,33285,33286,33287,33288,33289,33290,33291,33292,33293,33294,33295,33296,33297,33298,33299,33300,33301,33302,33303,33304,33305,33306,33307,33308,33309,33310,33311,33312,33313,33314,33315,33316,33317,33318,33319,33320,33321,33322,33323,33324,33325,33326,33327,33328,33329,33330,33331,33332,33333,33334,33335,33336,33337,33338,33339,33340,33341,33342,33343,33344,33345,33346,33347,33348,33349,33350,33351,33352,33353,33354,33355,33356,33357,33358,33359,33360,33361,33362,33363,33364,33365,33366,33367,33368,33369,33370,33371,33372,33373,33374,33375,33376,33377,33378,33379,33380,33381,33382,33383,33384,33385,33386,33387,33388,33389,33390,33391,33392,33393,33394,33395,33396,33397,33398,33399,33400,33401,33402,33403,33404,33405,33406,33407,33408,33409,33410,33411,33412,33413,33414,33415,33416,33417,33418,33419,33420,33421,33422,33423,33424,33425,33426,33427,33428,33429,33430,33431,33432,33433,33434,33435,33436,33437,33438,33439,33440,33441,33442,33443,33444,33445,33446,33447,33448,33449,33450,33451,33452,33453,33454,33455,33456,33457,33458,33459,33460,33461,33462,33463,33464,33465,33466,33467,33468,33469,33470,33471,33472,33473,33474,33475,33476,33477,33478,33479,33480,33481,33482,33483,33484,33485,33486,33487,33488,33489,33490,33491,33492,33493,33494,33495,33496,33497,33498,33499,33500,33501,33502,33503,33504,33505,33506,33507,33508,33509,33510,33511,33512,33513,33514,33515,33516,33517,33518,33519,33520,33521,33522,33523,33524,33525,33526,33527,33528,33529,33530,33531,33532,33533,33534,33535,33536,33537,33538,33539,33540,33541,33542,33543,33544,33545,33546,33547,33548,33549,33550,33551,33552,33553,33554,33555,33556,33557,33558,33559,33560,33561,33562,33563,33564,33565,33566,33567,33568,33569,33570,33571,33572,33573,33574,33575,33576,33577,33578,33579,33580,33581,33582,33583,33584,33585,33586,33587,33588,33589,33590,33591,33592,33593,33594,33595,33596,33597,33598,33599,33600,33601,33602,33603,33604,33605,33606,33607,33608,33609,33610,33611,33612,33613,33614,33615,33616,33617,33618,33619,33620,33621,33622,33623,33624,33625,33626,33627,33628,33629,33630,33631,33632,33633,33634,33635,33636,33637,33638,33639,33640,33641,33642,33643,33644,33645,33646,33647,33648,33649,33650,33651,33652,33653,33654,33655,33656,33657,33658,33659,33660,33661,33662,33663,33664,33665,33666,33667,33668,33669,33670,33671,33672,33673,33674,33675,33676,33677,33678,33679,33680,33681,33682,33683,33684,33685,33686,33687,33688,33689,33690,33691,33692,33693,33694,33695,33696,33697,33698,33699,33700,33701,33702,33703,33704,33705,33706,33707,33708,33709,33710,33711,33712,33713,33714,33715,33716,33717,33718,33719,33720,33721,33722,33723,33724,33725,33726,33727,33728,33729,33730,33731,33732,33733,33734,33735,33736,33737,33738,33739,33740,33741,33742,33743,33744,33745,33746,33747,33748,33749,33750,33751,33752,33753,33754,33755,33756,33757,33758,33759,33760,33761,33762,33763,33764,33765,33766,33767,33768,33769,33770,33771,33772,33773,33774,33775,33776,33777,33778,33779,33780,33781,33782,33783,33784,33785,33786,33787,33788,33789,33790,33791,33792,33793,33794,33795,33796,33797,33798,33799,33800,33801,33802,33803,33804,33805,33806,33807,33808,33809,33810,33811,33812,33813,33814,33815,33816,33817,33818,33819,33820,33821,33822,33823,33824,33825,33826,33827,33828,33829,33830,33831,33832,33833,33834,33835,33836,33837,33838,33839,33840,33841,33842,33843,33844,33845,33846,33847,33848,33849,33850,33851,33852,33853,33854,33855,33856,33857,33858,33859,33860,33861,33862,33863,33864,33865,33866,33867,33868,33869,33870,33871,33872,33873,33874,33875,33876,33877,33878,33879,33880,33881,33882,33883,33884,33885,33886,33887,33888,33889,33890,33891,33892,33893,33894,33895,33896,33897,33898,33899,33900,33901,33902,33903,33904,33905,33906,33907,33908,33909,33910,33911,33912,33913,33914,33915,33916,33917,33918,33919,33920,33921,33922,33923,33924,33925,33926,33927,33928,33929,33930,33931,33932,33933,33934,33935,33936,33937,33938,33939,33940,33941,33942,33943,33944,33945,33946,33947,33948,33949,33950,33951,33952,33953,33954,33955,33956,33957,33958,33959,33960,33961,33962,33963,33964,33965,33966,33967,33968,33969,33970,33971,33972,33973,33974,33975,33976,33977,33978,33979,33980,33981,33982,33983,33984,33985,33986,33987,33988,33989,33990,33991,33992,33993,33994,33995,33996,33997,33998,33999,34000,34001,34002,34003,34004,34005,34006,34007,34008,34009,34010,34011,34012,34013,34014,34015,34016,34017,34018,34019,34020,34021,34022,34023,34024,34025,34026,34027,34028,34029,34030,34031,34032,34033,34034,34035,34036,34037,34038,34039,34040,34041,34042,34043,34044,34045,34046,34047,34048,34049,34050,34051,34052,34053,34054,34055,34056,34057,34058,34059,34060,34061,34062,34063,34064,34065,34066,34067,34068,34069,34070,34071,34072,34073,34074,34075,34076,34077,34078,34079,34080,34081,34082,34083,34084,34085,34086,34087,34088,34089,34090,34091,34092,34093,34094,34095,34096,34097,34098,34099,34100,34101,34102,34103,34104,34105,34106,34107,34108,34109,34110,34111,34112,34113,34114,34115,34116,34117,34118,34119,34120,34121,34122,34123,34124,34125,34126,34127,34128,34129,34130,34131,34132,34133,34134,34135,34136,34137,34138,34139,34140,34141,34142,34143,34144,34145,34146,34147,34148,34149,34150,34151,34152,34153,34154,34155,34156,34157,34158,34159,34160,34161,34162,34163,34164,34165,34166,34167,34168,34169,34170,34171,34172,34173,34174,34175,34176,34177,34178,34179,34180,34181,34182,34183,34184,34185,34186,34187,34188,34189,34190,34191,34192,34193,34194,34195,34196,34197,34198,34199,34200,34201,34202,34203,34204,34205,34206,34207,34208,34209,34210,34211,34212,34213,34214,34215,34216,34217,34218,34219,34220,34221,34222,34223,34224,34225,34226,34227,34228,34229,34230,34231,34232,34233,34234,34235,34236,34237,34238,34239,34240,34241,34242,34243,34244,34245,34246,34247,34248,34249,34250,34251,34252,34253,34254,34255,34256,34257,34258,34259,34260,34261,34262,34263,34264,34265,34266,34267,34268,34269,34270,34271,34272,34273,34274,34275,34276,34277,34278,34279,34280,34281,34282,34283,34284,34285,34286,34287,34288,34289,34290,34291,34292,34293,34294,34295,34296,34297,34298,34299,34300,34301,34302,34303,34304,34305,34306,34307,34308,34309,34310,34311,34312,34313,34314,34315,34316,34317,34318,34319,34320,34321,34322,34323,34324,34325,34326,34327,34328,34329,34330,34331,34332,34333,34334,34335,34336,34337,34338,34339,34340,34341,34342,34343,34344,34345,34346,34347,34348,34349,34350,34351,34352,34353,34354,34355,34356,34357,34358,34359,34360,34361,34362,34363,34364,34365,34366,34367,34368,34369,34370,34371,34372,34373,34374,34375,34376,34377,34378,34379,34380,34381,34382,34383,34384,34385,34386,34387,34388,34389,34390,34391,34392,34393,34394,34395,34396,34397,34398,34399,34400,34401,34402,34403,34404,34405,34406,34407,34408,34409,34410,34411,34412,34413,34414,34415,34416,34417,34418,34419,34420,34421,34422,34423,34424,34425,34426,34427,34428,34429,34430,34431,34432,34433,34434,34435,34436,34437,34438,34439,34440,34441,34442,34443,34444,34445,34446,34447,34448,34449,34450,34451,34452,34453,34454,34455,34456,34457,34458,34459,34460,34461,34462,34463,34464,34465,34466,34467,34468,34469,34470,34471,34472,34473,34474,34475,34476,34477,34478,34479,34480,34481,34482,34483,34484,34485,34486,34487,34488,34489,34490,34491,34492,34493,34494,34495,34496,34497,34498,34499,34500,34501,34502,34503,34504,34505,34506,34507,34508,34509,34510,34511,34512,34513,34514,34515,34516,34517,34518,34519,34520,34521,34522,34523,34524,34525,34526,34527,34528,34529,34530,34531,34532,34533,34534,34535,34536,34537,34538,34539,34540,34541,34542,34543,34544,34545,34546,34547,34548,34549,34550,34551,34552,34553,34554,34555,34556,34557,34558,34559,34560,34561,34562,34563,34564,34565,34566,34567,34568,34569,34570,34571,34572,34573,34574,34575,34576,34577,34578,34579,34580,34581,34582,34583,34584,34585,34586,34587,34588,34589,34590,34591,34592,34593,34594,34595,34596,34597,34598,34599,34600,34601,34602,34603,34604,34605,34606,34607,34608,34609,34610,34611,34612,34613,34614,34615,34616,34617,34618,34619,34620,34621,34622,34623,34624,34625,34626,34627,34628,34629,34630,34631,34632,34633,34634,34635,34636,34637,34638,34639,34640,34641,34642,34643,34644,34645,34646,34647,34648,34649,34650,34651,34652,34653,34654,34655,34656,34657,34658,34659,34660,34661,34662,34663,34664,34665,34666,34667,34668,34669,34670,34671,34672,34673,34674,34675,34676,34677,34678,34679,34680,34681,34682,34683,34684,34685,34686,34687,34688,34689,34690,34691,34692,34693,34694,34695,34696,34697,34698,34699,34700,34701,34702,34703,34704,34705,34706,34707,34708,34709,34710,34711,34712,34713,34714,34715,34716,34717,34718,34719,34720,34721,34722,34723,34724,34725,34726,34727,34728,34729,34730,34731,34732,34733,34734,34735,34736,34737,34738,34739,34740,34741,34742,34743,34744,34745,34746,34747,34748,34749,34750,34751,34752,34753,34754,34755,34756,34757,34758,34759,34760,34761,34762,34763,34764,34765,34766,34767,34768,34769,34770,34771,34772,34773,34774,34775,34776,34777,34778,34779,34780,34781,34782,34783,34784,34785,34786,34787,34788,34789,34790,34791,34792,34793,34794,34795,34796,34797,34798,34799,34800,34801,34802,34803,34804,34805,34806,34807,34808,34809,34810,34811,34812,34813,34814,34815,34816,34817,34818,34819,34820,34821,34822,34823,34824,34825,34826,34827,34828,34829,34830,34831,34832,34833,34834,34835,34836,34837,34838,34839,34840,34841,34842,34843,34844,34845,34846,34847,34848,34849,34850,34851,34852,34853,34854,34855,34856,34857,34858,34859,34860,34861,34862,34863,34864,34865,34866,34867,34868,34869,34870,34871,34872,34873,34874,34875,34876,34877,34878,34879,34880,34881,34882,34883,34884,34885,34886,34887,34888,34889,34890,34891,34892,34893,34894,34895,34896,34897,34898,34899,34900,34901,34902,34903,34904,34905,34906,34907,34908,34909,34910,34911,34912,34913,34914,34915,34916,34917,34918,34919,34920,34921,34922,34923,34924,34925,34926,34927,34928,34929,34930,34931,34932,34933,34934,34935,34936,34937,34938,34939,34940,34941,34942,34943,34944,34945,34946,34947,34948,34949,34950,34951,34952,34953,34954,34955,34956,34957,34958,34959,34960,34961,34962,34963,34964,34965,34966,34967,34968,34969,34970,34971,34972,34973,34974,34975,34976,34977,34978,34979,34980,34981,34982,34983,34984,34985,34986,34987,34988,34989,34990,34991,34992,34993,34994,34995,34996,34997,34998,34999,35000,35001,35002,35003,35004,35005,35006,35007,35008,35009,35010,35011,35012,35013,35014,35015,35016,35017,35018,35019,35020,35021,35022,35023,35024,35025,35026,35027,35028,35029,35030,35031,35032,35033,35034,35035,35036,35037,35038,35039,35040,35041,35042,35043,35044,35045,35046,35047,35048,35049,35050,35051,35052,35053,35054,35055,35056,35057,35058,35059,35060,35061,35062,35063,35064,35065,35066,35067,35068,35069,35070,35071,35072,35073,35074,35075,35076,35077,35078,35079,35080,35081,35082,35083,35084,35085,35086,35087,35088,35089,35090,35091,35092,35093,35094,35095,35096,35097,35098,35099,35100,35101,35102,35103,35104,35105,35106,35107,35108,35109,35110,35111,35112,35113,35114,35115,35116,35117,35118,35119,35120,35121,35122,35123,35124,35125,35126,35127,35128,35129,35130,35131,35132,35133,35134,35135,35136,35137,35138,35139,35140,35141,35142,35143,35144,35145,35146,35147,35148,35149,35150,35151,35152,35153,35154,35155,35156,35157,35158,35159,35160,35161,35162,35163,35164,35165,35166,35167,35168,35169,35170,35171,35172,35173,35174,35175,35176,35177,35178,35179,35180,35181,35182,35183,35184,35185,35186,35187,35188,35189,35190,35191,35192,35193,35194,35195,35196,35197,35198,35199,35200,35201,35202,35203,35204,35205,35206,35207,35208,35209,35210,35211,35212,35213,35214,35215,35216,35217,35218,35219,35220,35221,35222,35223,35224,35225,35226,35227,35228,35229,35230,35231,35232,35233,35234,35235,35236,35237,35238,35239,35240,35241,35242,35243,35244,35245,35246,35247,35248,35249,35250,35251,35252,35253,35254,35255,35256,35257,35258,35259,35260,35261,35262,35263,35264,35265,35266,35267,35268,35269,35270,35271,35272,35273,35274,35275,35276,35277,35278,35279,35280,35281,35282,35283,35284,35285,35286,35287,35288,35289,35290,35291,35292,35293,35294,35295,35296,35297,35298,35299,35300,35301,35302,35303,35304,35305,35306,35307,35308,35309,35310,35311,35312,35313,35314,35315,35316,35317,35318,35319,35320,35321,35322,35323,35324,35325,35326,35327,35328,35329,35330,35331,35332,35333,35334,35335,35336,35337,35338,35339,35340,35341,35342,35343,35344,35345,35346,35347,35348,35349,35350,35351,35352,35353,35354,35355,35356,35357,35358,35359,35360,35361,35362,35363,35364,35365,35366,35367,35368,35369,35370,35371,35372,35373,35374,35375,35376,35377,35378,35379,35380,35381,35382,35383,35384,35385,35386,35387,35388,35389,35390,35391,35392,35393,35394,35395,35396,35397,35398,35399,35400,35401,35402,35403,35404,35405,35406,35407,35408,35409,35410,35411,35412,35413,35414,35415,35416,35417,35418,35419,35420,35421,35422,35423,35424,35425,35426,35427,35428,35429,35430,35431,35432,35433,35434,35435,35436,35437,35438,35439,35440,35441,35442,35443,35444,35445,35446,35447,35448,35449,35450,35451,35452,35453,35454,35455,35456,35457,35458,35459,35460,35461,35462,35463,35464,35465,35466,35467,35468,35469,35470,35471,35472,35473,35474,35475,35476,35477,35478,35479,35480,35481,35482,35483,35484,35485,35486,35487,35488,35489,35490,35491,35492,35493,35494,35495,35496,35497,35498,35499,35500,35501,35502,35503,35504,35505,35506,35507,35508,35509,35510,35511,35512,35513,35514,35515,35516,35517,35518,35519,35520,35521,35522,35523,35524,35525,35526,35527,35528,35529,35530,35531,35532,35533,35534,35535,35536,35537,35538,35539,35540,35541,35542,35543,35544,35545,35546,35547,35548,35549,35550,35551,35552,35553,35554,35555,35556,35557,35558,35559,35560,35561,35562,35563,35564,35565,35566,35567,35568,35569,35570,35571,35572,35573,35574,35575,35576,35577,35578,35579,35580,35581,35582,35583,35584,35585,35586,35587,35588,35589,35590,35591,35592,35593,35594,35595,35596,35597,35598,35599,35600,35601,35602,35603,35604,35605,35606,35607,35608,35609,35610,35611,35612,35613,35614,35615,35616,35617,35618,35619,35620,35621,35622,35623,35624,35625,35626,35627,35628,35629,35630,35631,35632,35633,35634,35635,35636,35637,35638,35639,35640,35641,35642,35643,35644,35645,35646,35647,35648,35649,35650,35651,35652,35653,35654,35655,35656,35657,35658,35659,35660,35661,35662,35663,35664,35665,35666,35667,35668,35669,35670,35671,35672,35673,35674,35675,35676,35677,35678,35679,35680,35681,35682,35683,35684,35685,35686,35687,35688,35689,35690,35691,35692,35693,35694,35695,35696,35697,35698,35699,35700,35701,35702,35703,35704,35705,35706,35707,35708,35709,35710,35711,35712,35713,35714,35715,35716,35717,35718,35719,35720,35721,35722,35723,35724,35725,35726,35727,35728,35729,35730,35731,35732,35733,35734,35735,35736,35737,35738,35739,35740,35741,35742,35743,35744,35745,35746,35747,35748,35749,35750,35751,35752,35753,35754,35755,35756,35757,35758,35759,35760,35761,35762,35763,35764,35765,35766,35767,35768,35769,35770,35771,35772,35773,35774,35775,35776,35777,35778,35779,35780,35781,35782,35783,35784,35785,35786,35787,35788,35789,35790,35791,35792,35793,35794,35795,35796,35797,35798,35799,35800,35801,35802,35803,35804,35805,35806,35807,35808,35809,35810,35811,35812,35813,35814,35815,35816,35817,35818,35819,35820,35821,35822,35823,35824,35825,35826,35827,35828,35829,35830,35831,35832,35833,35834,35835,35836,35837,35838,35839,35840,35841,35842,35843,35844,35845,35846,35847,35848,35849,35850,35851,35852,35853,35854,35855,35856,35857,35858,35859,35860,35861,35862,35863,35864,35865,35866,35867,35868,35869,35870,35871,35872,35873,35874,35875,35876,35877,35878,35879,35880,35881,35882,35883,35884,35885,35886,35887,35888,35889,35890,35891,35892,35893,35894,35895,35896,35897,35898,35899,35900,35901,35902,35903,35904,35905,35906,35907,35908,35909,35910,35911,35912,35913,35914,35915,35916,35917,35918,35919,35920,35921,35922,35923,35924,35925,35926,35927,35928,35929,35930,35931,35932,35933,35934,35935,35936,35937,35938,35939,35940,35941,35942,35943,35944,35945,35946,35947,35948,35949,35950,35951,35952,35953,35954,35955,35956,35957,35958,35959,35960,35961,35962,35963,35964,35965,35966,35967,35968,35969,35970,35971,35972,35973,35974,35975,35976,35977,35978,35979,35980,35981,35982,35983,35984,35985,35986,35987,35988,35989,35990,35991,35992,35993,35994,35995,35996,35997,35998,35999,36000,36001,36002,36003,36004,36005,36006,36007,36008,36009,36010,36011,36012,36013,36014,36015,36016,36017,36018,36019,36020,36021,36022,36023,36024,36025,36026,36027,36028,36029,36030,36031,36032,36033,36034,36035,36036,36037,36038,36039,36040,36041,36042,36043,36044,36045,36046,36047,36048,36049,36050,36051,36052,36053,36054,36055,36056,36057,36058,36059,36060,36061,36062,36063,36064,36065,36066,36067,36068,36069,36070,36071,36072,36073,36074,36075,36076,36077,36078,36079,36080,36081,36082,36083,36084,36085,36086,36087,36088,36089,36090,36091,36092,36093,36094,36095,36096,36097,36098,36099,36100,36101,36102,36103,36104,36105,36106,36107,36108,36109,36110,36111,36112,36113,36114,36115,36116,36117,36118,36119,36120,36121,36122,36123,36124,36125,36126,36127,36128,36129,36130,36131,36132,36133,36134,36135,36136,36137,36138,36139,36140,36141,36142,36143,36144,36145,36146,36147,36148,36149,36150,36151,36152,36153,36154,36155,36156,36157,36158,36159,36160,36161,36162,36163,36164,36165,36166,36167,36168,36169,36170,36171,36172,36173,36174,36175,36176,36177,36178,36179,36180,36181,36182,36183,36184,36185,36186,36187,36188,36189,36190,36191,36192,36193,36194,36195,36196,36197,36198,36199,36200,36201,36202,36203,36204,36205,36206,36207,36208,36209,36210,36211,36212,36213,36214,36215,36216,36217,36218,36219,36220,36221,36222,36223,36224,36225,36226,36227,36228,36229,36230,36231,36232,36233,36234,36235,36236,36237,36238,36239,36240,36241,36242,36243,36244,36245,36246,36247,36248,36249,36250,36251,36252,36253,36254,36255,36256,36257,36258,36259,36260,36261,36262,36263,36264,36265,36266,36267,36268,36269,36270,36271,36272,36273,36274,36275,36276,36277,36278,36279,36280,36281,36282,36283,36284,36285,36286,36287,36288,36289,36290,36291,36292,36293,36294,36295,36296,36297,36298,36299,36300,36301,36302,36303,36304,36305,36306,36307,36308,36309,36310,36311,36312,36313,36314,36315,36316,36317,36318,36319,36320,36321,36322,36323,36324,36325,36326,36327,36328,36329,36330,36331,36332,36333,36334,36335,36336,36337,36338,36339,36340,36341,36342,36343,36344,36345,36346,36347,36348,36349,36350,36351,36352,36353,36354,36355,36356,36357,36358,36359,36360,36361,36362,36363,36364,36365,36366,36367,36368,36369,36370,36371,36372,36373,36374,36375,36376,36377,36378,36379,36380,36381,36382,36383,36384,36385,36386,36387,36388,36389,36390,36391,36392,36393,36394,36395,36396,36397,36398,36399,36400,36401,36402,36403,36404,36405,36406,36407,36408,36409,36410,36411,36412,36413,36414,36415,36416,36417,36418,36419,36420,36421,36422,36423,36424,36425,36426,36427,36428,36429,36430,36431,36432,36433,36434,36435,36436,36437,36438,36439,36440,36441,36442,36443,36444,36445,36446,36447,36448,36449,36450,36451,36452,36453,36454,36455,36456,36457,36458,36459,36460,36461,36462,36463,36464,36465,36466,36467,36468,36469,36470,36471,36472,36473,36474,36475,36476,36477,36478,36479,36480,36481,36482,36483,36484,36485,36486,36487,36488,36489,36490,36491,36492,36493,36494,36495,36496,36497,36498,36499,36500,36501,36502,36503,36504,36505,36506,36507,36508,36509,36510,36511,36512,36513,36514,36515,36516,36517,36518,36519,36520,36521,36522,36523,36524,36525,36526,36527,36528,36529,36530,36531,36532,36533,36534,36535,36536,36537,36538,36539,36540,36541,36542,36543,36544,36545,36546,36547,36548,36549,36550,36551,36552,36553,36554,36555,36556,36557,36558,36559,36560,36561,36562,36563,36564,36565,36566,36567,36568,36569,36570,36571,36572,36573,36574,36575,36576,36577,36578,36579,36580,36581,36582,36583,36584,36585,36586,36587,36588,36589,36590,36591,36592,36593,36594,36595,36596,36597,36598,36599,36600,36601,36602,36603,36604,36605,36606,36607,36608,36609,36610,36611,36612,36613,36614,36615,36616,36617,36618,36619,36620,36621,36622,36623,36624,36625,36626,36627,36628,36629,36630,36631,36632,36633,36634,36635,36636,36637,36638,36639,36640,36641,36642,36643,36644,36645,36646,36647,36648,36649,36650,36651,36652,36653,36654,36655,36656,36657,36658,36659,36660,36661,36662,36663,36664,36665,36666,36667,36668,36669,36670,36671,36672,36673,36674,36675,36676,36677,36678,36679,36680,36681,36682,36683,36684,36685,36686,36687,36688,36689,36690,36691,36692,36693,36694,36695,36696,36697,36698,36699,36700,36701,36702,36703,36704,36705,36706,36707,36708,36709,36710,36711,36712,36713,36714,36715,36716,36717,36718,36719,36720,36721,36722,36723,36724,36725,36726,36727,36728,36729,36730,36731,36732,36733,36734,36735,36736,36737,36738,36739,36740,36741,36742,36743,36744,36745,36746,36747,36748,36749,36750,36751,36752,36753,36754,36755,36756,36757,36758,36759,36760,36761,36762,36763,36764,36765,36766,36767,36768,36769,36770,36771,36772,36773,36774,36775,36776,36777,36778,36779,36780,36781,36782,36783,36784,36785,36786,36787,36788,36789,36790,36791,36792,36793,36794,36795,36796,36797,36798,36799,36800,36801,36802,36803,36804,36805,36806,36807,36808,36809,36810,36811,36812,36813,36814,36815,36816,36817,36818,36819,36820,36821,36822,36823,36824,36825,36826,36827,36828,36829,36830,36831,36832,36833,36834,36835,36836,36837,36838,36839,36840,36841,36842,36843,36844,36845,36846,36847,36848,36849,36850,36851,36852,36853,36854,36855,36856,36857,36858,36859,36860,36861,36862,36863,36864,36865,36866,36867,36868,36869,36870,36871,36872,36873,36874,36875,36876,36877,36878,36879,36880,36881,36882,36883,36884,36885,36886,36887,36888,36889,36890,36891,36892,36893,36894,36895,36896,36897,36898,36899,36900,36901,36902,36903,36904,36905,36906,36907,36908,36909,36910,36911,36912,36913,36914,36915,36916,36917,36918,36919,36920,36921,36922,36923,36924,36925,36926,36927,36928,36929,36930,36931,36932,36933,36934,36935,36936,36937,36938,36939,36940,36941,36942,36943,36944,36945,36946,36947,36948,36949,36950,36951,36952,36953,36954,36955,36956,36957,36958,36959,36960,36961,36962,36963,36964,36965,36966,36967,36968,36969,36970,36971,36972,36973,36974,36975,36976,36977,36978,36979,36980,36981,36982,36983,36984,36985,36986,36987,36988,36989,36990,36991,36992,36993,36994,36995,36996,36997,36998,36999,37000,37001,37002,37003,37004,37005,37006,37007,37008,37009,37010,37011,37012,37013,37014,37015,37016,37017,37018,37019,37020,37021,37022,37023,37024,37025,37026,37027,37028,37029,37030,37031,37032,37033,37034,37035,37036,37037,37038,37039,37040,37041,37042,37043,37044,37045,37046,37047,37048,37049,37050,37051,37052,37053,37054,37055,37056,37057,37058,37059,37060,37061,37062,37063,37064,37065,37066,37067,37068,37069,37070,37071,37072,37073,37074,37075,37076,37077,37078,37079,37080,37081,37082,37083,37084,37085,37086,37087,37088,37089,37090,37091,37092,37093,37094,37095,37096,37097,37098,37099,37100,37101,37102,37103,37104,37105,37106,37107,37108,37109,37110,37111,37112,37113,37114,37115,37116,37117,37118,37119,37120,37121,37122,37123,37124,37125,37126,37127,37128,37129,37130,37131,37132,37133,37134,37135,37136,37137,37138,37139,37140,37141,37142,37143,37144,37145,37146,37147,37148,37149,37150,37151,37152,37153,37154,37155,37156,37157,37158,37159,37160,37161,37162,37163,37164,37165,37166,37167,37168,37169,37170,37171,37172,37173,37174,37175,37176,37177,37178,37179,37180,37181,37182,37183,37184,37185,37186,37187,37188,37189,37190,37191,37192,37193,37194,37195,37196,37197,37198,37199,37200,37201,37202,37203,37204,37205,37206,37207,37208,37209,37210,37211,37212,37213,37214,37215,37216,37217,37218,37219,37220,37221,37222,37223,37224,37225,37226,37227,37228,37229,37230,37231,37232,37233,37234,37235,37236,37237,37238,37239,37240,37241,37242,37243,37244,37245,37246,37247,37248,37249,37250,37251,37252,37253,37254,37255,37256,37257,37258,37259,37260,37261,37262,37263,37264,37265,37266,37267,37268,37269,37270,37271,37272,37273,37274,37275,37276,37277,37278,37279,37280,37281,37282,37283,37284,37285,37286,37287,37288,37289,37290,37291,37292,37293,37294,37295,37296,37297,37298,37299,37300,37301,37302,37303,37304,37305,37306,37307,37308,37309,37310,37311,37312,37313,37314,37315,37316,37317,37318,37319,37320,37321,37322,37323,37324,37325,37326,37327,37328,37329,37330,37331,37332,37333,37334,37335,37336,37337,37338,37339,37340,37341,37342,37343,37344,37345,37346,37347,37348,37349,37350,37351,37352,37353,37354,37355,37356,37357,37358,37359,37360,37361,37362,37363,37364,37365,37366,37367,37368,37369,37370,37371,37372,37373,37374,37375,37376,37377,37378,37379,37380,37381,37382,37383,37384,37385,37386,37387,37388,37389,37390,37391,37392,37393,37394,37395,37396,37397,37398,37399,37400,37401,37402,37403,37404,37405,37406,37407,37408,37409,37410,37411,37412,37413,37414,37415,37416,37417,37418,37419,37420,37421,37422,37423,37424,37425,37426,37427,37428,37429,37430,37431,37432,37433,37434,37435,37436,37437,37438,37439,37440,37441,37442,37443,37444,37445,37446,37447,37448,37449,37450,37451,37452,37453,37454,37455,37456,37457,37458,37459,37460,37461,37462,37463,37464,37465,37466,37467,37468,37469,37470,37471,37472,37473,37474,37475,37476,37477,37478,37479,37480,37481,37482,37483,37484,37485,37486,37487,37488,37489,37490,37491,37492,37493,37494,37495,37496,37497,37498,37499,37500,37501,37502,37503,37504,37505,37506,37507,37508,37509,37510,37511,37512,37513,37514,37515,37516,37517,37518,37519,37520,37521,37522,37523,37524,37525,37526,37527,37528,37529,37530,37531,37532,37533,37534,37535,37536,37537,37538,37539,37540,37541,37542,37543,37544,37545,37546,37547,37548,37549,37550,37551,37552,37553,37554,37555,37556,37557,37558,37559,37560,37561,37562,37563,37564,37565,37566,37567,37568,37569,37570,37571,37572,37573,37574,37575,37576,37577,37578,37579,37580,37581,37582,37583,37584,37585,37586,37587,37588,37589,37590,37591,37592,37593,37594,37595,37596,37597,37598,37599,37600,37601,37602,37603,37604,37605,37606,37607,37608,37609,37610,37611,37612,37613,37614,37615,37616,37617,37618,37619,37620,37621,37622,37623,37624,37625,37626,37627,37628,37629,37630,37631,37632,37633,37634,37635,37636,37637,37638,37639,37640,37641,37642,37643,37644,37645,37646,37647,37648,37649,37650,37651,37652,37653,37654,37655,37656,37657,37658,37659,37660,37661,37662,37663,37664,37665,37666,37667,37668,37669,37670,37671,37672,37673,37674,37675,37676,37677,37678,37679,37680,37681,37682,37683,37684,37685,37686,37687,37688,37689,37690,37691,37692,37693,37694,37695,37696,37697,37698,37699,37700,37701,37702,37703,37704,37705,37706,37707,37708,37709,37710,37711,37712,37713,37714,37715,37716,37717,37718,37719,37720,37721,37722,37723,37724,37725,37726,37727,37728,37729,37730,37731,37732,37733,37734,37735,37736,37737,37738,37739,37740,37741,37742,37743,37744,37745,37746,37747,37748,37749,37750,37751,37752,37753,37754,37755,37756,37757,37758,37759,37760,37761,37762,37763,37764,37765,37766,37767,37768,37769,37770,37771,37772,37773,37774,37775,37776,37777,37778,37779,37780,37781,37782,37783,37784,37785,37786,37787,37788,37789,37790,37791,37792,37793,37794,37795,37796,37797,37798,37799,37800,37801,37802,37803,37804,37805,37806,37807,37808,37809,37810,37811,37812,37813,37814,37815,37816,37817,37818,37819,37820,37821,37822,37823,37824,37825,37826,37827,37828,37829,37830,37831,37832,37833,37834,37835,37836,37837,37838,37839,37840,37841,37842,37843,37844,37845,37846,37847,37848,37849,37850,37851,37852,37853,37854,37855,37856,37857,37858,37859,37860,37861,37862,37863,37864,37865,37866,37867,37868,37869,37870,37871,37872,37873,37874,37875,37876,37877,37878,37879,37880,37881,37882,37883,37884,37885,37886,37887,37888,37889,37890,37891,37892,37893,37894,37895,37896,37897,37898,37899,37900,37901,37902,37903,37904,37905,37906,37907,37908,37909,37910,37911,37912,37913,37914,37915,37916,37917,37918,37919,37920,37921,37922,37923,37924,37925,37926,37927,37928,37929,37930,37931,37932,37933,37934,37935,37936,37937,37938,37939,37940,37941,37942,37943,37944,37945,37946,37947,37948,37949,37950,37951,37952,37953,37954,37955,37956,37957,37958,37959,37960,37961,37962,37963,37964,37965,37966,37967,37968,37969,37970,37971,37972,37973,37974,37975,37976,37977,37978,37979,37980,37981,37982,37983,37984,37985,37986,37987,37988,37989,37990,37991,37992,37993,37994,37995,37996,37997,37998,37999,38000,38001,38002,38003,38004,38005,38006,38007,38008,38009,38010,38011,38012,38013,38014,38015,38016,38017,38018,38019,38020,38021,38022,38023,38024,38025,38026,38027,38028,38029,38030,38031,38032,38033,38034,38035,38036,38037,38038,38039,38040,38041,38042,38043,38044,38045,38046,38047,38048,38049,38050,38051,38052,38053,38054,38055,38056,38057,38058,38059,38060,38061,38062,38063,38064,38065,38066,38067,38068,38069,38070,38071,38072,38073,38074,38075,38076,38077,38078,38079,38080,38081,38082,38083,38084,38085,38086,38087,38088,38089,38090,38091,38092,38093,38094,38095,38096,38097,38098,38099,38100,38101,38102,38103,38104,38105,38106,38107,38108,38109,38110,38111,38112,38113,38114,38115,38116,38117,38118,38119,38120,38121,38122,38123,38124,38125,38126,38127,38128,38129,38130,38131,38132,38133,38134,38135,38136,38137,38138,38139,38140,38141,38142,38143,38144,38145,38146,38147,38148,38149,38150,38151,38152,38153,38154,38155,38156,38157,38158,38159,38160,38161,38162,38163,38164,38165,38166,38167,38168,38169,38170,38171,38172,38173,38174,38175,38176,38177,38178,38179,38180,38181,38182,38183,38184,38185,38186,38187,38188,38189,38190,38191,38192,38193,38194,38195,38196,38197,38198,38199,38200,38201,38202,38203,38204,38205,38206,38207,38208,38209,38210,38211,38212,38213,38214,38215,38216,38217,38218,38219,38220,38221,38222,38223,38224,38225,38226,38227,38228,38229,38230,38231,38232,38233,38234,38235,38236,38237,38238,38239,38240,38241,38242,38243,38244,38245,38246,38247,38248,38249,38250,38251,38252,38253,38254,38255,38256,38257,38258,38259,38260,38261,38262,38263,38264,38265,38266,38267,38268,38269,38270,38271,38272,38273,38274,38275,38276,38277,38278,38279,38280,38281,38282,38283,38284,38285,38286,38287,38288,38289,38290,38291,38292,38293,38294,38295,38296,38297,38298,38299,38300,38301,38302,38303,38304,38305,38306,38307,38308,38309,38310,38311,38312,38313,38314,38315,38316,38317,38318,38319,38320,38321,38322,38323,38324,38325,38326,38327,38328,38329,38330,38331,38332,38333,38334,38335,38336,38337,38338,38339,38340,38341,38342,38343,38344,38345,38346,38347,38348,38349,38350,38351,38352,38353,38354,38355,38356,38357,38358,38359,38360,38361,38362,38363,38364,38365,38366,38367,38368,38369,38370,38371,38372,38373,38374,38375,38376,38377,38378,38379,38380,38381,38382,38383,38384,38385,38386,38387,38388,38389,38390,38391,38392,38393,38394,38395,38396,38397,38398,38399,38400,38401,38402,38403,38404,38405,38406,38407,38408,38409,38410,38411,38412,38413,38414,38415,38416,38417,38418,38419,38420,38421,38422,38423,38424,38425,38426,38427,38428,38429,38430,38431,38432,38433,38434,38435,38436,38437,38438,38439,38440,38441,38442,38443,38444,38445,38446,38447,38448,38449,38450,38451,38452,38453,38454,38455,38456,38457,38458,38459,38460,38461,38462,38463,38464,38465,38466,38467,38468,38469,38470,38471,38472,38473,38474,38475,38476,38477,38478,38479,38480,38481,38482,38483,38484,38485,38486,38487,38488,38489,38490,38491,38492,38493,38494,38495,38496,38497,38498,38499,38500,38501,38502,38503,38504,38505,38506,38507,38508,38509,38510,38511,38512,38513,38514,38515,38516,38517,38518,38519,38520,38521,38522,38523,38524,38525,38526,38527,38528,38529,38530,38531,38532,38533,38534,38535,38536,38537,38538,38539,38540,38541,38542,38543,38544,38545,38546,38547,38548,38549,38550,38551,38552,38553,38554,38555,38556,38557,38558,38559,38560,38561,38562,38563,38564,38565,38566,38567,38568,38569,38570,38571,38572,38573,38574,38575,38576,38577,38578,38579,38580,38581,38582,38583,38584,38585,38586,38587,38588,38589,38590,38591,38592,38593,38594,38595,38596,38597,38598,38599,38600,38601,38602,38603,38604,38605,38606,38607,38608,38609,38610,38611,38612,38613,38614,38615,38616,38617,38618,38619,38620,38621,38622,38623,38624,38625,38626,38627,38628,38629,38630,38631,38632,38633,38634,38635,38636,38637,38638,38639,38640,38641,38642,38643,38644,38645,38646,38647,38648,38649,38650,38651,38652,38653,38654,38655,38656,38657,38658,38659,38660,38661,38662,38663,38664,38665,38666,38667,38668,38669,38670,38671,38672,38673,38674,38675,38676,38677,38678,38679,38680,38681,38682,38683,38684,38685,38686,38687,38688,38689,38690,38691,38692,38693,38694,38695,38696,38697,38698,38699,38700,38701,38702,38703,38704,38705,38706,38707,38708,38709,38710,38711,38712,38713,38714,38715,38716,38717,38718,38719,38720,38721,38722,38723,38724,38725,38726,38727,38728,38729,38730,38731,38732,38733,38734,38735,38736,38737,38738,38739,38740,38741,38742,38743,38744,38745,38746,38747,38748,38749,38750,38751,38752,38753,38754,38755,38756,38757,38758,38759,38760,38761,38762,38763,38764,38765,38766,38767,38768,38769,38770,38771,38772,38773,38774,38775,38776,38777,38778,38779,38780,38781,38782,38783,38784,38785,38786,38787,38788,38789,38790,38791,38792,38793,38794,38795,38796,38797,38798,38799,38800,38801,38802,38803,38804,38805,38806,38807,38808,38809,38810,38811,38812,38813,38814,38815,38816,38817,38818,38819,38820,38821,38822,38823,38824,38825,38826,38827,38828,38829,38830,38831,38832,38833,38834,38835,38836,38837,38838,38839,38840,38841,38842,38843,38844,38845,38846,38847,38848,38849,38850,38851,38852,38853,38854,38855,38856,38857,38858,38859,38860,38861,38862,38863,38864,38865,38866,38867,38868,38869,38870,38871,38872,38873,38874,38875,38876,38877,38878,38879,38880,38881,38882,38883,38884,38885,38886,38887,38888,38889,38890,38891,38892,38893,38894,38895,38896,38897,38898,38899,38900,38901,38902,38903,38904,38905,38906,38907,38908,38909,38910,38911,38912,38913,38914,38915,38916,38917,38918,38919,38920,38921,38922,38923,38924,38925,38926,38927,38928,38929,38930,38931,38932,38933,38934,38935,38936,38937,38938,38939,38940,38941,38942,38943,38944,38945,38946,38947,38948,38949,38950,38951,38952,38953,38954,38955,38956,38957,38958,38959,38960,38961,38962,38963,38964,38965,38966,38967,38968,38969,38970,38971,38972,38973,38974,38975,38976,38977,38978,38979,38980,38981,38982,38983,38984,38985,38986,38987,38988,38989,38990,38991,38992,38993,38994,38995,38996,38997,38998,38999,39000,39001,39002,39003,39004,39005,39006,39007,39008,39009,39010,39011,39012,39013,39014,39015,39016,39017,39018,39019,39020,39021,39022,39023,39024,39025,39026,39027,39028,39029,39030,39031,39032,39033,39034,39035,39036,39037,39038,39039,39040,39041,39042,39043,39044,39045,39046,39047,39048,39049,39050,39051,39052,39053,39054,39055,39056,39057,39058,39059,39060,39061,39062,39063,39064,39065,39066,39067,39068,39069,39070,39071,39072,39073,39074,39075,39076,39077,39078,39079,39080,39081,39082,39083,39084,39085,39086,39087,39088,39089,39090,39091,39092,39093,39094,39095,39096,39097,39098,39099,39100,39101,39102,39103,39104,39105,39106,39107,39108,39109,39110,39111,39112,39113,39114,39115,39116,39117,39118,39119,39120,39121,39122,39123,39124,39125,39126,39127,39128,39129,39130,39131,39132,39133,39134,39135,39136,39137,39138,39139,39140,39141,39142,39143,39144,39145,39146,39147,39148,39149,39150,39151,39152,39153,39154,39155,39156,39157,39158,39159,39160,39161,39162,39163,39164,39165,39166,39167,39168,39169,39170,39171,39172,39173,39174,39175,39176,39177,39178,39179,39180,39181,39182,39183,39184,39185,39186,39187,39188,39189,39190,39191,39192,39193,39194,39195,39196,39197,39198,39199,39200,39201,39202,39203,39204,39205,39206,39207,39208,39209,39210,39211,39212,39213,39214,39215,39216,39217,39218,39219,39220,39221,39222,39223,39224,39225,39226,39227,39228,39229,39230,39231,39232,39233,39234,39235,39236,39237,39238,39239,39240,39241,39242,39243,39244,39245,39246,39247,39248,39249,39250,39251,39252,39253,39254,39255,39256,39257,39258,39259,39260,39261,39262,39263,39264,39265,39266,39267,39268,39269,39270,39271,39272,39273,39274,39275,39276,39277,39278,39279,39280,39281,39282,39283,39284,39285,39286,39287,39288,39289,39290,39291,39292,39293,39294,39295,39296,39297,39298,39299,39300,39301,39302,39303,39304,39305,39306,39307,39308,39309,39310,39311,39312,39313,39314,39315,39316,39317,39318,39319,39320,39321,39322,39323,39324,39325,39326,39327,39328,39329,39330,39331,39332,39333,39334,39335,39336,39337,39338,39339,39340,39341,39342,39343,39344,39345,39346,39347,39348,39349,39350,39351,39352,39353,39354,39355,39356,39357,39358,39359,39360,39361,39362,39363,39364,39365,39366,39367,39368,39369,39370,39371,39372,39373,39374,39375,39376,39377,39378,39379,39380,39381,39382,39383,39384,39385,39386,39387,39388,39389,39390,39391,39392,39393,39394,39395,39396,39397,39398,39399,39400,39401,39402,39403,39404,39405,39406,39407,39408,39409,39410,39411,39412,39413,39414,39415,39416,39417,39418,39419,39420,39421,39422,39423,39424,39425,39426,39427,39428,39429,39430,39431,39432,39433,39434,39435,39436,39437,39438,39439,39440,39441,39442,39443,39444,39445,39446,39447,39448,39449,39450,39451,39452,39453,39454,39455,39456,39457,39458,39459,39460,39461,39462,39463,39464,39465,39466,39467,39468,39469,39470,39471,39472,39473,39474,39475,39476,39477,39478,39479,39480,39481,39482,39483,39484,39485,39486,39487,39488,39489,39490,39491,39492,39493,39494,39495,39496,39497,39498,39499,39500,39501,39502,39503,39504,39505,39506,39507,39508,39509,39510,39511,39512,39513,39514,39515,39516,39517,39518,39519,39520,39521,39522,39523,39524,39525,39526,39527,39528,39529,39530,39531,39532,39533,39534,39535,39536,39537,39538,39539,39540,39541,39542,39543,39544,39545,39546,39547,39548,39549,39550,39551,39552,39553,39554,39555,39556,39557,39558,39559,39560,39561,39562,39563,39564,39565,39566,39567,39568,39569,39570,39571,39572,39573,39574,39575,39576,39577,39578,39579,39580,39581,39582,39583,39584,39585,39586,39587,39588,39589,39590,39591,39592,39593,39594,39595,39596,39597,39598,39599,39600,39601,39602,39603,39604,39605,39606,39607,39608,39609,39610,39611,39612,39613,39614,39615,39616,39617,39618,39619,39620,39621,39622,39623,39624,39625,39626,39627,39628,39629,39630,39631,39632,39633,39634,39635,39636,39637,39638,39639,39640,39641,39642,39643,39644,39645,39646,39647,39648,39649,39650,39651,39652,39653,39654,39655,39656,39657,39658,39659,39660,39661,39662,39663,39664,39665,39666,39667,39668,39669,39670,39671,39672,39673,39674,39675,39676,39677,39678,39679,39680,39681,39682,39683,39684,39685,39686,39687,39688,39689,39690,39691,39692,39693,39694,39695,39696,39697,39698,39699,39700,39701,39702,39703,39704,39705,39706,39707,39708,39709,39710,39711,39712,39713,39714,39715,39716,39717,39718,39719,39720,39721,39722,39723,39724,39725,39726,39727,39728,39729,39730,39731,39732,39733,39734,39735,39736,39737,39738,39739,39740,39741,39742,39743,39744,39745,39746,39747,39748,39749,39750,39751,39752,39753,39754,39755,39756,39757,39758,39759,39760,39761,39762,39763,39764,39765,39766,39767,39768,39769,39770,39771,39772,39773,39774,39775,39776,39777,39778,39779,39780,39781,39782,39783,39784,39785,39786,39787,39788,39789,39790,39791,39792,39793,39794,39795,39796,39797,39798,39799,39800,39801,39802,39803,39804,39805,39806,39807,39808,39809,39810,39811,39812,39813,39814,39815,39816,39817,39818,39819,39820,39821,39822,39823,39824,39825,39826,39827,39828,39829,39830,39831,39832,39833,39834,39835,39836,39837,39838,39839,39840,39841,39842,39843,39844,39845,39846,39847,39848,39849,39850,39851,39852,39853,39854,39855,39856,39857,39858,39859,39860,39861,39862,39863,39864,39865,39866,39867,39868,39869,39870,39871,39872,39873,39874,39875,39876,39877,39878,39879,39880,39881,39882,39883,39884,39885,39886,39887,39888,39889,39890,39891,39892,39893,39894,39895,39896,39897,39898,39899,39900,39901,39902,39903,39904,39905,39906,39907,39908,39909,39910,39911,39912,39913,39914,39915,39916,39917,39918,39919,39920,39921,39922,39923,39924,39925,39926,39927,39928,39929,39930,39931,39932,39933,39934,39935,39936,39937,39938,39939,39940,39941,39942,39943,39944,39945,39946,39947,39948,39949,39950,39951,39952,39953,39954,39955,39956,39957,39958,39959,39960,39961,39962,39963,39964,39965,39966,39967,39968,39969,39970,39971,39972,39973,39974,39975,39976,39977,39978,39979,39980,39981,39982,39983,39984,39985,39986,39987,39988,39989,39990,39991,39992,39993,39994,39995,39996,39997,39998,39999,40000,40001,40002,40003,40004,40005,40006,40007,40008,40009,40010,40011,40012,40013,40014,40015,40016,40017,40018,40019,40020,40021,40022,40023,40024,40025,40026,40027,40028,40029,40030,40031,40032,40033,40034,40035,40036,40037,40038,40039,40040,40041,40042,40043,40044,40045,40046,40047,40048,40049,40050,40051,40052,40053,40054,40055,40056,40057,40058,40059,40060,40061,40062,40063,40064,40065,40066,40067,40068,40069,40070,40071,40072,40073,40074,40075,40076,40077,40078,40079,40080,40081,40082,40083,40084,40085,40086,40087,40088,40089,40090,40091,40092,40093,40094,40095,40096,40097,40098,40099,40100,40101,40102,40103,40104,40105,40106,40107,40108,40109,40110,40111,40112,40113,40114,40115,40116,40117,40118,40119,40120,40121,40122,40123,40124,40125,40126,40127,40128,40129,40130,40131,40132,40133,40134,40135,40136,40137,40138,40139,40140,40141,40142,40143,40144,40145,40146,40147,40148,40149,40150,40151,40152,40153,40154,40155,40156,40157,40158,40159,40160,40161,40162,40163,40164,40165,40166,40167,40168,40169,40170,40171,40172,40173,40174,40175,40176,40177,40178,40179,40180,40181,40182,40183,40184,40185,40186,40187,40188,40189,40190,40191,40192,40193,40194,40195,40196,40197,40198,40199,40200,40201,40202,40203,40204,40205,40206,40207,40208,40209,40210,40211,40212,40213,40214,40215,40216,40217,40218,40219,40220,40221,40222,40223,40224,40225,40226,40227,40228,40229,40230,40231,40232,40233,40234,40235,40236,40237,40238,40239,40240,40241,40242,40243,40244,40245,40246,40247,40248,40249,40250,40251,40252,40253,40254,40255,40256,40257,40258,40259,40260,40261,40262,40263,40264,40265,40266,40267,40268,40269,40270,40271,40272,40273,40274,40275,40276,40277,40278,40279,40280,40281,40282,40283,40284,40285,40286,40287,40288,40289,40290,40291,40292,40293,40294,40295,40296,40297,40298,40299,40300,40301,40302,40303,40304,40305,40306,40307,40308,40309,40310,40311,40312,40313,40314,40315,40316,40317,40318,40319,40320,40321,40322,40323,40324,40325,40326,40327,40328,40329,40330,40331,40332,40333,40334,40335,40336,40337,40338,40339,40340,40341,40342,40343,40344,40345,40346,40347,40348,40349,40350,40351,40352,40353,40354,40355,40356,40357,40358,40359,40360,40361,40362,40363,40364,40365,40366,40367,40368,40369,40370,40371,40372,40373,40374,40375,40376,40377,40378,40379,40380,40381,40382,40383,40384,40385,40386,40387,40388,40389,40390,40391,40392,40393,40394,40395,40396,40397,40398,40399,40400,40401,40402,40403,40404,40405,40406,40407,40408,40409,40410,40411,40412,40413,40414,40415,40416,40417,40418,40419,40420,40421,40422,40423,40424,40425,40426,40427,40428,40429,40430,40431,40432,40433,40434,40435,40436,40437,40438,40439,40440,40441,40442,40443,40444,40445,40446,40447,40448,40449,40450,40451,40452,40453,40454,40455,40456,40457,40458,40459,40460,40461,40462,40463,40464,40465,40466,40467,40468,40469,40470,40471,40472,40473,40474,40475,40476,40477,40478,40479,40480,40481,40482,40483,40484,40485,40486,40487,40488,40489,40490,40491,40492,40493,40494,40495,40496,40497,40498,40499,40500,40501,40502,40503,40504,40505,40506,40507,40508,40509,40510,40511,40512,40513,40514,40515,40516,40517,40518,40519,40520,40521,40522,40523,40524,40525,40526,40527,40528,40529,40530,40531,40532,40533,40534,40535,40536,40537,40538,40539,40540,40541,40542,40543,40544,40545,40546,40547,40548,40549,40550,40551,40552,40553,40554,40555,40556,40557,40558,40559,40560,40561,40562,40563,40564,40565,40566,40567,40568,40569,40570,40571,40572,40573,40574,40575,40576,40577,40578,40579,40580,40581,40582,40583,40584,40585,40586,40587,40588,40589,40590,40591,40592,40593,40594,40595,40596,40597,40598,40599,40600,40601,40602,40603,40604,40605,40606,40607,40608,40609,40610,40611,40612,40613,40614,40615,40616,40617,40618,40619,40620,40621,40622,40623,40624,40625,40626,40627,40628,40629,40630,40631,40632,40633,40634,40635,40636,40637,40638,40639,40640,40641,40642,40643,40644,40645,40646,40647,40648,40649,40650,40651,40652,40653,40654,40655,40656,40657,40658,40659,40660,40661,40662,40663,40664,40665,40666,40667,40668,40669,40670,40671,40672,40673,40674,40675,40676,40677,40678,40679,40680,40681,40682,40683,40684,40685,40686,40687,40688,40689,40690,40691,40692,40693,40694,40695,40696,40697,40698,40699,40700,40701,40702,40703,40704,40705,40706,40707,40708,40709,40710,40711,40712,40713,40714,40715,40716,40717,40718,40719,40720,40721,40722,40723,40724,40725,40726,40727,40728,40729,40730,40731,40732,40733,40734,40735,40736,40737,40738,40739,40740,40741,40742,40743,40744,40745,40746,40747,40748,40749,40750,40751,40752,40753,40754,40755,40756,40757,40758,40759,40760,40761,40762,40763,40764,40765,40766,40767,40768,40769,40770,40771,40772,40773,40774,40775,40776,40777,40778,40779,40780,40781,40782,40783,40784,40785,40786,40787,40788,40789,40790,40791,40792,40793,40794,40795,40796,40797,40798,40799,40800,40801,40802,40803,40804,40805,40806,40807,40808,40809,40810,40811,40812,40813,40814,40815,40816,40817,40818,40819,40820,40821,40822,40823,40824,40825,40826,40827,40828,40829,40830,40831,40832,40833,40834,40835,40836,40837,40838,40839,40840,40841,40842,40843,40844,40845,40846,40847,40848,40849,40850,40851,40852,40853,40854,40855,40856,40857,40858,40859,40860,40861,40862,40863,40864,40865,40866,40867,40868,40869,40870,40871,40872,40873,40874,40875,40876,40877,40878,40879,40880,40881,40882,40883,40884,40885,40886,40887,40888,40889,40890,40891,40892,40893,40894,40895,40896,40897,40898,40899,40900,40901,40902,40903,40904,40905,40906,40907,40908,40960,40961,40962,40963,40964,40965,40966,40967,40968,40969,40970,40971,40972,40973,40974,40975,40976,40977,40978,40979,40980,40981,40982,40983,40984,40985,40986,40987,40988,40989,40990,40991,40992,40993,40994,40995,40996,40997,40998,40999,41000,41001,41002,41003,41004,41005,41006,41007,41008,41009,41010,41011,41012,41013,41014,41015,41016,41017,41018,41019,41020,41021,41022,41023,41024,41025,41026,41027,41028,41029,41030,41031,41032,41033,41034,41035,41036,41037,41038,41039,41040,41041,41042,41043,41044,41045,41046,41047,41048,41049,41050,41051,41052,41053,41054,41055,41056,41057,41058,41059,41060,41061,41062,41063,41064,41065,41066,41067,41068,41069,41070,41071,41072,41073,41074,41075,41076,41077,41078,41079,41080,41081,41082,41083,41084,41085,41086,41087,41088,41089,41090,41091,41092,41093,41094,41095,41096,41097,41098,41099,41100,41101,41102,41103,41104,41105,41106,41107,41108,41109,41110,41111,41112,41113,41114,41115,41116,41117,41118,41119,41120,41121,41122,41123,41124,41125,41126,41127,41128,41129,41130,41131,41132,41133,41134,41135,41136,41137,41138,41139,41140,41141,41142,41143,41144,41145,41146,41147,41148,41149,41150,41151,41152,41153,41154,41155,41156,41157,41158,41159,41160,41161,41162,41163,41164,41165,41166,41167,41168,41169,41170,41171,41172,41173,41174,41175,41176,41177,41178,41179,41180,41181,41182,41183,41184,41185,41186,41187,41188,41189,41190,41191,41192,41193,41194,41195,41196,41197,41198,41199,41200,41201,41202,41203,41204,41205,41206,41207,41208,41209,41210,41211,41212,41213,41214,41215,41216,41217,41218,41219,41220,41221,41222,41223,41224,41225,41226,41227,41228,41229,41230,41231,41232,41233,41234,41235,41236,41237,41238,41239,41240,41241,41242,41243,41244,41245,41246,41247,41248,41249,41250,41251,41252,41253,41254,41255,41256,41257,41258,41259,41260,41261,41262,41263,41264,41265,41266,41267,41268,41269,41270,41271,41272,41273,41274,41275,41276,41277,41278,41279,41280,41281,41282,41283,41284,41285,41286,41287,41288,41289,41290,41291,41292,41293,41294,41295,41296,41297,41298,41299,41300,41301,41302,41303,41304,41305,41306,41307,41308,41309,41310,41311,41312,41313,41314,41315,41316,41317,41318,41319,41320,41321,41322,41323,41324,41325,41326,41327,41328,41329,41330,41331,41332,41333,41334,41335,41336,41337,41338,41339,41340,41341,41342,41343,41344,41345,41346,41347,41348,41349,41350,41351,41352,41353,41354,41355,41356,41357,41358,41359,41360,41361,41362,41363,41364,41365,41366,41367,41368,41369,41370,41371,41372,41373,41374,41375,41376,41377,41378,41379,41380,41381,41382,41383,41384,41385,41386,41387,41388,41389,41390,41391,41392,41393,41394,41395,41396,41397,41398,41399,41400,41401,41402,41403,41404,41405,41406,41407,41408,41409,41410,41411,41412,41413,41414,41415,41416,41417,41418,41419,41420,41421,41422,41423,41424,41425,41426,41427,41428,41429,41430,41431,41432,41433,41434,41435,41436,41437,41438,41439,41440,41441,41442,41443,41444,41445,41446,41447,41448,41449,41450,41451,41452,41453,41454,41455,41456,41457,41458,41459,41460,41461,41462,41463,41464,41465,41466,41467,41468,41469,41470,41471,41472,41473,41474,41475,41476,41477,41478,41479,41480,41481,41482,41483,41484,41485,41486,41487,41488,41489,41490,41491,41492,41493,41494,41495,41496,41497,41498,41499,41500,41501,41502,41503,41504,41505,41506,41507,41508,41509,41510,41511,41512,41513,41514,41515,41516,41517,41518,41519,41520,41521,41522,41523,41524,41525,41526,41527,41528,41529,41530,41531,41532,41533,41534,41535,41536,41537,41538,41539,41540,41541,41542,41543,41544,41545,41546,41547,41548,41549,41550,41551,41552,41553,41554,41555,41556,41557,41558,41559,41560,41561,41562,41563,41564,41565,41566,41567,41568,41569,41570,41571,41572,41573,41574,41575,41576,41577,41578,41579,41580,41581,41582,41583,41584,41585,41586,41587,41588,41589,41590,41591,41592,41593,41594,41595,41596,41597,41598,41599,41600,41601,41602,41603,41604,41605,41606,41607,41608,41609,41610,41611,41612,41613,41614,41615,41616,41617,41618,41619,41620,41621,41622,41623,41624,41625,41626,41627,41628,41629,41630,41631,41632,41633,41634,41635,41636,41637,41638,41639,41640,41641,41642,41643,41644,41645,41646,41647,41648,41649,41650,41651,41652,41653,41654,41655,41656,41657,41658,41659,41660,41661,41662,41663,41664,41665,41666,41667,41668,41669,41670,41671,41672,41673,41674,41675,41676,41677,41678,41679,41680,41681,41682,41683,41684,41685,41686,41687,41688,41689,41690,41691,41692,41693,41694,41695,41696,41697,41698,41699,41700,41701,41702,41703,41704,41705,41706,41707,41708,41709,41710,41711,41712,41713,41714,41715,41716,41717,41718,41719,41720,41721,41722,41723,41724,41725,41726,41727,41728,41729,41730,41731,41732,41733,41734,41735,41736,41737,41738,41739,41740,41741,41742,41743,41744,41745,41746,41747,41748,41749,41750,41751,41752,41753,41754,41755,41756,41757,41758,41759,41760,41761,41762,41763,41764,41765,41766,41767,41768,41769,41770,41771,41772,41773,41774,41775,41776,41777,41778,41779,41780,41781,41782,41783,41784,41785,41786,41787,41788,41789,41790,41791,41792,41793,41794,41795,41796,41797,41798,41799,41800,41801,41802,41803,41804,41805,41806,41807,41808,41809,41810,41811,41812,41813,41814,41815,41816,41817,41818,41819,41820,41821,41822,41823,41824,41825,41826,41827,41828,41829,41830,41831,41832,41833,41834,41835,41836,41837,41838,41839,41840,41841,41842,41843,41844,41845,41846,41847,41848,41849,41850,41851,41852,41853,41854,41855,41856,41857,41858,41859,41860,41861,41862,41863,41864,41865,41866,41867,41868,41869,41870,41871,41872,41873,41874,41875,41876,41877,41878,41879,41880,41881,41882,41883,41884,41885,41886,41887,41888,41889,41890,41891,41892,41893,41894,41895,41896,41897,41898,41899,41900,41901,41902,41903,41904,41905,41906,41907,41908,41909,41910,41911,41912,41913,41914,41915,41916,41917,41918,41919,41920,41921,41922,41923,41924,41925,41926,41927,41928,41929,41930,41931,41932,41933,41934,41935,41936,41937,41938,41939,41940,41941,41942,41943,41944,41945,41946,41947,41948,41949,41950,41951,41952,41953,41954,41955,41956,41957,41958,41959,41960,41961,41962,41963,41964,41965,41966,41967,41968,41969,41970,41971,41972,41973,41974,41975,41976,41977,41978,41979,41980,41981,41982,41983,41984,41985,41986,41987,41988,41989,41990,41991,41992,41993,41994,41995,41996,41997,41998,41999,42000,42001,42002,42003,42004,42005,42006,42007,42008,42009,42010,42011,42012,42013,42014,42015,42016,42017,42018,42019,42020,42021,42022,42023,42024,42025,42026,42027,42028,42029,42030,42031,42032,42033,42034,42035,42036,42037,42038,42039,42040,42041,42042,42043,42044,42045,42046,42047,42048,42049,42050,42051,42052,42053,42054,42055,42056,42057,42058,42059,42060,42061,42062,42063,42064,42065,42066,42067,42068,42069,42070,42071,42072,42073,42074,42075,42076,42077,42078,42079,42080,42081,42082,42083,42084,42085,42086,42087,42088,42089,42090,42091,42092,42093,42094,42095,42096,42097,42098,42099,42100,42101,42102,42103,42104,42105,42106,42107,42108,42109,42110,42111,42112,42113,42114,42115,42116,42117,42118,42119,42120,42121,42122,42123,42124,42192,42193,42194,42195,42196,42197,42198,42199,42200,42201,42202,42203,42204,42205,42206,42207,42208,42209,42210,42211,42212,42213,42214,42215,42216,42217,42218,42219,42220,42221,42222,42223,42224,42225,42226,42227,42228,42229,42230,42231,42232,42233,42234,42235,42236,42237,42240,42241,42242,42243,42244,42245,42246,42247,42248,42249,42250,42251,42252,42253,42254,42255,42256,42257,42258,42259,42260,42261,42262,42263,42264,42265,42266,42267,42268,42269,42270,42271,42272,42273,42274,42275,42276,42277,42278,42279,42280,42281,42282,42283,42284,42285,42286,42287,42288,42289,42290,42291,42292,42293,42294,42295,42296,42297,42298,42299,42300,42301,42302,42303,42304,42305,42306,42307,42308,42309,42310,42311,42312,42313,42314,42315,42316,42317,42318,42319,42320,42321,42322,42323,42324,42325,42326,42327,42328,42329,42330,42331,42332,42333,42334,42335,42336,42337,42338,42339,42340,42341,42342,42343,42344,42345,42346,42347,42348,42349,42350,42351,42352,42353,42354,42355,42356,42357,42358,42359,42360,42361,42362,42363,42364,42365,42366,42367,42368,42369,42370,42371,42372,42373,42374,42375,42376,42377,42378,42379,42380,42381,42382,42383,42384,42385,42386,42387,42388,42389,42390,42391,42392,42393,42394,42395,42396,42397,42398,42399,42400,42401,42402,42403,42404,42405,42406,42407,42408,42409,42410,42411,42412,42413,42414,42415,42416,42417,42418,42419,42420,42421,42422,42423,42424,42425,42426,42427,42428,42429,42430,42431,42432,42433,42434,42435,42436,42437,42438,42439,42440,42441,42442,42443,42444,42445,42446,42447,42448,42449,42450,42451,42452,42453,42454,42455,42456,42457,42458,42459,42460,42461,42462,42463,42464,42465,42466,42467,42468,42469,42470,42471,42472,42473,42474,42475,42476,42477,42478,42479,42480,42481,42482,42483,42484,42485,42486,42487,42488,42489,42490,42491,42492,42493,42494,42495,42496,42497,42498,42499,42500,42501,42502,42503,42504,42505,42506,42507,42508,42512,42513,42514,42515,42516,42517,42518,42519,42520,42521,42522,42523,42524,42525,42526,42527,42538,42539,42560,42561,42562,42563,42564,42565,42566,42567,42568,42569,42570,42571,42572,42573,42574,42575,42576,42577,42578,42579,42580,42581,42582,42583,42584,42585,42586,42587,42588,42589,42590,42591,42592,42593,42594,42595,42596,42597,42598,42599,42600,42601,42602,42603,42604,42605,42606,42623,42624,42625,42626,42627,42628,42629,42630,42631,42632,42633,42634,42635,42636,42637,42638,42639,42640,42641,42642,42643,42644,42645,42646,42647,42656,42657,42658,42659,42660,42661,42662,42663,42664,42665,42666,42667,42668,42669,42670,42671,42672,42673,42674,42675,42676,42677,42678,42679,42680,42681,42682,42683,42684,42685,42686,42687,42688,42689,42690,42691,42692,42693,42694,42695,42696,42697,42698,42699,42700,42701,42702,42703,42704,42705,42706,42707,42708,42709,42710,42711,42712,42713,42714,42715,42716,42717,42718,42719,42720,42721,42722,42723,42724,42725,42726,42727,42728,42729,42730,42731,42732,42733,42734,42735,42775,42776,42777,42778,42779,42780,42781,42782,42783,42786,42787,42788,42789,42790,42791,42792,42793,42794,42795,42796,42797,42798,42799,42800,42801,42802,42803,42804,42805,42806,42807,42808,42809,42810,42811,42812,42813,42814,42815,42816,42817,42818,42819,42820,42821,42822,42823,42824,42825,42826,42827,42828,42829,42830,42831,42832,42833,42834,42835,42836,42837,42838,42839,42840,42841,42842,42843,42844,42845,42846,42847,42848,42849,42850,42851,42852,42853,42854,42855,42856,42857,42858,42859,42860,42861,42862,42863,42864,42865,42866,42867,42868,42869,42870,42871,42872,42873,42874,42875,42876,42877,42878,42879,42880,42881,42882,42883,42884,42885,42886,42887,42888,42891,42892,42893,42894,42896,42897,42898,42899,42912,42913,42914,42915,42916,42917,42918,42919,42920,42921,42922,43000,43001,43002,43003,43004,43005,43006,43007,43008,43009,43011,43012,43013,43015,43016,43017,43018,43020,43021,43022,43023,43024,43025,43026,43027,43028,43029,43030,43031,43032,43033,43034,43035,43036,43037,43038,43039,43040,43041,43042,43072,43073,43074,43075,43076,43077,43078,43079,43080,43081,43082,43083,43084,43085,43086,43087,43088,43089,43090,43091,43092,43093,43094,43095,43096,43097,43098,43099,43100,43101,43102,43103,43104,43105,43106,43107,43108,43109,43110,43111,43112,43113,43114,43115,43116,43117,43118,43119,43120,43121,43122,43123,43138,43139,43140,43141,43142,43143,43144,43145,43146,43147,43148,43149,43150,43151,43152,43153,43154,43155,43156,43157,43158,43159,43160,43161,43162,43163,43164,43165,43166,43167,43168,43169,43170,43171,43172,43173,43174,43175,43176,43177,43178,43179,43180,43181,43182,43183,43184,43185,43186,43187,43250,43251,43252,43253,43254,43255,43259,43274,43275,43276,43277,43278,43279,43280,43281,43282,43283,43284,43285,43286,43287,43288,43289,43290,43291,43292,43293,43294,43295,43296,43297,43298,43299,43300,43301,43312,43313,43314,43315,43316,43317,43318,43319,43320,43321,43322,43323,43324,43325,43326,43327,43328,43329,43330,43331,43332,43333,43334,43360,43361,43362,43363,43364,43365,43366,43367,43368,43369,43370,43371,43372,43373,43374,43375,43376,43377,43378,43379,43380,43381,43382,43383,43384,43385,43386,43387,43388,43396,43397,43398,43399,43400,43401,43402,43403,43404,43405,43406,43407,43408,43409,43410,43411,43412,43413,43414,43415,43416,43417,43418,43419,43420,43421,43422,43423,43424,43425,43426,43427,43428,43429,43430,43431,43432,43433,43434,43435,43436,43437,43438,43439,43440,43441,43442,43471,43520,43521,43522,43523,43524,43525,43526,43527,43528,43529,43530,43531,43532,43533,43534,43535,43536,43537,43538,43539,43540,43541,43542,43543,43544,43545,43546,43547,43548,43549,43550,43551,43552,43553,43554,43555,43556,43557,43558,43559,43560,43584,43585,43586,43588,43589,43590,43591,43592,43593,43594,43595,43616,43617,43618,43619,43620,43621,43622,43623,43624,43625,43626,43627,43628,43629,43630,43631,43632,43633,43634,43635,43636,43637,43638,43642,43648,43649,43650,43651,43652,43653,43654,43655,43656,43657,43658,43659,43660,43661,43662,43663,43664,43665,43666,43667,43668,43669,43670,43671,43672,43673,43674,43675,43676,43677,43678,43679,43680,43681,43682,43683,43684,43685,43686,43687,43688,43689,43690,43691,43692,43693,43694,43695,43697,43701,43702,43705,43706,43707,43708,43709,43712,43714,43739,43740,43741,43744,43745,43746,43747,43748,43749,43750,43751,43752,43753,43754,43762,43763,43764,43777,43778,43779,43780,43781,43782,43785,43786,43787,43788,43789,43790,43793,43794,43795,43796,43797,43798,43808,43809,43810,43811,43812,43813,43814,43816,43817,43818,43819,43820,43821,43822,43968,43969,43970,43971,43972,43973,43974,43975,43976,43977,43978,43979,43980,43981,43982,43983,43984,43985,43986,43987,43988,43989,43990,43991,43992,43993,43994,43995,43996,43997,43998,43999,44000,44001,44002,44032,44033,44034,44035,44036,44037,44038,44039,44040,44041,44042,44043,44044,44045,44046,44047,44048,44049,44050,44051,44052,44053,44054,44055,44056,44057,44058,44059,44060,44061,44062,44063,44064,44065,44066,44067,44068,44069,44070,44071,44072,44073,44074,44075,44076,44077,44078,44079,44080,44081,44082,44083,44084,44085,44086,44087,44088,44089,44090,44091,44092,44093,44094,44095,44096,44097,44098,44099,44100,44101,44102,44103,44104,44105,44106,44107,44108,44109,44110,44111,44112,44113,44114,44115,44116,44117,44118,44119,44120,44121,44122,44123,44124,44125,44126,44127,44128,44129,44130,44131,44132,44133,44134,44135,44136,44137,44138,44139,44140,44141,44142,44143,44144,44145,44146,44147,44148,44149,44150,44151,44152,44153,44154,44155,44156,44157,44158,44159,44160,44161,44162,44163,44164,44165,44166,44167,44168,44169,44170,44171,44172,44173,44174,44175,44176,44177,44178,44179,44180,44181,44182,44183,44184,44185,44186,44187,44188,44189,44190,44191,44192,44193,44194,44195,44196,44197,44198,44199,44200,44201,44202,44203,44204,44205,44206,44207,44208,44209,44210,44211,44212,44213,44214,44215,44216,44217,44218,44219,44220,44221,44222,44223,44224,44225,44226,44227,44228,44229,44230,44231,44232,44233,44234,44235,44236,44237,44238,44239,44240,44241,44242,44243,44244,44245,44246,44247,44248,44249,44250,44251,44252,44253,44254,44255,44256,44257,44258,44259,44260,44261,44262,44263,44264,44265,44266,44267,44268,44269,44270,44271,44272,44273,44274,44275,44276,44277,44278,44279,44280,44281,44282,44283,44284,44285,44286,44287,44288,44289,44290,44291,44292,44293,44294,44295,44296,44297,44298,44299,44300,44301,44302,44303,44304,44305,44306,44307,44308,44309,44310,44311,44312,44313,44314,44315,44316,44317,44318,44319,44320,44321,44322,44323,44324,44325,44326,44327,44328,44329,44330,44331,44332,44333,44334,44335,44336,44337,44338,44339,44340,44341,44342,44343,44344,44345,44346,44347,44348,44349,44350,44351,44352,44353,44354,44355,44356,44357,44358,44359,44360,44361,44362,44363,44364,44365,44366,44367,44368,44369,44370,44371,44372,44373,44374,44375,44376,44377,44378,44379,44380,44381,44382,44383,44384,44385,44386,44387,44388,44389,44390,44391,44392,44393,44394,44395,44396,44397,44398,44399,44400,44401,44402,44403,44404,44405,44406,44407,44408,44409,44410,44411,44412,44413,44414,44415,44416,44417,44418,44419,44420,44421,44422,44423,44424,44425,44426,44427,44428,44429,44430,44431,44432,44433,44434,44435,44436,44437,44438,44439,44440,44441,44442,44443,44444,44445,44446,44447,44448,44449,44450,44451,44452,44453,44454,44455,44456,44457,44458,44459,44460,44461,44462,44463,44464,44465,44466,44467,44468,44469,44470,44471,44472,44473,44474,44475,44476,44477,44478,44479,44480,44481,44482,44483,44484,44485,44486,44487,44488,44489,44490,44491,44492,44493,44494,44495,44496,44497,44498,44499,44500,44501,44502,44503,44504,44505,44506,44507,44508,44509,44510,44511,44512,44513,44514,44515,44516,44517,44518,44519,44520,44521,44522,44523,44524,44525,44526,44527,44528,44529,44530,44531,44532,44533,44534,44535,44536,44537,44538,44539,44540,44541,44542,44543,44544,44545,44546,44547,44548,44549,44550,44551,44552,44553,44554,44555,44556,44557,44558,44559,44560,44561,44562,44563,44564,44565,44566,44567,44568,44569,44570,44571,44572,44573,44574,44575,44576,44577,44578,44579,44580,44581,44582,44583,44584,44585,44586,44587,44588,44589,44590,44591,44592,44593,44594,44595,44596,44597,44598,44599,44600,44601,44602,44603,44604,44605,44606,44607,44608,44609,44610,44611,44612,44613,44614,44615,44616,44617,44618,44619,44620,44621,44622,44623,44624,44625,44626,44627,44628,44629,44630,44631,44632,44633,44634,44635,44636,44637,44638,44639,44640,44641,44642,44643,44644,44645,44646,44647,44648,44649,44650,44651,44652,44653,44654,44655,44656,44657,44658,44659,44660,44661,44662,44663,44664,44665,44666,44667,44668,44669,44670,44671,44672,44673,44674,44675,44676,44677,44678,44679,44680,44681,44682,44683,44684,44685,44686,44687,44688,44689,44690,44691,44692,44693,44694,44695,44696,44697,44698,44699,44700,44701,44702,44703,44704,44705,44706,44707,44708,44709,44710,44711,44712,44713,44714,44715,44716,44717,44718,44719,44720,44721,44722,44723,44724,44725,44726,44727,44728,44729,44730,44731,44732,44733,44734,44735,44736,44737,44738,44739,44740,44741,44742,44743,44744,44745,44746,44747,44748,44749,44750,44751,44752,44753,44754,44755,44756,44757,44758,44759,44760,44761,44762,44763,44764,44765,44766,44767,44768,44769,44770,44771,44772,44773,44774,44775,44776,44777,44778,44779,44780,44781,44782,44783,44784,44785,44786,44787,44788,44789,44790,44791,44792,44793,44794,44795,44796,44797,44798,44799,44800,44801,44802,44803,44804,44805,44806,44807,44808,44809,44810,44811,44812,44813,44814,44815,44816,44817,44818,44819,44820,44821,44822,44823,44824,44825,44826,44827,44828,44829,44830,44831,44832,44833,44834,44835,44836,44837,44838,44839,44840,44841,44842,44843,44844,44845,44846,44847,44848,44849,44850,44851,44852,44853,44854,44855,44856,44857,44858,44859,44860,44861,44862,44863,44864,44865,44866,44867,44868,44869,44870,44871,44872,44873,44874,44875,44876,44877,44878,44879,44880,44881,44882,44883,44884,44885,44886,44887,44888,44889,44890,44891,44892,44893,44894,44895,44896,44897,44898,44899,44900,44901,44902,44903,44904,44905,44906,44907,44908,44909,44910,44911,44912,44913,44914,44915,44916,44917,44918,44919,44920,44921,44922,44923,44924,44925,44926,44927,44928,44929,44930,44931,44932,44933,44934,44935,44936,44937,44938,44939,44940,44941,44942,44943,44944,44945,44946,44947,44948,44949,44950,44951,44952,44953,44954,44955,44956,44957,44958,44959,44960,44961,44962,44963,44964,44965,44966,44967,44968,44969,44970,44971,44972,44973,44974,44975,44976,44977,44978,44979,44980,44981,44982,44983,44984,44985,44986,44987,44988,44989,44990,44991,44992,44993,44994,44995,44996,44997,44998,44999,45000,45001,45002,45003,45004,45005,45006,45007,45008,45009,45010,45011,45012,45013,45014,45015,45016,45017,45018,45019,45020,45021,45022,45023,45024,45025,45026,45027,45028,45029,45030,45031,45032,45033,45034,45035,45036,45037,45038,45039,45040,45041,45042,45043,45044,45045,45046,45047,45048,45049,45050,45051,45052,45053,45054,45055,45056,45057,45058,45059,45060,45061,45062,45063,45064,45065,45066,45067,45068,45069,45070,45071,45072,45073,45074,45075,45076,45077,45078,45079,45080,45081,45082,45083,45084,45085,45086,45087,45088,45089,45090,45091,45092,45093,45094,45095,45096,45097,45098,45099,45100,45101,45102,45103,45104,45105,45106,45107,45108,45109,45110,45111,45112,45113,45114,45115,45116,45117,45118,45119,45120,45121,45122,45123,45124,45125,45126,45127,45128,45129,45130,45131,45132,45133,45134,45135,45136,45137,45138,45139,45140,45141,45142,45143,45144,45145,45146,45147,45148,45149,45150,45151,45152,45153,45154,45155,45156,45157,45158,45159,45160,45161,45162,45163,45164,45165,45166,45167,45168,45169,45170,45171,45172,45173,45174,45175,45176,45177,45178,45179,45180,45181,45182,45183,45184,45185,45186,45187,45188,45189,45190,45191,45192,45193,45194,45195,45196,45197,45198,45199,45200,45201,45202,45203,45204,45205,45206,45207,45208,45209,45210,45211,45212,45213,45214,45215,45216,45217,45218,45219,45220,45221,45222,45223,45224,45225,45226,45227,45228,45229,45230,45231,45232,45233,45234,45235,45236,45237,45238,45239,45240,45241,45242,45243,45244,45245,45246,45247,45248,45249,45250,45251,45252,45253,45254,45255,45256,45257,45258,45259,45260,45261,45262,45263,45264,45265,45266,45267,45268,45269,45270,45271,45272,45273,45274,45275,45276,45277,45278,45279,45280,45281,45282,45283,45284,45285,45286,45287,45288,45289,45290,45291,45292,45293,45294,45295,45296,45297,45298,45299,45300,45301,45302,45303,45304,45305,45306,45307,45308,45309,45310,45311,45312,45313,45314,45315,45316,45317,45318,45319,45320,45321,45322,45323,45324,45325,45326,45327,45328,45329,45330,45331,45332,45333,45334,45335,45336,45337,45338,45339,45340,45341,45342,45343,45344,45345,45346,45347,45348,45349,45350,45351,45352,45353,45354,45355,45356,45357,45358,45359,45360,45361,45362,45363,45364,45365,45366,45367,45368,45369,45370,45371,45372,45373,45374,45375,45376,45377,45378,45379,45380,45381,45382,45383,45384,45385,45386,45387,45388,45389,45390,45391,45392,45393,45394,45395,45396,45397,45398,45399,45400,45401,45402,45403,45404,45405,45406,45407,45408,45409,45410,45411,45412,45413,45414,45415,45416,45417,45418,45419,45420,45421,45422,45423,45424,45425,45426,45427,45428,45429,45430,45431,45432,45433,45434,45435,45436,45437,45438,45439,45440,45441,45442,45443,45444,45445,45446,45447,45448,45449,45450,45451,45452,45453,45454,45455,45456,45457,45458,45459,45460,45461,45462,45463,45464,45465,45466,45467,45468,45469,45470,45471,45472,45473,45474,45475,45476,45477,45478,45479,45480,45481,45482,45483,45484,45485,45486,45487,45488,45489,45490,45491,45492,45493,45494,45495,45496,45497,45498,45499,45500,45501,45502,45503,45504,45505,45506,45507,45508,45509,45510,45511,45512,45513,45514,45515,45516,45517,45518,45519,45520,45521,45522,45523,45524,45525,45526,45527,45528,45529,45530,45531,45532,45533,45534,45535,45536,45537,45538,45539,45540,45541,45542,45543,45544,45545,45546,45547,45548,45549,45550,45551,45552,45553,45554,45555,45556,45557,45558,45559,45560,45561,45562,45563,45564,45565,45566,45567,45568,45569,45570,45571,45572,45573,45574,45575,45576,45577,45578,45579,45580,45581,45582,45583,45584,45585,45586,45587,45588,45589,45590,45591,45592,45593,45594,45595,45596,45597,45598,45599,45600,45601,45602,45603,45604,45605,45606,45607,45608,45609,45610,45611,45612,45613,45614,45615,45616,45617,45618,45619,45620,45621,45622,45623,45624,45625,45626,45627,45628,45629,45630,45631,45632,45633,45634,45635,45636,45637,45638,45639,45640,45641,45642,45643,45644,45645,45646,45647,45648,45649,45650,45651,45652,45653,45654,45655,45656,45657,45658,45659,45660,45661,45662,45663,45664,45665,45666,45667,45668,45669,45670,45671,45672,45673,45674,45675,45676,45677,45678,45679,45680,45681,45682,45683,45684,45685,45686,45687,45688,45689,45690,45691,45692,45693,45694,45695,45696,45697,45698,45699,45700,45701,45702,45703,45704,45705,45706,45707,45708,45709,45710,45711,45712,45713,45714,45715,45716,45717,45718,45719,45720,45721,45722,45723,45724,45725,45726,45727,45728,45729,45730,45731,45732,45733,45734,45735,45736,45737,45738,45739,45740,45741,45742,45743,45744,45745,45746,45747,45748,45749,45750,45751,45752,45753,45754,45755,45756,45757,45758,45759,45760,45761,45762,45763,45764,45765,45766,45767,45768,45769,45770,45771,45772,45773,45774,45775,45776,45777,45778,45779,45780,45781,45782,45783,45784,45785,45786,45787,45788,45789,45790,45791,45792,45793,45794,45795,45796,45797,45798,45799,45800,45801,45802,45803,45804,45805,45806,45807,45808,45809,45810,45811,45812,45813,45814,45815,45816,45817,45818,45819,45820,45821,45822,45823,45824,45825,45826,45827,45828,45829,45830,45831,45832,45833,45834,45835,45836,45837,45838,45839,45840,45841,45842,45843,45844,45845,45846,45847,45848,45849,45850,45851,45852,45853,45854,45855,45856,45857,45858,45859,45860,45861,45862,45863,45864,45865,45866,45867,45868,45869,45870,45871,45872,45873,45874,45875,45876,45877,45878,45879,45880,45881,45882,45883,45884,45885,45886,45887,45888,45889,45890,45891,45892,45893,45894,45895,45896,45897,45898,45899,45900,45901,45902,45903,45904,45905,45906,45907,45908,45909,45910,45911,45912,45913,45914,45915,45916,45917,45918,45919,45920,45921,45922,45923,45924,45925,45926,45927,45928,45929,45930,45931,45932,45933,45934,45935,45936,45937,45938,45939,45940,45941,45942,45943,45944,45945,45946,45947,45948,45949,45950,45951,45952,45953,45954,45955,45956,45957,45958,45959,45960,45961,45962,45963,45964,45965,45966,45967,45968,45969,45970,45971,45972,45973,45974,45975,45976,45977,45978,45979,45980,45981,45982,45983,45984,45985,45986,45987,45988,45989,45990,45991,45992,45993,45994,45995,45996,45997,45998,45999,46000,46001,46002,46003,46004,46005,46006,46007,46008,46009,46010,46011,46012,46013,46014,46015,46016,46017,46018,46019,46020,46021,46022,46023,46024,46025,46026,46027,46028,46029,46030,46031,46032,46033,46034,46035,46036,46037,46038,46039,46040,46041,46042,46043,46044,46045,46046,46047,46048,46049,46050,46051,46052,46053,46054,46055,46056,46057,46058,46059,46060,46061,46062,46063,46064,46065,46066,46067,46068,46069,46070,46071,46072,46073,46074,46075,46076,46077,46078,46079,46080,46081,46082,46083,46084,46085,46086,46087,46088,46089,46090,46091,46092,46093,46094,46095,46096,46097,46098,46099,46100,46101,46102,46103,46104,46105,46106,46107,46108,46109,46110,46111,46112,46113,46114,46115,46116,46117,46118,46119,46120,46121,46122,46123,46124,46125,46126,46127,46128,46129,46130,46131,46132,46133,46134,46135,46136,46137,46138,46139,46140,46141,46142,46143,46144,46145,46146,46147,46148,46149,46150,46151,46152,46153,46154,46155,46156,46157,46158,46159,46160,46161,46162,46163,46164,46165,46166,46167,46168,46169,46170,46171,46172,46173,46174,46175,46176,46177,46178,46179,46180,46181,46182,46183,46184,46185,46186,46187,46188,46189,46190,46191,46192,46193,46194,46195,46196,46197,46198,46199,46200,46201,46202,46203,46204,46205,46206,46207,46208,46209,46210,46211,46212,46213,46214,46215,46216,46217,46218,46219,46220,46221,46222,46223,46224,46225,46226,46227,46228,46229,46230,46231,46232,46233,46234,46235,46236,46237,46238,46239,46240,46241,46242,46243,46244,46245,46246,46247,46248,46249,46250,46251,46252,46253,46254,46255,46256,46257,46258,46259,46260,46261,46262,46263,46264,46265,46266,46267,46268,46269,46270,46271,46272,46273,46274,46275,46276,46277,46278,46279,46280,46281,46282,46283,46284,46285,46286,46287,46288,46289,46290,46291,46292,46293,46294,46295,46296,46297,46298,46299,46300,46301,46302,46303,46304,46305,46306,46307,46308,46309,46310,46311,46312,46313,46314,46315,46316,46317,46318,46319,46320,46321,46322,46323,46324,46325,46326,46327,46328,46329,46330,46331,46332,46333,46334,46335,46336,46337,46338,46339,46340,46341,46342,46343,46344,46345,46346,46347,46348,46349,46350,46351,46352,46353,46354,46355,46356,46357,46358,46359,46360,46361,46362,46363,46364,46365,46366,46367,46368,46369,46370,46371,46372,46373,46374,46375,46376,46377,46378,46379,46380,46381,46382,46383,46384,46385,46386,46387,46388,46389,46390,46391,46392,46393,46394,46395,46396,46397,46398,46399,46400,46401,46402,46403,46404,46405,46406,46407,46408,46409,46410,46411,46412,46413,46414,46415,46416,46417,46418,46419,46420,46421,46422,46423,46424,46425,46426,46427,46428,46429,46430,46431,46432,46433,46434,46435,46436,46437,46438,46439,46440,46441,46442,46443,46444,46445,46446,46447,46448,46449,46450,46451,46452,46453,46454,46455,46456,46457,46458,46459,46460,46461,46462,46463,46464,46465,46466,46467,46468,46469,46470,46471,46472,46473,46474,46475,46476,46477,46478,46479,46480,46481,46482,46483,46484,46485,46486,46487,46488,46489,46490,46491,46492,46493,46494,46495,46496,46497,46498,46499,46500,46501,46502,46503,46504,46505,46506,46507,46508,46509,46510,46511,46512,46513,46514,46515,46516,46517,46518,46519,46520,46521,46522,46523,46524,46525,46526,46527,46528,46529,46530,46531,46532,46533,46534,46535,46536,46537,46538,46539,46540,46541,46542,46543,46544,46545,46546,46547,46548,46549,46550,46551,46552,46553,46554,46555,46556,46557,46558,46559,46560,46561,46562,46563,46564,46565,46566,46567,46568,46569,46570,46571,46572,46573,46574,46575,46576,46577,46578,46579,46580,46581,46582,46583,46584,46585,46586,46587,46588,46589,46590,46591,46592,46593,46594,46595,46596,46597,46598,46599,46600,46601,46602,46603,46604,46605,46606,46607,46608,46609,46610,46611,46612,46613,46614,46615,46616,46617,46618,46619,46620,46621,46622,46623,46624,46625,46626,46627,46628,46629,46630,46631,46632,46633,46634,46635,46636,46637,46638,46639,46640,46641,46642,46643,46644,46645,46646,46647,46648,46649,46650,46651,46652,46653,46654,46655,46656,46657,46658,46659,46660,46661,46662,46663,46664,46665,46666,46667,46668,46669,46670,46671,46672,46673,46674,46675,46676,46677,46678,46679,46680,46681,46682,46683,46684,46685,46686,46687,46688,46689,46690,46691,46692,46693,46694,46695,46696,46697,46698,46699,46700,46701,46702,46703,46704,46705,46706,46707,46708,46709,46710,46711,46712,46713,46714,46715,46716,46717,46718,46719,46720,46721,46722,46723,46724,46725,46726,46727,46728,46729,46730,46731,46732,46733,46734,46735,46736,46737,46738,46739,46740,46741,46742,46743,46744,46745,46746,46747,46748,46749,46750,46751,46752,46753,46754,46755,46756,46757,46758,46759,46760,46761,46762,46763,46764,46765,46766,46767,46768,46769,46770,46771,46772,46773,46774,46775,46776,46777,46778,46779,46780,46781,46782,46783,46784,46785,46786,46787,46788,46789,46790,46791,46792,46793,46794,46795,46796,46797,46798,46799,46800,46801,46802,46803,46804,46805,46806,46807,46808,46809,46810,46811,46812,46813,46814,46815,46816,46817,46818,46819,46820,46821,46822,46823,46824,46825,46826,46827,46828,46829,46830,46831,46832,46833,46834,46835,46836,46837,46838,46839,46840,46841,46842,46843,46844,46845,46846,46847,46848,46849,46850,46851,46852,46853,46854,46855,46856,46857,46858,46859,46860,46861,46862,46863,46864,46865,46866,46867,46868,46869,46870,46871,46872,46873,46874,46875,46876,46877,46878,46879,46880,46881,46882,46883,46884,46885,46886,46887,46888,46889,46890,46891,46892,46893,46894,46895,46896,46897,46898,46899,46900,46901,46902,46903,46904,46905,46906,46907,46908,46909,46910,46911,46912,46913,46914,46915,46916,46917,46918,46919,46920,46921,46922,46923,46924,46925,46926,46927,46928,46929,46930,46931,46932,46933,46934,46935,46936,46937,46938,46939,46940,46941,46942,46943,46944,46945,46946,46947,46948,46949,46950,46951,46952,46953,46954,46955,46956,46957,46958,46959,46960,46961,46962,46963,46964,46965,46966,46967,46968,46969,46970,46971,46972,46973,46974,46975,46976,46977,46978,46979,46980,46981,46982,46983,46984,46985,46986,46987,46988,46989,46990,46991,46992,46993,46994,46995,46996,46997,46998,46999,47000,47001,47002,47003,47004,47005,47006,47007,47008,47009,47010,47011,47012,47013,47014,47015,47016,47017,47018,47019,47020,47021,47022,47023,47024,47025,47026,47027,47028,47029,47030,47031,47032,47033,47034,47035,47036,47037,47038,47039,47040,47041,47042,47043,47044,47045,47046,47047,47048,47049,47050,47051,47052,47053,47054,47055,47056,47057,47058,47059,47060,47061,47062,47063,47064,47065,47066,47067,47068,47069,47070,47071,47072,47073,47074,47075,47076,47077,47078,47079,47080,47081,47082,47083,47084,47085,47086,47087,47088,47089,47090,47091,47092,47093,47094,47095,47096,47097,47098,47099,47100,47101,47102,47103,47104,47105,47106,47107,47108,47109,47110,47111,47112,47113,47114,47115,47116,47117,47118,47119,47120,47121,47122,47123,47124,47125,47126,47127,47128,47129,47130,47131,47132,47133,47134,47135,47136,47137,47138,47139,47140,47141,47142,47143,47144,47145,47146,47147,47148,47149,47150,47151,47152,47153,47154,47155,47156,47157,47158,47159,47160,47161,47162,47163,47164,47165,47166,47167,47168,47169,47170,47171,47172,47173,47174,47175,47176,47177,47178,47179,47180,47181,47182,47183,47184,47185,47186,47187,47188,47189,47190,47191,47192,47193,47194,47195,47196,47197,47198,47199,47200,47201,47202,47203,47204,47205,47206,47207,47208,47209,47210,47211,47212,47213,47214,47215,47216,47217,47218,47219,47220,47221,47222,47223,47224,47225,47226,47227,47228,47229,47230,47231,47232,47233,47234,47235,47236,47237,47238,47239,47240,47241,47242,47243,47244,47245,47246,47247,47248,47249,47250,47251,47252,47253,47254,47255,47256,47257,47258,47259,47260,47261,47262,47263,47264,47265,47266,47267,47268,47269,47270,47271,47272,47273,47274,47275,47276,47277,47278,47279,47280,47281,47282,47283,47284,47285,47286,47287,47288,47289,47290,47291,47292,47293,47294,47295,47296,47297,47298,47299,47300,47301,47302,47303,47304,47305,47306,47307,47308,47309,47310,47311,47312,47313,47314,47315,47316,47317,47318,47319,47320,47321,47322,47323,47324,47325,47326,47327,47328,47329,47330,47331,47332,47333,47334,47335,47336,47337,47338,47339,47340,47341,47342,47343,47344,47345,47346,47347,47348,47349,47350,47351,47352,47353,47354,47355,47356,47357,47358,47359,47360,47361,47362,47363,47364,47365,47366,47367,47368,47369,47370,47371,47372,47373,47374,47375,47376,47377,47378,47379,47380,47381,47382,47383,47384,47385,47386,47387,47388,47389,47390,47391,47392,47393,47394,47395,47396,47397,47398,47399,47400,47401,47402,47403,47404,47405,47406,47407,47408,47409,47410,47411,47412,47413,47414,47415,47416,47417,47418,47419,47420,47421,47422,47423,47424,47425,47426,47427,47428,47429,47430,47431,47432,47433,47434,47435,47436,47437,47438,47439,47440,47441,47442,47443,47444,47445,47446,47447,47448,47449,47450,47451,47452,47453,47454,47455,47456,47457,47458,47459,47460,47461,47462,47463,47464,47465,47466,47467,47468,47469,47470,47471,47472,47473,47474,47475,47476,47477,47478,47479,47480,47481,47482,47483,47484,47485,47486,47487,47488,47489,47490,47491,47492,47493,47494,47495,47496,47497,47498,47499,47500,47501,47502,47503,47504,47505,47506,47507,47508,47509,47510,47511,47512,47513,47514,47515,47516,47517,47518,47519,47520,47521,47522,47523,47524,47525,47526,47527,47528,47529,47530,47531,47532,47533,47534,47535,47536,47537,47538,47539,47540,47541,47542,47543,47544,47545,47546,47547,47548,47549,47550,47551,47552,47553,47554,47555,47556,47557,47558,47559,47560,47561,47562,47563,47564,47565,47566,47567,47568,47569,47570,47571,47572,47573,47574,47575,47576,47577,47578,47579,47580,47581,47582,47583,47584,47585,47586,47587,47588,47589,47590,47591,47592,47593,47594,47595,47596,47597,47598,47599,47600,47601,47602,47603,47604,47605,47606,47607,47608,47609,47610,47611,47612,47613,47614,47615,47616,47617,47618,47619,47620,47621,47622,47623,47624,47625,47626,47627,47628,47629,47630,47631,47632,47633,47634,47635,47636,47637,47638,47639,47640,47641,47642,47643,47644,47645,47646,47647,47648,47649,47650,47651,47652,47653,47654,47655,47656,47657,47658,47659,47660,47661,47662,47663,47664,47665,47666,47667,47668,47669,47670,47671,47672,47673,47674,47675,47676,47677,47678,47679,47680,47681,47682,47683,47684,47685,47686,47687,47688,47689,47690,47691,47692,47693,47694,47695,47696,47697,47698,47699,47700,47701,47702,47703,47704,47705,47706,47707,47708,47709,47710,47711,47712,47713,47714,47715,47716,47717,47718,47719,47720,47721,47722,47723,47724,47725,47726,47727,47728,47729,47730,47731,47732,47733,47734,47735,47736,47737,47738,47739,47740,47741,47742,47743,47744,47745,47746,47747,47748,47749,47750,47751,47752,47753,47754,47755,47756,47757,47758,47759,47760,47761,47762,47763,47764,47765,47766,47767,47768,47769,47770,47771,47772,47773,47774,47775,47776,47777,47778,47779,47780,47781,47782,47783,47784,47785,47786,47787,47788,47789,47790,47791,47792,47793,47794,47795,47796,47797,47798,47799,47800,47801,47802,47803,47804,47805,47806,47807,47808,47809,47810,47811,47812,47813,47814,47815,47816,47817,47818,47819,47820,47821,47822,47823,47824,47825,47826,47827,47828,47829,47830,47831,47832,47833,47834,47835,47836,47837,47838,47839,47840,47841,47842,47843,47844,47845,47846,47847,47848,47849,47850,47851,47852,47853,47854,47855,47856,47857,47858,47859,47860,47861,47862,47863,47864,47865,47866,47867,47868,47869,47870,47871,47872,47873,47874,47875,47876,47877,47878,47879,47880,47881,47882,47883,47884,47885,47886,47887,47888,47889,47890,47891,47892,47893,47894,47895,47896,47897,47898,47899,47900,47901,47902,47903,47904,47905,47906,47907,47908,47909,47910,47911,47912,47913,47914,47915,47916,47917,47918,47919,47920,47921,47922,47923,47924,47925,47926,47927,47928,47929,47930,47931,47932,47933,47934,47935,47936,47937,47938,47939,47940,47941,47942,47943,47944,47945,47946,47947,47948,47949,47950,47951,47952,47953,47954,47955,47956,47957,47958,47959,47960,47961,47962,47963,47964,47965,47966,47967,47968,47969,47970,47971,47972,47973,47974,47975,47976,47977,47978,47979,47980,47981,47982,47983,47984,47985,47986,47987,47988,47989,47990,47991,47992,47993,47994,47995,47996,47997,47998,47999,48000,48001,48002,48003,48004,48005,48006,48007,48008,48009,48010,48011,48012,48013,48014,48015,48016,48017,48018,48019,48020,48021,48022,48023,48024,48025,48026,48027,48028,48029,48030,48031,48032,48033,48034,48035,48036,48037,48038,48039,48040,48041,48042,48043,48044,48045,48046,48047,48048,48049,48050,48051,48052,48053,48054,48055,48056,48057,48058,48059,48060,48061,48062,48063,48064,48065,48066,48067,48068,48069,48070,48071,48072,48073,48074,48075,48076,48077,48078,48079,48080,48081,48082,48083,48084,48085,48086,48087,48088,48089,48090,48091,48092,48093,48094,48095,48096,48097,48098,48099,48100,48101,48102,48103,48104,48105,48106,48107,48108,48109,48110,48111,48112,48113,48114,48115,48116,48117,48118,48119,48120,48121,48122,48123,48124,48125,48126,48127,48128,48129,48130,48131,48132,48133,48134,48135,48136,48137,48138,48139,48140,48141,48142,48143,48144,48145,48146,48147,48148,48149,48150,48151,48152,48153,48154,48155,48156,48157,48158,48159,48160,48161,48162,48163,48164,48165,48166,48167,48168,48169,48170,48171,48172,48173,48174,48175,48176,48177,48178,48179,48180,48181,48182,48183,48184,48185,48186,48187,48188,48189,48190,48191,48192,48193,48194,48195,48196,48197,48198,48199,48200,48201,48202,48203,48204,48205,48206,48207,48208,48209,48210,48211,48212,48213,48214,48215,48216,48217,48218,48219,48220,48221,48222,48223,48224,48225,48226,48227,48228,48229,48230,48231,48232,48233,48234,48235,48236,48237,48238,48239,48240,48241,48242,48243,48244,48245,48246,48247,48248,48249,48250,48251,48252,48253,48254,48255,48256,48257,48258,48259,48260,48261,48262,48263,48264,48265,48266,48267,48268,48269,48270,48271,48272,48273,48274,48275,48276,48277,48278,48279,48280,48281,48282,48283,48284,48285,48286,48287,48288,48289,48290,48291,48292,48293,48294,48295,48296,48297,48298,48299,48300,48301,48302,48303,48304,48305,48306,48307,48308,48309,48310,48311,48312,48313,48314,48315,48316,48317,48318,48319,48320,48321,48322,48323,48324,48325,48326,48327,48328,48329,48330,48331,48332,48333,48334,48335,48336,48337,48338,48339,48340,48341,48342,48343,48344,48345,48346,48347,48348,48349,48350,48351,48352,48353,48354,48355,48356,48357,48358,48359,48360,48361,48362,48363,48364,48365,48366,48367,48368,48369,48370,48371,48372,48373,48374,48375,48376,48377,48378,48379,48380,48381,48382,48383,48384,48385,48386,48387,48388,48389,48390,48391,48392,48393,48394,48395,48396,48397,48398,48399,48400,48401,48402,48403,48404,48405,48406,48407,48408,48409,48410,48411,48412,48413,48414,48415,48416,48417,48418,48419,48420,48421,48422,48423,48424,48425,48426,48427,48428,48429,48430,48431,48432,48433,48434,48435,48436,48437,48438,48439,48440,48441,48442,48443,48444,48445,48446,48447,48448,48449,48450,48451,48452,48453,48454,48455,48456,48457,48458,48459,48460,48461,48462,48463,48464,48465,48466,48467,48468,48469,48470,48471,48472,48473,48474,48475,48476,48477,48478,48479,48480,48481,48482,48483,48484,48485,48486,48487,48488,48489,48490,48491,48492,48493,48494,48495,48496,48497,48498,48499,48500,48501,48502,48503,48504,48505,48506,48507,48508,48509,48510,48511,48512,48513,48514,48515,48516,48517,48518,48519,48520,48521,48522,48523,48524,48525,48526,48527,48528,48529,48530,48531,48532,48533,48534,48535,48536,48537,48538,48539,48540,48541,48542,48543,48544,48545,48546,48547,48548,48549,48550,48551,48552,48553,48554,48555,48556,48557,48558,48559,48560,48561,48562,48563,48564,48565,48566,48567,48568,48569,48570,48571,48572,48573,48574,48575,48576,48577,48578,48579,48580,48581,48582,48583,48584,48585,48586,48587,48588,48589,48590,48591,48592,48593,48594,48595,48596,48597,48598,48599,48600,48601,48602,48603,48604,48605,48606,48607,48608,48609,48610,48611,48612,48613,48614,48615,48616,48617,48618,48619,48620,48621,48622,48623,48624,48625,48626,48627,48628,48629,48630,48631,48632,48633,48634,48635,48636,48637,48638,48639,48640,48641,48642,48643,48644,48645,48646,48647,48648,48649,48650,48651,48652,48653,48654,48655,48656,48657,48658,48659,48660,48661,48662,48663,48664,48665,48666,48667,48668,48669,48670,48671,48672,48673,48674,48675,48676,48677,48678,48679,48680,48681,48682,48683,48684,48685,48686,48687,48688,48689,48690,48691,48692,48693,48694,48695,48696,48697,48698,48699,48700,48701,48702,48703,48704,48705,48706,48707,48708,48709,48710,48711,48712,48713,48714,48715,48716,48717,48718,48719,48720,48721,48722,48723,48724,48725,48726,48727,48728,48729,48730,48731,48732,48733,48734,48735,48736,48737,48738,48739,48740,48741,48742,48743,48744,48745,48746,48747,48748,48749,48750,48751,48752,48753,48754,48755,48756,48757,48758,48759,48760,48761,48762,48763,48764,48765,48766,48767,48768,48769,48770,48771,48772,48773,48774,48775,48776,48777,48778,48779,48780,48781,48782,48783,48784,48785,48786,48787,48788,48789,48790,48791,48792,48793,48794,48795,48796,48797,48798,48799,48800,48801,48802,48803,48804,48805,48806,48807,48808,48809,48810,48811,48812,48813,48814,48815,48816,48817,48818,48819,48820,48821,48822,48823,48824,48825,48826,48827,48828,48829,48830,48831,48832,48833,48834,48835,48836,48837,48838,48839,48840,48841,48842,48843,48844,48845,48846,48847,48848,48849,48850,48851,48852,48853,48854,48855,48856,48857,48858,48859,48860,48861,48862,48863,48864,48865,48866,48867,48868,48869,48870,48871,48872,48873,48874,48875,48876,48877,48878,48879,48880,48881,48882,48883,48884,48885,48886,48887,48888,48889,48890,48891,48892,48893,48894,48895,48896,48897,48898,48899,48900,48901,48902,48903,48904,48905,48906,48907,48908,48909,48910,48911,48912,48913,48914,48915,48916,48917,48918,48919,48920,48921,48922,48923,48924,48925,48926,48927,48928,48929,48930,48931,48932,48933,48934,48935,48936,48937,48938,48939,48940,48941,48942,48943,48944,48945,48946,48947,48948,48949,48950,48951,48952,48953,48954,48955,48956,48957,48958,48959,48960,48961,48962,48963,48964,48965,48966,48967,48968,48969,48970,48971,48972,48973,48974,48975,48976,48977,48978,48979,48980,48981,48982,48983,48984,48985,48986,48987,48988,48989,48990,48991,48992,48993,48994,48995,48996,48997,48998,48999,49000,49001,49002,49003,49004,49005,49006,49007,49008,49009,49010,49011,49012,49013,49014,49015,49016,49017,49018,49019,49020,49021,49022,49023,49024,49025,49026,49027,49028,49029,49030,49031,49032,49033,49034,49035,49036,49037,49038,49039,49040,49041,49042,49043,49044,49045,49046,49047,49048,49049,49050,49051,49052,49053,49054,49055,49056,49057,49058,49059,49060,49061,49062,49063,49064,49065,49066,49067,49068,49069,49070,49071,49072,49073,49074,49075,49076,49077,49078,49079,49080,49081,49082,49083,49084,49085,49086,49087,49088,49089,49090,49091,49092,49093,49094,49095,49096,49097,49098,49099,49100,49101,49102,49103,49104,49105,49106,49107,49108,49109,49110,49111,49112,49113,49114,49115,49116,49117,49118,49119,49120,49121,49122,49123,49124,49125,49126,49127,49128,49129,49130,49131,49132,49133,49134,49135,49136,49137,49138,49139,49140,49141,49142,49143,49144,49145,49146,49147,49148,49149,49150,49151,49152,49153,49154,49155,49156,49157,49158,49159,49160,49161,49162,49163,49164,49165,49166,49167,49168,49169,49170,49171,49172,49173,49174,49175,49176,49177,49178,49179,49180,49181,49182,49183,49184,49185,49186,49187,49188,49189,49190,49191,49192,49193,49194,49195,49196,49197,49198,49199,49200,49201,49202,49203,49204,49205,49206,49207,49208,49209,49210,49211,49212,49213,49214,49215,49216,49217,49218,49219,49220,49221,49222,49223,49224,49225,49226,49227,49228,49229,49230,49231,49232,49233,49234,49235,49236,49237,49238,49239,49240,49241,49242,49243,49244,49245,49246,49247,49248,49249,49250,49251,49252,49253,49254,49255,49256,49257,49258,49259,49260,49261,49262,49263,49264,49265,49266,49267,49268,49269,49270,49271,49272,49273,49274,49275,49276,49277,49278,49279,49280,49281,49282,49283,49284,49285,49286,49287,49288,49289,49290,49291,49292,49293,49294,49295,49296,49297,49298,49299,49300,49301,49302,49303,49304,49305,49306,49307,49308,49309,49310,49311,49312,49313,49314,49315,49316,49317,49318,49319,49320,49321,49322,49323,49324,49325,49326,49327,49328,49329,49330,49331,49332,49333,49334,49335,49336,49337,49338,49339,49340,49341,49342,49343,49344,49345,49346,49347,49348,49349,49350,49351,49352,49353,49354,49355,49356,49357,49358,49359,49360,49361,49362,49363,49364,49365,49366,49367,49368,49369,49370,49371,49372,49373,49374,49375,49376,49377,49378,49379,49380,49381,49382,49383,49384,49385,49386,49387,49388,49389,49390,49391,49392,49393,49394,49395,49396,49397,49398,49399,49400,49401,49402,49403,49404,49405,49406,49407,49408,49409,49410,49411,49412,49413,49414,49415,49416,49417,49418,49419,49420,49421,49422,49423,49424,49425,49426,49427,49428,49429,49430,49431,49432,49433,49434,49435,49436,49437,49438,49439,49440,49441,49442,49443,49444,49445,49446,49447,49448,49449,49450,49451,49452,49453,49454,49455,49456,49457,49458,49459,49460,49461,49462,49463,49464,49465,49466,49467,49468,49469,49470,49471,49472,49473,49474,49475,49476,49477,49478,49479,49480,49481,49482,49483,49484,49485,49486,49487,49488,49489,49490,49491,49492,49493,49494,49495,49496,49497,49498,49499,49500,49501,49502,49503,49504,49505,49506,49507,49508,49509,49510,49511,49512,49513,49514,49515,49516,49517,49518,49519,49520,49521,49522,49523,49524,49525,49526,49527,49528,49529,49530,49531,49532,49533,49534,49535,49536,49537,49538,49539,49540,49541,49542,49543,49544,49545,49546,49547,49548,49549,49550,49551,49552,49553,49554,49555,49556,49557,49558,49559,49560,49561,49562,49563,49564,49565,49566,49567,49568,49569,49570,49571,49572,49573,49574,49575,49576,49577,49578,49579,49580,49581,49582,49583,49584,49585,49586,49587,49588,49589,49590,49591,49592,49593,49594,49595,49596,49597,49598,49599,49600,49601,49602,49603,49604,49605,49606,49607,49608,49609,49610,49611,49612,49613,49614,49615,49616,49617,49618,49619,49620,49621,49622,49623,49624,49625,49626,49627,49628,49629,49630,49631,49632,49633,49634,49635,49636,49637,49638,49639,49640,49641,49642,49643,49644,49645,49646,49647,49648,49649,49650,49651,49652,49653,49654,49655,49656,49657,49658,49659,49660,49661,49662,49663,49664,49665,49666,49667,49668,49669,49670,49671,49672,49673,49674,49675,49676,49677,49678,49679,49680,49681,49682,49683,49684,49685,49686,49687,49688,49689,49690,49691,49692,49693,49694,49695,49696,49697,49698,49699,49700,49701,49702,49703,49704,49705,49706,49707,49708,49709,49710,49711,49712,49713,49714,49715,49716,49717,49718,49719,49720,49721,49722,49723,49724,49725,49726,49727,49728,49729,49730,49731,49732,49733,49734,49735,49736,49737,49738,49739,49740,49741,49742,49743,49744,49745,49746,49747,49748,49749,49750,49751,49752,49753,49754,49755,49756,49757,49758,49759,49760,49761,49762,49763,49764,49765,49766,49767,49768,49769,49770,49771,49772,49773,49774,49775,49776,49777,49778,49779,49780,49781,49782,49783,49784,49785,49786,49787,49788,49789,49790,49791,49792,49793,49794,49795,49796,49797,49798,49799,49800,49801,49802,49803,49804,49805,49806,49807,49808,49809,49810,49811,49812,49813,49814,49815,49816,49817,49818,49819,49820,49821,49822,49823,49824,49825,49826,49827,49828,49829,49830,49831,49832,49833,49834,49835,49836,49837,49838,49839,49840,49841,49842,49843,49844,49845,49846,49847,49848,49849,49850,49851,49852,49853,49854,49855,49856,49857,49858,49859,49860,49861,49862,49863,49864,49865,49866,49867,49868,49869,49870,49871,49872,49873,49874,49875,49876,49877,49878,49879,49880,49881,49882,49883,49884,49885,49886,49887,49888,49889,49890,49891,49892,49893,49894,49895,49896,49897,49898,49899,49900,49901,49902,49903,49904,49905,49906,49907,49908,49909,49910,49911,49912,49913,49914,49915,49916,49917,49918,49919,49920,49921,49922,49923,49924,49925,49926,49927,49928,49929,49930,49931,49932,49933,49934,49935,49936,49937,49938,49939,49940,49941,49942,49943,49944,49945,49946,49947,49948,49949,49950,49951,49952,49953,49954,49955,49956,49957,49958,49959,49960,49961,49962,49963,49964,49965,49966,49967,49968,49969,49970,49971,49972,49973,49974,49975,49976,49977,49978,49979,49980,49981,49982,49983,49984,49985,49986,49987,49988,49989,49990,49991,49992,49993,49994,49995,49996,49997,49998,49999,50000,50001,50002,50003,50004,50005,50006,50007,50008,50009,50010,50011,50012,50013,50014,50015,50016,50017,50018,50019,50020,50021,50022,50023,50024,50025,50026,50027,50028,50029,50030,50031,50032,50033,50034,50035,50036,50037,50038,50039,50040,50041,50042,50043,50044,50045,50046,50047,50048,50049,50050,50051,50052,50053,50054,50055,50056,50057,50058,50059,50060,50061,50062,50063,50064,50065,50066,50067,50068,50069,50070,50071,50072,50073,50074,50075,50076,50077,50078,50079,50080,50081,50082,50083,50084,50085,50086,50087,50088,50089,50090,50091,50092,50093,50094,50095,50096,50097,50098,50099,50100,50101,50102,50103,50104,50105,50106,50107,50108,50109,50110,50111,50112,50113,50114,50115,50116,50117,50118,50119,50120,50121,50122,50123,50124,50125,50126,50127,50128,50129,50130,50131,50132,50133,50134,50135,50136,50137,50138,50139,50140,50141,50142,50143,50144,50145,50146,50147,50148,50149,50150,50151,50152,50153,50154,50155,50156,50157,50158,50159,50160,50161,50162,50163,50164,50165,50166,50167,50168,50169,50170,50171,50172,50173,50174,50175,50176,50177,50178,50179,50180,50181,50182,50183,50184,50185,50186,50187,50188,50189,50190,50191,50192,50193,50194,50195,50196,50197,50198,50199,50200,50201,50202,50203,50204,50205,50206,50207,50208,50209,50210,50211,50212,50213,50214,50215,50216,50217,50218,50219,50220,50221,50222,50223,50224,50225,50226,50227,50228,50229,50230,50231,50232,50233,50234,50235,50236,50237,50238,50239,50240,50241,50242,50243,50244,50245,50246,50247,50248,50249,50250,50251,50252,50253,50254,50255,50256,50257,50258,50259,50260,50261,50262,50263,50264,50265,50266,50267,50268,50269,50270,50271,50272,50273,50274,50275,50276,50277,50278,50279,50280,50281,50282,50283,50284,50285,50286,50287,50288,50289,50290,50291,50292,50293,50294,50295,50296,50297,50298,50299,50300,50301,50302,50303,50304,50305,50306,50307,50308,50309,50310,50311,50312,50313,50314,50315,50316,50317,50318,50319,50320,50321,50322,50323,50324,50325,50326,50327,50328,50329,50330,50331,50332,50333,50334,50335,50336,50337,50338,50339,50340,50341,50342,50343,50344,50345,50346,50347,50348,50349,50350,50351,50352,50353,50354,50355,50356,50357,50358,50359,50360,50361,50362,50363,50364,50365,50366,50367,50368,50369,50370,50371,50372,50373,50374,50375,50376,50377,50378,50379,50380,50381,50382,50383,50384,50385,50386,50387,50388,50389,50390,50391,50392,50393,50394,50395,50396,50397,50398,50399,50400,50401,50402,50403,50404,50405,50406,50407,50408,50409,50410,50411,50412,50413,50414,50415,50416,50417,50418,50419,50420,50421,50422,50423,50424,50425,50426,50427,50428,50429,50430,50431,50432,50433,50434,50435,50436,50437,50438,50439,50440,50441,50442,50443,50444,50445,50446,50447,50448,50449,50450,50451,50452,50453,50454,50455,50456,50457,50458,50459,50460,50461,50462,50463,50464,50465,50466,50467,50468,50469,50470,50471,50472,50473,50474,50475,50476,50477,50478,50479,50480,50481,50482,50483,50484,50485,50486,50487,50488,50489,50490,50491,50492,50493,50494,50495,50496,50497,50498,50499,50500,50501,50502,50503,50504,50505,50506,50507,50508,50509,50510,50511,50512,50513,50514,50515,50516,50517,50518,50519,50520,50521,50522,50523,50524,50525,50526,50527,50528,50529,50530,50531,50532,50533,50534,50535,50536,50537,50538,50539,50540,50541,50542,50543,50544,50545,50546,50547,50548,50549,50550,50551,50552,50553,50554,50555,50556,50557,50558,50559,50560,50561,50562,50563,50564,50565,50566,50567,50568,50569,50570,50571,50572,50573,50574,50575,50576,50577,50578,50579,50580,50581,50582,50583,50584,50585,50586,50587,50588,50589,50590,50591,50592,50593,50594,50595,50596,50597,50598,50599,50600,50601,50602,50603,50604,50605,50606,50607,50608,50609,50610,50611,50612,50613,50614,50615,50616,50617,50618,50619,50620,50621,50622,50623,50624,50625,50626,50627,50628,50629,50630,50631,50632,50633,50634,50635,50636,50637,50638,50639,50640,50641,50642,50643,50644,50645,50646,50647,50648,50649,50650,50651,50652,50653,50654,50655,50656,50657,50658,50659,50660,50661,50662,50663,50664,50665,50666,50667,50668,50669,50670,50671,50672,50673,50674,50675,50676,50677,50678,50679,50680,50681,50682,50683,50684,50685,50686,50687,50688,50689,50690,50691,50692,50693,50694,50695,50696,50697,50698,50699,50700,50701,50702,50703,50704,50705,50706,50707,50708,50709,50710,50711,50712,50713,50714,50715,50716,50717,50718,50719,50720,50721,50722,50723,50724,50725,50726,50727,50728,50729,50730,50731,50732,50733,50734,50735,50736,50737,50738,50739,50740,50741,50742,50743,50744,50745,50746,50747,50748,50749,50750,50751,50752,50753,50754,50755,50756,50757,50758,50759,50760,50761,50762,50763,50764,50765,50766,50767,50768,50769,50770,50771,50772,50773,50774,50775,50776,50777,50778,50779,50780,50781,50782,50783,50784,50785,50786,50787,50788,50789,50790,50791,50792,50793,50794,50795,50796,50797,50798,50799,50800,50801,50802,50803,50804,50805,50806,50807,50808,50809,50810,50811,50812,50813,50814,50815,50816,50817,50818,50819,50820,50821,50822,50823,50824,50825,50826,50827,50828,50829,50830,50831,50832,50833,50834,50835,50836,50837,50838,50839,50840,50841,50842,50843,50844,50845,50846,50847,50848,50849,50850,50851,50852,50853,50854,50855,50856,50857,50858,50859,50860,50861,50862,50863,50864,50865,50866,50867,50868,50869,50870,50871,50872,50873,50874,50875,50876,50877,50878,50879,50880,50881,50882,50883,50884,50885,50886,50887,50888,50889,50890,50891,50892,50893,50894,50895,50896,50897,50898,50899,50900,50901,50902,50903,50904,50905,50906,50907,50908,50909,50910,50911,50912,50913,50914,50915,50916,50917,50918,50919,50920,50921,50922,50923,50924,50925,50926,50927,50928,50929,50930,50931,50932,50933,50934,50935,50936,50937,50938,50939,50940,50941,50942,50943,50944,50945,50946,50947,50948,50949,50950,50951,50952,50953,50954,50955,50956,50957,50958,50959,50960,50961,50962,50963,50964,50965,50966,50967,50968,50969,50970,50971,50972,50973,50974,50975,50976,50977,50978,50979,50980,50981,50982,50983,50984,50985,50986,50987,50988,50989,50990,50991,50992,50993,50994,50995,50996,50997,50998,50999,51000,51001,51002,51003,51004,51005,51006,51007,51008,51009,51010,51011,51012,51013,51014,51015,51016,51017,51018,51019,51020,51021,51022,51023,51024,51025,51026,51027,51028,51029,51030,51031,51032,51033,51034,51035,51036,51037,51038,51039,51040,51041,51042,51043,51044,51045,51046,51047,51048,51049,51050,51051,51052,51053,51054,51055,51056,51057,51058,51059,51060,51061,51062,51063,51064,51065,51066,51067,51068,51069,51070,51071,51072,51073,51074,51075,51076,51077,51078,51079,51080,51081,51082,51083,51084,51085,51086,51087,51088,51089,51090,51091,51092,51093,51094,51095,51096,51097,51098,51099,51100,51101,51102,51103,51104,51105,51106,51107,51108,51109,51110,51111,51112,51113,51114,51115,51116,51117,51118,51119,51120,51121,51122,51123,51124,51125,51126,51127,51128,51129,51130,51131,51132,51133,51134,51135,51136,51137,51138,51139,51140,51141,51142,51143,51144,51145,51146,51147,51148,51149,51150,51151,51152,51153,51154,51155,51156,51157,51158,51159,51160,51161,51162,51163,51164,51165,51166,51167,51168,51169,51170,51171,51172,51173,51174,51175,51176,51177,51178,51179,51180,51181,51182,51183,51184,51185,51186,51187,51188,51189,51190,51191,51192,51193,51194,51195,51196,51197,51198,51199,51200,51201,51202,51203,51204,51205,51206,51207,51208,51209,51210,51211,51212,51213,51214,51215,51216,51217,51218,51219,51220,51221,51222,51223,51224,51225,51226,51227,51228,51229,51230,51231,51232,51233,51234,51235,51236,51237,51238,51239,51240,51241,51242,51243,51244,51245,51246,51247,51248,51249,51250,51251,51252,51253,51254,51255,51256,51257,51258,51259,51260,51261,51262,51263,51264,51265,51266,51267,51268,51269,51270,51271,51272,51273,51274,51275,51276,51277,51278,51279,51280,51281,51282,51283,51284,51285,51286,51287,51288,51289,51290,51291,51292,51293,51294,51295,51296,51297,51298,51299,51300,51301,51302,51303,51304,51305,51306,51307,51308,51309,51310,51311,51312,51313,51314,51315,51316,51317,51318,51319,51320,51321,51322,51323,51324,51325,51326,51327,51328,51329,51330,51331,51332,51333,51334,51335,51336,51337,51338,51339,51340,51341,51342,51343,51344,51345,51346,51347,51348,51349,51350,51351,51352,51353,51354,51355,51356,51357,51358,51359,51360,51361,51362,51363,51364,51365,51366,51367,51368,51369,51370,51371,51372,51373,51374,51375,51376,51377,51378,51379,51380,51381,51382,51383,51384,51385,51386,51387,51388,51389,51390,51391,51392,51393,51394,51395,51396,51397,51398,51399,51400,51401,51402,51403,51404,51405,51406,51407,51408,51409,51410,51411,51412,51413,51414,51415,51416,51417,51418,51419,51420,51421,51422,51423,51424,51425,51426,51427,51428,51429,51430,51431,51432,51433,51434,51435,51436,51437,51438,51439,51440,51441,51442,51443,51444,51445,51446,51447,51448,51449,51450,51451,51452,51453,51454,51455,51456,51457,51458,51459,51460,51461,51462,51463,51464,51465,51466,51467,51468,51469,51470,51471,51472,51473,51474,51475,51476,51477,51478,51479,51480,51481,51482,51483,51484,51485,51486,51487,51488,51489,51490,51491,51492,51493,51494,51495,51496,51497,51498,51499,51500,51501,51502,51503,51504,51505,51506,51507,51508,51509,51510,51511,51512,51513,51514,51515,51516,51517,51518,51519,51520,51521,51522,51523,51524,51525,51526,51527,51528,51529,51530,51531,51532,51533,51534,51535,51536,51537,51538,51539,51540,51541,51542,51543,51544,51545,51546,51547,51548,51549,51550,51551,51552,51553,51554,51555,51556,51557,51558,51559,51560,51561,51562,51563,51564,51565,51566,51567,51568,51569,51570,51571,51572,51573,51574,51575,51576,51577,51578,51579,51580,51581,51582,51583,51584,51585,51586,51587,51588,51589,51590,51591,51592,51593,51594,51595,51596,51597,51598,51599,51600,51601,51602,51603,51604,51605,51606,51607,51608,51609,51610,51611,51612,51613,51614,51615,51616,51617,51618,51619,51620,51621,51622,51623,51624,51625,51626,51627,51628,51629,51630,51631,51632,51633,51634,51635,51636,51637,51638,51639,51640,51641,51642,51643,51644,51645,51646,51647,51648,51649,51650,51651,51652,51653,51654,51655,51656,51657,51658,51659,51660,51661,51662,51663,51664,51665,51666,51667,51668,51669,51670,51671,51672,51673,51674,51675,51676,51677,51678,51679,51680,51681,51682,51683,51684,51685,51686,51687,51688,51689,51690,51691,51692,51693,51694,51695,51696,51697,51698,51699,51700,51701,51702,51703,51704,51705,51706,51707,51708,51709,51710,51711,51712,51713,51714,51715,51716,51717,51718,51719,51720,51721,51722,51723,51724,51725,51726,51727,51728,51729,51730,51731,51732,51733,51734,51735,51736,51737,51738,51739,51740,51741,51742,51743,51744,51745,51746,51747,51748,51749,51750,51751,51752,51753,51754,51755,51756,51757,51758,51759,51760,51761,51762,51763,51764,51765,51766,51767,51768,51769,51770,51771,51772,51773,51774,51775,51776,51777,51778,51779,51780,51781,51782,51783,51784,51785,51786,51787,51788,51789,51790,51791,51792,51793,51794,51795,51796,51797,51798,51799,51800,51801,51802,51803,51804,51805,51806,51807,51808,51809,51810,51811,51812,51813,51814,51815,51816,51817,51818,51819,51820,51821,51822,51823,51824,51825,51826,51827,51828,51829,51830,51831,51832,51833,51834,51835,51836,51837,51838,51839,51840,51841,51842,51843,51844,51845,51846,51847,51848,51849,51850,51851,51852,51853,51854,51855,51856,51857,51858,51859,51860,51861,51862,51863,51864,51865,51866,51867,51868,51869,51870,51871,51872,51873,51874,51875,51876,51877,51878,51879,51880,51881,51882,51883,51884,51885,51886,51887,51888,51889,51890,51891,51892,51893,51894,51895,51896,51897,51898,51899,51900,51901,51902,51903,51904,51905,51906,51907,51908,51909,51910,51911,51912,51913,51914,51915,51916,51917,51918,51919,51920,51921,51922,51923,51924,51925,51926,51927,51928,51929,51930,51931,51932,51933,51934,51935,51936,51937,51938,51939,51940,51941,51942,51943,51944,51945,51946,51947,51948,51949,51950,51951,51952,51953,51954,51955,51956,51957,51958,51959,51960,51961,51962,51963,51964,51965,51966,51967,51968,51969,51970,51971,51972,51973,51974,51975,51976,51977,51978,51979,51980,51981,51982,51983,51984,51985,51986,51987,51988,51989,51990,51991,51992,51993,51994,51995,51996,51997,51998,51999,52000,52001,52002,52003,52004,52005,52006,52007,52008,52009,52010,52011,52012,52013,52014,52015,52016,52017,52018,52019,52020,52021,52022,52023,52024,52025,52026,52027,52028,52029,52030,52031,52032,52033,52034,52035,52036,52037,52038,52039,52040,52041,52042,52043,52044,52045,52046,52047,52048,52049,52050,52051,52052,52053,52054,52055,52056,52057,52058,52059,52060,52061,52062,52063,52064,52065,52066,52067,52068,52069,52070,52071,52072,52073,52074,52075,52076,52077,52078,52079,52080,52081,52082,52083,52084,52085,52086,52087,52088,52089,52090,52091,52092,52093,52094,52095,52096,52097,52098,52099,52100,52101,52102,52103,52104,52105,52106,52107,52108,52109,52110,52111,52112,52113,52114,52115,52116,52117,52118,52119,52120,52121,52122,52123,52124,52125,52126,52127,52128,52129,52130,52131,52132,52133,52134,52135,52136,52137,52138,52139,52140,52141,52142,52143,52144,52145,52146,52147,52148,52149,52150,52151,52152,52153,52154,52155,52156,52157,52158,52159,52160,52161,52162,52163,52164,52165,52166,52167,52168,52169,52170,52171,52172,52173,52174,52175,52176,52177,52178,52179,52180,52181,52182,52183,52184,52185,52186,52187,52188,52189,52190,52191,52192,52193,52194,52195,52196,52197,52198,52199,52200,52201,52202,52203,52204,52205,52206,52207,52208,52209,52210,52211,52212,52213,52214,52215,52216,52217,52218,52219,52220,52221,52222,52223,52224,52225,52226,52227,52228,52229,52230,52231,52232,52233,52234,52235,52236,52237,52238,52239,52240,52241,52242,52243,52244,52245,52246,52247,52248,52249,52250,52251,52252,52253,52254,52255,52256,52257,52258,52259,52260,52261,52262,52263,52264,52265,52266,52267,52268,52269,52270,52271,52272,52273,52274,52275,52276,52277,52278,52279,52280,52281,52282,52283,52284,52285,52286,52287,52288,52289,52290,52291,52292,52293,52294,52295,52296,52297,52298,52299,52300,52301,52302,52303,52304,52305,52306,52307,52308,52309,52310,52311,52312,52313,52314,52315,52316,52317,52318,52319,52320,52321,52322,52323,52324,52325,52326,52327,52328,52329,52330,52331,52332,52333,52334,52335,52336,52337,52338,52339,52340,52341,52342,52343,52344,52345,52346,52347,52348,52349,52350,52351,52352,52353,52354,52355,52356,52357,52358,52359,52360,52361,52362,52363,52364,52365,52366,52367,52368,52369,52370,52371,52372,52373,52374,52375,52376,52377,52378,52379,52380,52381,52382,52383,52384,52385,52386,52387,52388,52389,52390,52391,52392,52393,52394,52395,52396,52397,52398,52399,52400,52401,52402,52403,52404,52405,52406,52407,52408,52409,52410,52411,52412,52413,52414,52415,52416,52417,52418,52419,52420,52421,52422,52423,52424,52425,52426,52427,52428,52429,52430,52431,52432,52433,52434,52435,52436,52437,52438,52439,52440,52441,52442,52443,52444,52445,52446,52447,52448,52449,52450,52451,52452,52453,52454,52455,52456,52457,52458,52459,52460,52461,52462,52463,52464,52465,52466,52467,52468,52469,52470,52471,52472,52473,52474,52475,52476,52477,52478,52479,52480,52481,52482,52483,52484,52485,52486,52487,52488,52489,52490,52491,52492,52493,52494,52495,52496,52497,52498,52499,52500,52501,52502,52503,52504,52505,52506,52507,52508,52509,52510,52511,52512,52513,52514,52515,52516,52517,52518,52519,52520,52521,52522,52523,52524,52525,52526,52527,52528,52529,52530,52531,52532,52533,52534,52535,52536,52537,52538,52539,52540,52541,52542,52543,52544,52545,52546,52547,52548,52549,52550,52551,52552,52553,52554,52555,52556,52557,52558,52559,52560,52561,52562,52563,52564,52565,52566,52567,52568,52569,52570,52571,52572,52573,52574,52575,52576,52577,52578,52579,52580,52581,52582,52583,52584,52585,52586,52587,52588,52589,52590,52591,52592,52593,52594,52595,52596,52597,52598,52599,52600,52601,52602,52603,52604,52605,52606,52607,52608,52609,52610,52611,52612,52613,52614,52615,52616,52617,52618,52619,52620,52621,52622,52623,52624,52625,52626,52627,52628,52629,52630,52631,52632,52633,52634,52635,52636,52637,52638,52639,52640,52641,52642,52643,52644,52645,52646,52647,52648,52649,52650,52651,52652,52653,52654,52655,52656,52657,52658,52659,52660,52661,52662,52663,52664,52665,52666,52667,52668,52669,52670,52671,52672,52673,52674,52675,52676,52677,52678,52679,52680,52681,52682,52683,52684,52685,52686,52687,52688,52689,52690,52691,52692,52693,52694,52695,52696,52697,52698,52699,52700,52701,52702,52703,52704,52705,52706,52707,52708,52709,52710,52711,52712,52713,52714,52715,52716,52717,52718,52719,52720,52721,52722,52723,52724,52725,52726,52727,52728,52729,52730,52731,52732,52733,52734,52735,52736,52737,52738,52739,52740,52741,52742,52743,52744,52745,52746,52747,52748,52749,52750,52751,52752,52753,52754,52755,52756,52757,52758,52759,52760,52761,52762,52763,52764,52765,52766,52767,52768,52769,52770,52771,52772,52773,52774,52775,52776,52777,52778,52779,52780,52781,52782,52783,52784,52785,52786,52787,52788,52789,52790,52791,52792,52793,52794,52795,52796,52797,52798,52799,52800,52801,52802,52803,52804,52805,52806,52807,52808,52809,52810,52811,52812,52813,52814,52815,52816,52817,52818,52819,52820,52821,52822,52823,52824,52825,52826,52827,52828,52829,52830,52831,52832,52833,52834,52835,52836,52837,52838,52839,52840,52841,52842,52843,52844,52845,52846,52847,52848,52849,52850,52851,52852,52853,52854,52855,52856,52857,52858,52859,52860,52861,52862,52863,52864,52865,52866,52867,52868,52869,52870,52871,52872,52873,52874,52875,52876,52877,52878,52879,52880,52881,52882,52883,52884,52885,52886,52887,52888,52889,52890,52891,52892,52893,52894,52895,52896,52897,52898,52899,52900,52901,52902,52903,52904,52905,52906,52907,52908,52909,52910,52911,52912,52913,52914,52915,52916,52917,52918,52919,52920,52921,52922,52923,52924,52925,52926,52927,52928,52929,52930,52931,52932,52933,52934,52935,52936,52937,52938,52939,52940,52941,52942,52943,52944,52945,52946,52947,52948,52949,52950,52951,52952,52953,52954,52955,52956,52957,52958,52959,52960,52961,52962,52963,52964,52965,52966,52967,52968,52969,52970,52971,52972,52973,52974,52975,52976,52977,52978,52979,52980,52981,52982,52983,52984,52985,52986,52987,52988,52989,52990,52991,52992,52993,52994,52995,52996,52997,52998,52999,53000,53001,53002,53003,53004,53005,53006,53007,53008,53009,53010,53011,53012,53013,53014,53015,53016,53017,53018,53019,53020,53021,53022,53023,53024,53025,53026,53027,53028,53029,53030,53031,53032,53033,53034,53035,53036,53037,53038,53039,53040,53041,53042,53043,53044,53045,53046,53047,53048,53049,53050,53051,53052,53053,53054,53055,53056,53057,53058,53059,53060,53061,53062,53063,53064,53065,53066,53067,53068,53069,53070,53071,53072,53073,53074,53075,53076,53077,53078,53079,53080,53081,53082,53083,53084,53085,53086,53087,53088,53089,53090,53091,53092,53093,53094,53095,53096,53097,53098,53099,53100,53101,53102,53103,53104,53105,53106,53107,53108,53109,53110,53111,53112,53113,53114,53115,53116,53117,53118,53119,53120,53121,53122,53123,53124,53125,53126,53127,53128,53129,53130,53131,53132,53133,53134,53135,53136,53137,53138,53139,53140,53141,53142,53143,53144,53145,53146,53147,53148,53149,53150,53151,53152,53153,53154,53155,53156,53157,53158,53159,53160,53161,53162,53163,53164,53165,53166,53167,53168,53169,53170,53171,53172,53173,53174,53175,53176,53177,53178,53179,53180,53181,53182,53183,53184,53185,53186,53187,53188,53189,53190,53191,53192,53193,53194,53195,53196,53197,53198,53199,53200,53201,53202,53203,53204,53205,53206,53207,53208,53209,53210,53211,53212,53213,53214,53215,53216,53217,53218,53219,53220,53221,53222,53223,53224,53225,53226,53227,53228,53229,53230,53231,53232,53233,53234,53235,53236,53237,53238,53239,53240,53241,53242,53243,53244,53245,53246,53247,53248,53249,53250,53251,53252,53253,53254,53255,53256,53257,53258,53259,53260,53261,53262,53263,53264,53265,53266,53267,53268,53269,53270,53271,53272,53273,53274,53275,53276,53277,53278,53279,53280,53281,53282,53283,53284,53285,53286,53287,53288,53289,53290,53291,53292,53293,53294,53295,53296,53297,53298,53299,53300,53301,53302,53303,53304,53305,53306,53307,53308,53309,53310,53311,53312,53313,53314,53315,53316,53317,53318,53319,53320,53321,53322,53323,53324,53325,53326,53327,53328,53329,53330,53331,53332,53333,53334,53335,53336,53337,53338,53339,53340,53341,53342,53343,53344,53345,53346,53347,53348,53349,53350,53351,53352,53353,53354,53355,53356,53357,53358,53359,53360,53361,53362,53363,53364,53365,53366,53367,53368,53369,53370,53371,53372,53373,53374,53375,53376,53377,53378,53379,53380,53381,53382,53383,53384,53385,53386,53387,53388,53389,53390,53391,53392,53393,53394,53395,53396,53397,53398,53399,53400,53401,53402,53403,53404,53405,53406,53407,53408,53409,53410,53411,53412,53413,53414,53415,53416,53417,53418,53419,53420,53421,53422,53423,53424,53425,53426,53427,53428,53429,53430,53431,53432,53433,53434,53435,53436,53437,53438,53439,53440,53441,53442,53443,53444,53445,53446,53447,53448,53449,53450,53451,53452,53453,53454,53455,53456,53457,53458,53459,53460,53461,53462,53463,53464,53465,53466,53467,53468,53469,53470,53471,53472,53473,53474,53475,53476,53477,53478,53479,53480,53481,53482,53483,53484,53485,53486,53487,53488,53489,53490,53491,53492,53493,53494,53495,53496,53497,53498,53499,53500,53501,53502,53503,53504,53505,53506,53507,53508,53509,53510,53511,53512,53513,53514,53515,53516,53517,53518,53519,53520,53521,53522,53523,53524,53525,53526,53527,53528,53529,53530,53531,53532,53533,53534,53535,53536,53537,53538,53539,53540,53541,53542,53543,53544,53545,53546,53547,53548,53549,53550,53551,53552,53553,53554,53555,53556,53557,53558,53559,53560,53561,53562,53563,53564,53565,53566,53567,53568,53569,53570,53571,53572,53573,53574,53575,53576,53577,53578,53579,53580,53581,53582,53583,53584,53585,53586,53587,53588,53589,53590,53591,53592,53593,53594,53595,53596,53597,53598,53599,53600,53601,53602,53603,53604,53605,53606,53607,53608,53609,53610,53611,53612,53613,53614,53615,53616,53617,53618,53619,53620,53621,53622,53623,53624,53625,53626,53627,53628,53629,53630,53631,53632,53633,53634,53635,53636,53637,53638,53639,53640,53641,53642,53643,53644,53645,53646,53647,53648,53649,53650,53651,53652,53653,53654,53655,53656,53657,53658,53659,53660,53661,53662,53663,53664,53665,53666,53667,53668,53669,53670,53671,53672,53673,53674,53675,53676,53677,53678,53679,53680,53681,53682,53683,53684,53685,53686,53687,53688,53689,53690,53691,53692,53693,53694,53695,53696,53697,53698,53699,53700,53701,53702,53703,53704,53705,53706,53707,53708,53709,53710,53711,53712,53713,53714,53715,53716,53717,53718,53719,53720,53721,53722,53723,53724,53725,53726,53727,53728,53729,53730,53731,53732,53733,53734,53735,53736,53737,53738,53739,53740,53741,53742,53743,53744,53745,53746,53747,53748,53749,53750,53751,53752,53753,53754,53755,53756,53757,53758,53759,53760,53761,53762,53763,53764,53765,53766,53767,53768,53769,53770,53771,53772,53773,53774,53775,53776,53777,53778,53779,53780,53781,53782,53783,53784,53785,53786,53787,53788,53789,53790,53791,53792,53793,53794,53795,53796,53797,53798,53799,53800,53801,53802,53803,53804,53805,53806,53807,53808,53809,53810,53811,53812,53813,53814,53815,53816,53817,53818,53819,53820,53821,53822,53823,53824,53825,53826,53827,53828,53829,53830,53831,53832,53833,53834,53835,53836,53837,53838,53839,53840,53841,53842,53843,53844,53845,53846,53847,53848,53849,53850,53851,53852,53853,53854,53855,53856,53857,53858,53859,53860,53861,53862,53863,53864,53865,53866,53867,53868,53869,53870,53871,53872,53873,53874,53875,53876,53877,53878,53879,53880,53881,53882,53883,53884,53885,53886,53887,53888,53889,53890,53891,53892,53893,53894,53895,53896,53897,53898,53899,53900,53901,53902,53903,53904,53905,53906,53907,53908,53909,53910,53911,53912,53913,53914,53915,53916,53917,53918,53919,53920,53921,53922,53923,53924,53925,53926,53927,53928,53929,53930,53931,53932,53933,53934,53935,53936,53937,53938,53939,53940,53941,53942,53943,53944,53945,53946,53947,53948,53949,53950,53951,53952,53953,53954,53955,53956,53957,53958,53959,53960,53961,53962,53963,53964,53965,53966,53967,53968,53969,53970,53971,53972,53973,53974,53975,53976,53977,53978,53979,53980,53981,53982,53983,53984,53985,53986,53987,53988,53989,53990,53991,53992,53993,53994,53995,53996,53997,53998,53999,54000,54001,54002,54003,54004,54005,54006,54007,54008,54009,54010,54011,54012,54013,54014,54015,54016,54017,54018,54019,54020,54021,54022,54023,54024,54025,54026,54027,54028,54029,54030,54031,54032,54033,54034,54035,54036,54037,54038,54039,54040,54041,54042,54043,54044,54045,54046,54047,54048,54049,54050,54051,54052,54053,54054,54055,54056,54057,54058,54059,54060,54061,54062,54063,54064,54065,54066,54067,54068,54069,54070,54071,54072,54073,54074,54075,54076,54077,54078,54079,54080,54081,54082,54083,54084,54085,54086,54087,54088,54089,54090,54091,54092,54093,54094,54095,54096,54097,54098,54099,54100,54101,54102,54103,54104,54105,54106,54107,54108,54109,54110,54111,54112,54113,54114,54115,54116,54117,54118,54119,54120,54121,54122,54123,54124,54125,54126,54127,54128,54129,54130,54131,54132,54133,54134,54135,54136,54137,54138,54139,54140,54141,54142,54143,54144,54145,54146,54147,54148,54149,54150,54151,54152,54153,54154,54155,54156,54157,54158,54159,54160,54161,54162,54163,54164,54165,54166,54167,54168,54169,54170,54171,54172,54173,54174,54175,54176,54177,54178,54179,54180,54181,54182,54183,54184,54185,54186,54187,54188,54189,54190,54191,54192,54193,54194,54195,54196,54197,54198,54199,54200,54201,54202,54203,54204,54205,54206,54207,54208,54209,54210,54211,54212,54213,54214,54215,54216,54217,54218,54219,54220,54221,54222,54223,54224,54225,54226,54227,54228,54229,54230,54231,54232,54233,54234,54235,54236,54237,54238,54239,54240,54241,54242,54243,54244,54245,54246,54247,54248,54249,54250,54251,54252,54253,54254,54255,54256,54257,54258,54259,54260,54261,54262,54263,54264,54265,54266,54267,54268,54269,54270,54271,54272,54273,54274,54275,54276,54277,54278,54279,54280,54281,54282,54283,54284,54285,54286,54287,54288,54289,54290,54291,54292,54293,54294,54295,54296,54297,54298,54299,54300,54301,54302,54303,54304,54305,54306,54307,54308,54309,54310,54311,54312,54313,54314,54315,54316,54317,54318,54319,54320,54321,54322,54323,54324,54325,54326,54327,54328,54329,54330,54331,54332,54333,54334,54335,54336,54337,54338,54339,54340,54341,54342,54343,54344,54345,54346,54347,54348,54349,54350,54351,54352,54353,54354,54355,54356,54357,54358,54359,54360,54361,54362,54363,54364,54365,54366,54367,54368,54369,54370,54371,54372,54373,54374,54375,54376,54377,54378,54379,54380,54381,54382,54383,54384,54385,54386,54387,54388,54389,54390,54391,54392,54393,54394,54395,54396,54397,54398,54399,54400,54401,54402,54403,54404,54405,54406,54407,54408,54409,54410,54411,54412,54413,54414,54415,54416,54417,54418,54419,54420,54421,54422,54423,54424,54425,54426,54427,54428,54429,54430,54431,54432,54433,54434,54435,54436,54437,54438,54439,54440,54441,54442,54443,54444,54445,54446,54447,54448,54449,54450,54451,54452,54453,54454,54455,54456,54457,54458,54459,54460,54461,54462,54463,54464,54465,54466,54467,54468,54469,54470,54471,54472,54473,54474,54475,54476,54477,54478,54479,54480,54481,54482,54483,54484,54485,54486,54487,54488,54489,54490,54491,54492,54493,54494,54495,54496,54497,54498,54499,54500,54501,54502,54503,54504,54505,54506,54507,54508,54509,54510,54511,54512,54513,54514,54515,54516,54517,54518,54519,54520,54521,54522,54523,54524,54525,54526,54527,54528,54529,54530,54531,54532,54533,54534,54535,54536,54537,54538,54539,54540,54541,54542,54543,54544,54545,54546,54547,54548,54549,54550,54551,54552,54553,54554,54555,54556,54557,54558,54559,54560,54561,54562,54563,54564,54565,54566,54567,54568,54569,54570,54571,54572,54573,54574,54575,54576,54577,54578,54579,54580,54581,54582,54583,54584,54585,54586,54587,54588,54589,54590,54591,54592,54593,54594,54595,54596,54597,54598,54599,54600,54601,54602,54603,54604,54605,54606,54607,54608,54609,54610,54611,54612,54613,54614,54615,54616,54617,54618,54619,54620,54621,54622,54623,54624,54625,54626,54627,54628,54629,54630,54631,54632,54633,54634,54635,54636,54637,54638,54639,54640,54641,54642,54643,54644,54645,54646,54647,54648,54649,54650,54651,54652,54653,54654,54655,54656,54657,54658,54659,54660,54661,54662,54663,54664,54665,54666,54667,54668,54669,54670,54671,54672,54673,54674,54675,54676,54677,54678,54679,54680,54681,54682,54683,54684,54685,54686,54687,54688,54689,54690,54691,54692,54693,54694,54695,54696,54697,54698,54699,54700,54701,54702,54703,54704,54705,54706,54707,54708,54709,54710,54711,54712,54713,54714,54715,54716,54717,54718,54719,54720,54721,54722,54723,54724,54725,54726,54727,54728,54729,54730,54731,54732,54733,54734,54735,54736,54737,54738,54739,54740,54741,54742,54743,54744,54745,54746,54747,54748,54749,54750,54751,54752,54753,54754,54755,54756,54757,54758,54759,54760,54761,54762,54763,54764,54765,54766,54767,54768,54769,54770,54771,54772,54773,54774,54775,54776,54777,54778,54779,54780,54781,54782,54783,54784,54785,54786,54787,54788,54789,54790,54791,54792,54793,54794,54795,54796,54797,54798,54799,54800,54801,54802,54803,54804,54805,54806,54807,54808,54809,54810,54811,54812,54813,54814,54815,54816,54817,54818,54819,54820,54821,54822,54823,54824,54825,54826,54827,54828,54829,54830,54831,54832,54833,54834,54835,54836,54837,54838,54839,54840,54841,54842,54843,54844,54845,54846,54847,54848,54849,54850,54851,54852,54853,54854,54855,54856,54857,54858,54859,54860,54861,54862,54863,54864,54865,54866,54867,54868,54869,54870,54871,54872,54873,54874,54875,54876,54877,54878,54879,54880,54881,54882,54883,54884,54885,54886,54887,54888,54889,54890,54891,54892,54893,54894,54895,54896,54897,54898,54899,54900,54901,54902,54903,54904,54905,54906,54907,54908,54909,54910,54911,54912,54913,54914,54915,54916,54917,54918,54919,54920,54921,54922,54923,54924,54925,54926,54927,54928,54929,54930,54931,54932,54933,54934,54935,54936,54937,54938,54939,54940,54941,54942,54943,54944,54945,54946,54947,54948,54949,54950,54951,54952,54953,54954,54955,54956,54957,54958,54959,54960,54961,54962,54963,54964,54965,54966,54967,54968,54969,54970,54971,54972,54973,54974,54975,54976,54977,54978,54979,54980,54981,54982,54983,54984,54985,54986,54987,54988,54989,54990,54991,54992,54993,54994,54995,54996,54997,54998,54999,55000,55001,55002,55003,55004,55005,55006,55007,55008,55009,55010,55011,55012,55013,55014,55015,55016,55017,55018,55019,55020,55021,55022,55023,55024,55025,55026,55027,55028,55029,55030,55031,55032,55033,55034,55035,55036,55037,55038,55039,55040,55041,55042,55043,55044,55045,55046,55047,55048,55049,55050,55051,55052,55053,55054,55055,55056,55057,55058,55059,55060,55061,55062,55063,55064,55065,55066,55067,55068,55069,55070,55071,55072,55073,55074,55075,55076,55077,55078,55079,55080,55081,55082,55083,55084,55085,55086,55087,55088,55089,55090,55091,55092,55093,55094,55095,55096,55097,55098,55099,55100,55101,55102,55103,55104,55105,55106,55107,55108,55109,55110,55111,55112,55113,55114,55115,55116,55117,55118,55119,55120,55121,55122,55123,55124,55125,55126,55127,55128,55129,55130,55131,55132,55133,55134,55135,55136,55137,55138,55139,55140,55141,55142,55143,55144,55145,55146,55147,55148,55149,55150,55151,55152,55153,55154,55155,55156,55157,55158,55159,55160,55161,55162,55163,55164,55165,55166,55167,55168,55169,55170,55171,55172,55173,55174,55175,55176,55177,55178,55179,55180,55181,55182,55183,55184,55185,55186,55187,55188,55189,55190,55191,55192,55193,55194,55195,55196,55197,55198,55199,55200,55201,55202,55203,55216,55217,55218,55219,55220,55221,55222,55223,55224,55225,55226,55227,55228,55229,55230,55231,55232,55233,55234,55235,55236,55237,55238,55243,55244,55245,55246,55247,55248,55249,55250,55251,55252,55253,55254,55255,55256,55257,55258,55259,55260,55261,55262,55263,55264,55265,55266,55267,55268,55269,55270,55271,55272,55273,55274,55275,55276,55277,55278,55279,55280,55281,55282,55283,55284,55285,55286,55287,55288,55289,55290,55291,63744,63745,63746,63747,63748,63749,63750,63751,63752,63753,63754,63755,63756,63757,63758,63759,63760,63761,63762,63763,63764,63765,63766,63767,63768,63769,63770,63771,63772,63773,63774,63775,63776,63777,63778,63779,63780,63781,63782,63783,63784,63785,63786,63787,63788,63789,63790,63791,63792,63793,63794,63795,63796,63797,63798,63799,63800,63801,63802,63803,63804,63805,63806,63807,63808,63809,63810,63811,63812,63813,63814,63815,63816,63817,63818,63819,63820,63821,63822,63823,63824,63825,63826,63827,63828,63829,63830,63831,63832,63833,63834,63835,63836,63837,63838,63839,63840,63841,63842,63843,63844,63845,63846,63847,63848,63849,63850,63851,63852,63853,63854,63855,63856,63857,63858,63859,63860,63861,63862,63863,63864,63865,63866,63867,63868,63869,63870,63871,63872,63873,63874,63875,63876,63877,63878,63879,63880,63881,63882,63883,63884,63885,63886,63887,63888,63889,63890,63891,63892,63893,63894,63895,63896,63897,63898,63899,63900,63901,63902,63903,63904,63905,63906,63907,63908,63909,63910,63911,63912,63913,63914,63915,63916,63917,63918,63919,63920,63921,63922,63923,63924,63925,63926,63927,63928,63929,63930,63931,63932,63933,63934,63935,63936,63937,63938,63939,63940,63941,63942,63943,63944,63945,63946,63947,63948,63949,63950,63951,63952,63953,63954,63955,63956,63957,63958,63959,63960,63961,63962,63963,63964,63965,63966,63967,63968,63969,63970,63971,63972,63973,63974,63975,63976,63977,63978,63979,63980,63981,63982,63983,63984,63985,63986,63987,63988,63989,63990,63991,63992,63993,63994,63995,63996,63997,63998,63999,64000,64001,64002,64003,64004,64005,64006,64007,64008,64009,64010,64011,64012,64013,64014,64015,64016,64017,64018,64019,64020,64021,64022,64023,64024,64025,64026,64027,64028,64029,64030,64031,64032,64033,64034,64035,64036,64037,64038,64039,64040,64041,64042,64043,64044,64045,64046,64047,64048,64049,64050,64051,64052,64053,64054,64055,64056,64057,64058,64059,64060,64061,64062,64063,64064,64065,64066,64067,64068,64069,64070,64071,64072,64073,64074,64075,64076,64077,64078,64079,64080,64081,64082,64083,64084,64085,64086,64087,64088,64089,64090,64091,64092,64093,64094,64095,64096,64097,64098,64099,64100,64101,64102,64103,64104,64105,64106,64107,64108,64109,64112,64113,64114,64115,64116,64117,64118,64119,64120,64121,64122,64123,64124,64125,64126,64127,64128,64129,64130,64131,64132,64133,64134,64135,64136,64137,64138,64139,64140,64141,64142,64143,64144,64145,64146,64147,64148,64149,64150,64151,64152,64153,64154,64155,64156,64157,64158,64159,64160,64161,64162,64163,64164,64165,64166,64167,64168,64169,64170,64171,64172,64173,64174,64175,64176,64177,64178,64179,64180,64181,64182,64183,64184,64185,64186,64187,64188,64189,64190,64191,64192,64193,64194,64195,64196,64197,64198,64199,64200,64201,64202,64203,64204,64205,64206,64207,64208,64209,64210,64211,64212,64213,64214,64215,64216,64217,64256,64257,64258,64259,64260,64261,64262,64275,64276,64277,64278,64279,64285,64287,64288,64289,64290,64291,64292,64293,64294,64295,64296,64298,64299,64300,64301,64302,64303,64304,64305,64306,64307,64308,64309,64310,64312,64313,64314,64315,64316,64318,64320,64321,64323,64324,64326,64327,64328,64329,64330,64331,64332,64333,64334,64335,64336,64337,64338,64339,64340,64341,64342,64343,64344,64345,64346,64347,64348,64349,64350,64351,64352,64353,64354,64355,64356,64357,64358,64359,64360,64361,64362,64363,64364,64365,64366,64367,64368,64369,64370,64371,64372,64373,64374,64375,64376,64377,64378,64379,64380,64381,64382,64383,64384,64385,64386,64387,64388,64389,64390,64391,64392,64393,64394,64395,64396,64397,64398,64399,64400,64401,64402,64403,64404,64405,64406,64407,64408,64409,64410,64411,64412,64413,64414,64415,64416,64417,64418,64419,64420,64421,64422,64423,64424,64425,64426,64427,64428,64429,64430,64431,64432,64433,64467,64468,64469,64470,64471,64472,64473,64474,64475,64476,64477,64478,64479,64480,64481,64482,64483,64484,64485,64486,64487,64488,64489,64490,64491,64492,64493,64494,64495,64496,64497,64498,64499,64500,64501,64502,64503,64504,64505,64506,64507,64508,64509,64510,64511,64512,64513,64514,64515,64516,64517,64518,64519,64520,64521,64522,64523,64524,64525,64526,64527,64528,64529,64530,64531,64532,64533,64534,64535,64536,64537,64538,64539,64540,64541,64542,64543,64544,64545,64546,64547,64548,64549,64550,64551,64552,64553,64554,64555,64556,64557,64558,64559,64560,64561,64562,64563,64564,64565,64566,64567,64568,64569,64570,64571,64572,64573,64574,64575,64576,64577,64578,64579,64580,64581,64582,64583,64584,64585,64586,64587,64588,64589,64590,64591,64592,64593,64594,64595,64596,64597,64598,64599,64600,64601,64602,64603,64604,64605,64606,64607,64608,64609,64610,64611,64612,64613,64614,64615,64616,64617,64618,64619,64620,64621,64622,64623,64624,64625,64626,64627,64628,64629,64630,64631,64632,64633,64634,64635,64636,64637,64638,64639,64640,64641,64642,64643,64644,64645,64646,64647,64648,64649,64650,64651,64652,64653,64654,64655,64656,64657,64658,64659,64660,64661,64662,64663,64664,64665,64666,64667,64668,64669,64670,64671,64672,64673,64674,64675,64676,64677,64678,64679,64680,64681,64682,64683,64684,64685,64686,64687,64688,64689,64690,64691,64692,64693,64694,64695,64696,64697,64698,64699,64700,64701,64702,64703,64704,64705,64706,64707,64708,64709,64710,64711,64712,64713,64714,64715,64716,64717,64718,64719,64720,64721,64722,64723,64724,64725,64726,64727,64728,64729,64730,64731,64732,64733,64734,64735,64736,64737,64738,64739,64740,64741,64742,64743,64744,64745,64746,64747,64748,64749,64750,64751,64752,64753,64754,64755,64756,64757,64758,64759,64760,64761,64762,64763,64764,64765,64766,64767,64768,64769,64770,64771,64772,64773,64774,64775,64776,64777,64778,64779,64780,64781,64782,64783,64784,64785,64786,64787,64788,64789,64790,64791,64792,64793,64794,64795,64796,64797,64798,64799,64800,64801,64802,64803,64804,64805,64806,64807,64808,64809,64810,64811,64812,64813,64814,64815,64816,64817,64818,64819,64820,64821,64822,64823,64824,64825,64826,64827,64828,64829,64848,64849,64850,64851,64852,64853,64854,64855,64856,64857,64858,64859,64860,64861,64862,64863,64864,64865,64866,64867,64868,64869,64870,64871,64872,64873,64874,64875,64876,64877,64878,64879,64880,64881,64882,64883,64884,64885,64886,64887,64888,64889,64890,64891,64892,64893,64894,64895,64896,64897,64898,64899,64900,64901,64902,64903,64904,64905,64906,64907,64908,64909,64910,64911,64914,64915,64916,64917,64918,64919,64920,64921,64922,64923,64924,64925,64926,64927,64928,64929,64930,64931,64932,64933,64934,64935,64936,64937,64938,64939,64940,64941,64942,64943,64944,64945,64946,64947,64948,64949,64950,64951,64952,64953,64954,64955,64956,64957,64958,64959,64960,64961,64962,64963,64964,64965,64966,64967,65008,65009,65010,65011,65012,65013,65014,65015,65016,65017,65018,65019,65136,65137,65138,65139,65140,65142,65143,65144,65145,65146,65147,65148,65149,65150,65151,65152,65153,65154,65155,65156,65157,65158,65159,65160,65161,65162,65163,65164,65165,65166,65167,65168,65169,65170,65171,65172,65173,65174,65175,65176,65177,65178,65179,65180,65181,65182,65183,65184,65185,65186,65187,65188,65189,65190,65191,65192,65193,65194,65195,65196,65197,65198,65199,65200,65201,65202,65203,65204,65205,65206,65207,65208,65209,65210,65211,65212,65213,65214,65215,65216,65217,65218,65219,65220,65221,65222,65223,65224,65225,65226,65227,65228,65229,65230,65231,65232,65233,65234,65235,65236,65237,65238,65239,65240,65241,65242,65243,65244,65245,65246,65247,65248,65249,65250,65251,65252,65253,65254,65255,65256,65257,65258,65259,65260,65261,65262,65263,65264,65265,65266,65267,65268,65269,65270,65271,65272,65273,65274,65275,65276,65313,65314,65315,65316,65317,65318,65319,65320,65321,65322,65323,65324,65325,65326,65327,65328,65329,65330,65331,65332,65333,65334,65335,65336,65337,65338,65345,65346,65347,65348,65349,65350,65351,65352,65353,65354,65355,65356,65357,65358,65359,65360,65361,65362,65363,65364,65365,65366,65367,65368,65369,65370,65382,65383,65384,65385,65386,65387,65388,65389,65390,65391,65392,65393,65394,65395,65396,65397,65398,65399,65400,65401,65402,65403,65404,65405,65406,65407,65408,65409,65410,65411,65412,65413,65414,65415,65416,65417,65418,65419,65420,65421,65422,65423,65424,65425,65426,65427,65428,65429,65430,65431,65432,65433,65434,65435,65436,65437,65438,65439,65440,65441,65442,65443,65444,65445,65446,65447,65448,65449,65450,65451,65452,65453,65454,65455,65456,65457,65458,65459,65460,65461,65462,65463,65464,65465,65466,65467,65468,65469,65470,65474,65475,65476,65477,65478,65479,65482,65483,65484,65485,65486,65487,65490,65491,65492,65493,65494,65495,65498,65499,65500'; +var arr = str.split(',').map(function(code) { + return parseInt(code, 10); +}); +module.exports = arr; },{}],4:[function(require,module,exports){ // http://wiki.commonjs.org/wiki/Unit_Testing/1.0 // @@ -50500,10 +461,8 @@ EventEmitter.prototype.emit = function(type) { er = arguments[1]; if (er instanceof Error) { throw er; // Unhandled 'error' event - } else { - throw TypeError('Uncaught, unspecified "error" event.'); } - return false; + throw TypeError('Uncaught, unspecified "error" event.'); } } @@ -50588,7 +547,10 @@ EventEmitter.prototype.addListener = function(type, listener) { 'leak detected. %d listeners added. ' + 'Use emitter.setMaxListeners() to increase limit.', this._events[type].length); - console.trace(); + if (typeof console.trace === 'function') { + // not supported in IE 10 + console.trace(); + } } } @@ -50775,6 +737,8 @@ var process = module.exports = {}; process.nextTick = (function () { var canSetImmediate = typeof window !== 'undefined' && window.setImmediate; + var canMutationObserver = typeof window !== 'undefined' + && window.MutationObserver; var canPost = typeof window !== 'undefined' && window.postMessage && window.addEventListener ; @@ -50783,10 +747,32 @@ process.nextTick = (function () { return function (f) { return window.setImmediate(f) }; } + var queue = []; + + if (canMutationObserver) { + var hiddenDiv = document.createElement("div"); + var observer = new MutationObserver(function () { + var queueList = queue.slice(); + queue.length = 0; + queueList.forEach(function (fn) { + fn(); + }); + }); + + observer.observe(hiddenDiv, { attributes: true }); + + return function nextTick(fn) { + if (!queue.length) { + hiddenDiv.setAttribute('yes', 'no'); + } + queue.push(fn); + }; + } + if (canPost) { - var queue = []; window.addEventListener('message', function (ev) { - if (ev.source === window && ev.data === 'process-tick') { + var source = ev.source; + if ((source === window || source === null) && ev.data === 'process-tick') { ev.stopPropagation(); if (queue.length > 0) { var fn = queue.shift(); @@ -50811,9 +797,19 @@ process.browser = true; process.env = {}; process.argv = []; +function noop() {} + +process.on = noop; +process.addListener = noop; +process.once = noop; +process.off = noop; +process.removeListener = noop; +process.removeAllListeners = noop; +process.emit = noop; + process.binding = function (name) { throw new Error('process.binding is not supported'); -} +}; // TODO(shtylman) process.cwd = function () { return '/' }; @@ -50829,7 +825,8 @@ module.exports = function isBuffer(arg) { && typeof arg.readUInt8 === 'function'; } },{}],9:[function(require,module,exports){ -var process=require("__browserify_process"),global=typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {};// Copyright Joyent, Inc. and other Node contributors. +(function (process,global){ +// Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the @@ -51416,10 +1413,13 @@ function hasOwnProperty(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); } -},{"./support/isBuffer":8,"__browserify_process":7,"inherits":6}],10:[function(require,module,exports){ -var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {};/*global window, global*/ +}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./support/isBuffer":8,"_process":7,"inherits":6}],10:[function(require,module,exports){ +(function (global){ +/*global window, global*/ var util = require("util") var assert = require("assert") +var now = require("date-now") var slice = Array.prototype.slice var console @@ -51430,19 +1430,19 @@ if (typeof global !== "undefined" && global.console) { } else if (typeof window !== "undefined" && window.console) { console = window.console } else { - console = window.console = {} + console = {} } var functions = [ - [log, "log"] - , [info, "info"] - , [warn, "warn"] - , [error, "error"] - , [time, "time"] - , [timeEnd, "timeEnd"] - , [trace, "trace"] - , [dir, "dir"] - , [assert, "assert"] + [log, "log"], + [info, "info"], + [warn, "warn"], + [error, "error"], + [time, "time"], + [timeEnd, "timeEnd"], + [trace, "trace"], + [dir, "dir"], + [consoleAssert, "assert"] ] for (var i = 0; i < functions.length; i++) { @@ -51472,7 +1472,7 @@ function error() { } function time(label) { - times[label] = Date.now() + times[label] = now() } function timeEnd(label) { @@ -51481,7 +1481,7 @@ function timeEnd(label) { throw new Error("No such label: " + label) } - var duration = Date.now() - time + var duration = now() - time console.log(label + ": " + duration + "ms") } @@ -51496,17 +1496,25 @@ function dir(object) { console.log(util.inspect(object) + "\n") } -function assert(expression) { +function consoleAssert(expression) { if (!expression) { var arr = slice.call(arguments, 1) assert.ok(false, util.format.apply(null, arr)) } } -},{"assert":4,"util":9}],11:[function(require,module,exports){ -// Underscore.js 1.4.4 +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"assert":4,"date-now":11,"util":9}],11:[function(require,module,exports){ +module.exports = now + +function now() { + return new Date().getTime() +} + +},{}],12:[function(require,module,exports){ +// Underscore.js 1.6.0 // http://underscorejs.org -// (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc. +// (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors // Underscore may be freely distributed under the MIT license. (function() { @@ -51514,7 +1522,7 @@ function assert(expression) { // Baseline setup // -------------- - // Establish the root object, `window` in the browser, or `global` on the server. + // Establish the root object, `window` in the browser, or `exports` on the server. var root = this; // Save the previous value of the `_` variable. @@ -51527,11 +1535,12 @@ function assert(expression) { var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; // Create quick reference variables for speed access to core prototypes. - var push = ArrayProto.push, - slice = ArrayProto.slice, - concat = ArrayProto.concat, - toString = ObjProto.toString, - hasOwnProperty = ObjProto.hasOwnProperty; + var + push = ArrayProto.push, + slice = ArrayProto.slice, + concat = ArrayProto.concat, + toString = ObjProto.toString, + hasOwnProperty = ObjProto.hasOwnProperty; // All **ECMAScript 5** native function implementations that we hope to use // are declared here. @@ -51570,7 +1579,7 @@ function assert(expression) { } // Current version. - _.VERSION = '1.4.4'; + _.VERSION = '1.6.0'; // Collection Functions // -------------------- @@ -51579,20 +1588,20 @@ function assert(expression) { // Handles objects with the built-in `forEach`, arrays, and raw objects. // Delegates to **ECMAScript 5**'s native `forEach` if available. var each = _.each = _.forEach = function(obj, iterator, context) { - if (obj == null) return; + if (obj == null) return obj; if (nativeForEach && obj.forEach === nativeForEach) { obj.forEach(iterator, context); } else if (obj.length === +obj.length) { - for (var i = 0, l = obj.length; i < l; i++) { + for (var i = 0, length = obj.length; i < length; i++) { if (iterator.call(context, obj[i], i, obj) === breaker) return; } } else { - for (var key in obj) { - if (_.has(obj, key)) { - if (iterator.call(context, obj[key], key, obj) === breaker) return; - } + var keys = _.keys(obj); + for (var i = 0, length = keys.length; i < length; i++) { + if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return; } } + return obj; }; // Return the results of applying the iterator to each element. @@ -51602,7 +1611,7 @@ function assert(expression) { if (obj == null) return results; if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); each(obj, function(value, index, list) { - results[results.length] = iterator.call(context, value, index, list); + results.push(iterator.call(context, value, index, list)); }); return results; }; @@ -51658,10 +1667,10 @@ function assert(expression) { }; // Return the first value which passes a truth test. Aliased as `detect`. - _.find = _.detect = function(obj, iterator, context) { + _.find = _.detect = function(obj, predicate, context) { var result; any(obj, function(value, index, list) { - if (iterator.call(context, value, index, list)) { + if (predicate.call(context, value, index, list)) { result = value; return true; } @@ -51672,33 +1681,33 @@ function assert(expression) { // Return all the elements that pass a truth test. // Delegates to **ECMAScript 5**'s native `filter` if available. // Aliased as `select`. - _.filter = _.select = function(obj, iterator, context) { + _.filter = _.select = function(obj, predicate, context) { var results = []; if (obj == null) return results; - if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); + if (nativeFilter && obj.filter === nativeFilter) return obj.filter(predicate, context); each(obj, function(value, index, list) { - if (iterator.call(context, value, index, list)) results[results.length] = value; + if (predicate.call(context, value, index, list)) results.push(value); }); return results; }; // Return all the elements for which a truth test fails. - _.reject = function(obj, iterator, context) { + _.reject = function(obj, predicate, context) { return _.filter(obj, function(value, index, list) { - return !iterator.call(context, value, index, list); + return !predicate.call(context, value, index, list); }, context); }; // Determine whether all of the elements match a truth test. // Delegates to **ECMAScript 5**'s native `every` if available. // Aliased as `all`. - _.every = _.all = function(obj, iterator, context) { - iterator || (iterator = _.identity); + _.every = _.all = function(obj, predicate, context) { + predicate || (predicate = _.identity); var result = true; if (obj == null) return result; - if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); + if (nativeEvery && obj.every === nativeEvery) return obj.every(predicate, context); each(obj, function(value, index, list) { - if (!(result = result && iterator.call(context, value, index, list))) return breaker; + if (!(result = result && predicate.call(context, value, index, list))) return breaker; }); return !!result; }; @@ -51706,13 +1715,13 @@ function assert(expression) { // Determine if at least one element in the object matches a truth test. // Delegates to **ECMAScript 5**'s native `some` if available. // Aliased as `any`. - var any = _.some = _.any = function(obj, iterator, context) { - iterator || (iterator = _.identity); + var any = _.some = _.any = function(obj, predicate, context) { + predicate || (predicate = _.identity); var result = false; if (obj == null) return result; - if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); + if (nativeSome && obj.some === nativeSome) return obj.some(predicate, context); each(obj, function(value, index, list) { - if (result || (result = iterator.call(context, value, index, list))) return breaker; + if (result || (result = predicate.call(context, value, index, list))) return breaker; }); return !!result; }; @@ -51738,41 +1747,37 @@ function assert(expression) { // Convenience version of a common use case of `map`: fetching a property. _.pluck = function(obj, key) { - return _.map(obj, function(value){ return value[key]; }); + return _.map(obj, _.property(key)); }; // Convenience version of a common use case of `filter`: selecting only objects // containing specific `key:value` pairs. - _.where = function(obj, attrs, first) { - if (_.isEmpty(attrs)) return first ? null : []; - return _[first ? 'find' : 'filter'](obj, function(value) { - for (var key in attrs) { - if (attrs[key] !== value[key]) return false; - } - return true; - }); + _.where = function(obj, attrs) { + return _.filter(obj, _.matches(attrs)); }; // Convenience version of a common use case of `find`: getting the first object // containing specific `key:value` pairs. _.findWhere = function(obj, attrs) { - return _.where(obj, attrs, true); + return _.find(obj, _.matches(attrs)); }; // Return the maximum element or (element-based computation). // Can't optimize arrays of integers longer than 65,535 elements. - // See: https://bugs.webkit.org/show_bug.cgi?id=80797 + // See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797) _.max = function(obj, iterator, context) { if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { return Math.max.apply(Math, obj); } - if (!iterator && _.isEmpty(obj)) return -Infinity; - var result = {computed : -Infinity, value: -Infinity}; + var result = -Infinity, lastComputed = -Infinity; each(obj, function(value, index, list) { var computed = iterator ? iterator.call(context, value, index, list) : value; - computed >= result.computed && (result = {value : value, computed : computed}); + if (computed > lastComputed) { + result = value; + lastComputed = computed; + } }); - return result.value; + return result; }; // Return the minimum element (or element-based computation). @@ -51780,16 +1785,19 @@ function assert(expression) { if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { return Math.min.apply(Math, obj); } - if (!iterator && _.isEmpty(obj)) return Infinity; - var result = {computed : Infinity, value: Infinity}; + var result = Infinity, lastComputed = Infinity; each(obj, function(value, index, list) { var computed = iterator ? iterator.call(context, value, index, list) : value; - computed < result.computed && (result = {value : value, computed : computed}); + if (computed < lastComputed) { + result = value; + lastComputed = computed; + } }); - return result.value; + return result; }; - // Shuffle an array. + // Shuffle an array, using the modern version of the + // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle). _.shuffle = function(obj) { var rand; var index = 0; @@ -51802,19 +1810,32 @@ function assert(expression) { return shuffled; }; + // Sample **n** random values from a collection. + // If **n** is not specified, returns a single random element. + // The internal `guard` argument allows it to work with `map`. + _.sample = function(obj, n, guard) { + if (n == null || guard) { + if (obj.length !== +obj.length) obj = _.values(obj); + return obj[_.random(obj.length - 1)]; + } + return _.shuffle(obj).slice(0, Math.max(0, n)); + }; + // An internal function to generate lookup iterators. var lookupIterator = function(value) { - return _.isFunction(value) ? value : function(obj){ return obj[value]; }; + if (value == null) return _.identity; + if (_.isFunction(value)) return value; + return _.property(value); }; // Sort the object's values by a criterion produced by an iterator. - _.sortBy = function(obj, value, context) { - var iterator = lookupIterator(value); + _.sortBy = function(obj, iterator, context) { + iterator = lookupIterator(iterator); return _.pluck(_.map(obj, function(value, index, list) { return { - value : value, - index : index, - criteria : iterator.call(context, value, index, list) + value: value, + index: index, + criteria: iterator.call(context, value, index, list) }; }).sort(function(left, right) { var a = left.criteria; @@ -51823,43 +1844,46 @@ function assert(expression) { if (a > b || a === void 0) return 1; if (a < b || b === void 0) return -1; } - return left.index < right.index ? -1 : 1; + return left.index - right.index; }), 'value'); }; // An internal function used for aggregate "group by" operations. - var group = function(obj, value, context, behavior) { - var result = {}; - var iterator = lookupIterator(value || _.identity); - each(obj, function(value, index) { - var key = iterator.call(context, value, index, obj); - behavior(result, key, value); - }); - return result; + var group = function(behavior) { + return function(obj, iterator, context) { + var result = {}; + iterator = lookupIterator(iterator); + each(obj, function(value, index) { + var key = iterator.call(context, value, index, obj); + behavior(result, key, value); + }); + return result; + }; }; // Groups the object's values by a criterion. Pass either a string attribute // to group by, or a function that returns the criterion. - _.groupBy = function(obj, value, context) { - return group(obj, value, context, function(result, key, value) { - (_.has(result, key) ? result[key] : (result[key] = [])).push(value); - }); - }; + _.groupBy = group(function(result, key, value) { + _.has(result, key) ? result[key].push(value) : result[key] = [value]; + }); + + // Indexes the object's values by a criterion, similar to `groupBy`, but for + // when you know that your index values will be unique. + _.indexBy = group(function(result, key, value) { + result[key] = value; + }); // Counts instances of an object that group by a certain criterion. Pass // either a string attribute to count by, or a function that returns the // criterion. - _.countBy = function(obj, value, context) { - return group(obj, value, context, function(result, key) { - if (!_.has(result, key)) result[key] = 0; - result[key]++; - }); - }; + _.countBy = group(function(result, key) { + _.has(result, key) ? result[key]++ : result[key] = 1; + }); // Use a comparator function to figure out the smallest index at which // an object should be inserted so as to maintain order. Uses binary search. _.sortedIndex = function(array, obj, iterator, context) { - iterator = iterator == null ? _.identity : lookupIterator(iterator); + iterator = lookupIterator(iterator); var value = iterator.call(context, obj); var low = 0, high = array.length; while (low < high) { @@ -51869,7 +1893,7 @@ function assert(expression) { return low; }; - // Safely convert anything iterable into a real, live array. + // Safely create a real, live array from anything iterable. _.toArray = function(obj) { if (!obj) return []; if (_.isArray(obj)) return slice.call(obj); @@ -51891,7 +1915,9 @@ function assert(expression) { // allows it to work with `_.map`. _.first = _.head = _.take = function(array, n, guard) { if (array == null) return void 0; - return (n != null) && !guard ? slice.call(array, 0, n) : array[0]; + if ((n == null) || guard) return array[0]; + if (n < 0) return []; + return slice.call(array, 0, n); }; // Returns everything but the last entry of the array. Especially useful on @@ -51906,11 +1932,8 @@ function assert(expression) { // values in the array. The **guard** check allows it to work with `_.map`. _.last = function(array, n, guard) { if (array == null) return void 0; - if ((n != null) && !guard) { - return slice.call(array, Math.max(array.length - n, 0)); - } else { - return array[array.length - 1]; - } + if ((n == null) || guard) return array[array.length - 1]; + return slice.call(array, Math.max(array.length - n, 0)); }; // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. @@ -51928,8 +1951,11 @@ function assert(expression) { // Internal implementation of a recursive `flatten` function. var flatten = function(input, shallow, output) { + if (shallow && _.every(input, _.isArray)) { + return concat.apply(output, input); + } each(input, function(value) { - if (_.isArray(value)) { + if (_.isArray(value) || _.isArguments(value)) { shallow ? push.apply(output, value) : flatten(value, shallow, output); } else { output.push(value); @@ -51938,7 +1964,7 @@ function assert(expression) { return output; }; - // Return a completely flattened version of an array. + // Flatten out an array, either recursively (by default), or just one level. _.flatten = function(array, shallow) { return flatten(array, shallow, []); }; @@ -51948,6 +1974,16 @@ function assert(expression) { return _.difference(array, slice.call(arguments, 1)); }; + // Split an array into two arrays: one whose elements all satisfy the given + // predicate, and one whose elements all do not satisfy the predicate. + _.partition = function(array, predicate) { + var pass = [], fail = []; + each(array, function(elem) { + (predicate(elem) ? pass : fail).push(elem); + }); + return [pass, fail]; + }; + // Produce a duplicate-free version of the array. If the array has already // been sorted, you have the option of using a faster algorithm. // Aliased as `unique`. @@ -51972,7 +2008,7 @@ function assert(expression) { // Produce an array that contains the union: each distinct element from all of // the passed-in arrays. _.union = function() { - return _.uniq(concat.apply(ArrayProto, arguments)); + return _.uniq(_.flatten(arguments, true)); }; // Produce an array that contains every item shared between all the @@ -51981,7 +2017,7 @@ function assert(expression) { var rest = slice.call(arguments, 1); return _.filter(_.uniq(array), function(item) { return _.every(rest, function(other) { - return _.indexOf(other, item) >= 0; + return _.contains(other, item); }); }); }; @@ -51996,11 +2032,10 @@ function assert(expression) { // Zip together multiple lists into a single array -- elements that share // an index go together. _.zip = function() { - var args = slice.call(arguments); - var length = _.max(_.pluck(args, 'length')); + var length = _.max(_.pluck(arguments, 'length').concat(0)); var results = new Array(length); for (var i = 0; i < length; i++) { - results[i] = _.pluck(args, "" + i); + results[i] = _.pluck(arguments, '' + i); } return results; }; @@ -52011,7 +2046,7 @@ function assert(expression) { _.object = function(list, values) { if (list == null) return {}; var result = {}; - for (var i = 0, l = list.length; i < l; i++) { + for (var i = 0, length = list.length; i < length; i++) { if (values) { result[list[i]] = values[i]; } else { @@ -52029,17 +2064,17 @@ function assert(expression) { // for **isSorted** to use binary search. _.indexOf = function(array, item, isSorted) { if (array == null) return -1; - var i = 0, l = array.length; + var i = 0, length = array.length; if (isSorted) { if (typeof isSorted == 'number') { - i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted); + i = (isSorted < 0 ? Math.max(0, length + isSorted) : isSorted); } else { i = _.sortedIndex(array, item); return array[i] === item ? i : -1; } } if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted); - for (; i < l; i++) if (array[i] === item) return i; + for (; i < length; i++) if (array[i] === item) return i; return -1; }; @@ -52065,11 +2100,11 @@ function assert(expression) { } step = arguments[2] || 1; - var len = Math.max(Math.ceil((stop - start) / step), 0); + var length = Math.max(Math.ceil((stop - start) / step), 0); var idx = 0; - var range = new Array(len); + var range = new Array(length); - while(idx < len) { + while(idx < length) { range[idx++] = start; start += step; } @@ -52080,31 +2115,50 @@ function assert(expression) { // Function (ahem) Functions // ------------------ + // Reusable constructor function for prototype setting. + var ctor = function(){}; + // Create a function bound to a given object (assigning `this`, and arguments, // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if // available. _.bind = function(func, context) { - if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); - var args = slice.call(arguments, 2); - return function() { - return func.apply(context, args.concat(slice.call(arguments))); + var args, bound; + if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); + if (!_.isFunction(func)) throw new TypeError; + args = slice.call(arguments, 2); + return bound = function() { + if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments))); + ctor.prototype = func.prototype; + var self = new ctor; + ctor.prototype = null; + var result = func.apply(self, args.concat(slice.call(arguments))); + if (Object(result) === result) return result; + return self; }; }; // Partially apply a function by creating a version that has had some of its - // arguments pre-filled, without changing its dynamic `this` context. + // arguments pre-filled, without changing its dynamic `this` context. _ acts + // as a placeholder, allowing any combination of arguments to be pre-filled. _.partial = function(func) { - var args = slice.call(arguments, 1); + var boundArgs = slice.call(arguments, 1); return function() { - return func.apply(this, args.concat(slice.call(arguments))); + var position = 0; + var args = boundArgs.slice(); + for (var i = 0, length = args.length; i < length; i++) { + if (args[i] === _) args[i] = arguments[position++]; + } + while (position < arguments.length) args.push(arguments[position++]); + return func.apply(this, args); }; }; - // Bind all of an object's methods to that object. Useful for ensuring that - // all callbacks defined on an object belong to it. + // Bind a number of an object's methods to that object. Remaining arguments + // are the method names to be bound. Useful for ensuring that all callbacks + // defined on an object belong to it. _.bindAll = function(obj) { var funcs = slice.call(arguments, 1); - if (funcs.length === 0) funcs = _.functions(obj); + if (funcs.length === 0) throw new Error('bindAll must be passed function names'); each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); return obj; }; @@ -52133,17 +2187,24 @@ function assert(expression) { }; // Returns a function, that, when invoked, will only be triggered at most once - // during a given window of time. - _.throttle = function(func, wait) { - var context, args, timeout, result; + // during a given window of time. Normally, the throttled function will run + // as much as it can, without ever going more than once per `wait` duration; + // but if you'd like to disable the execution on the leading edge, pass + // `{leading: false}`. To disable execution on the trailing edge, ditto. + _.throttle = function(func, wait, options) { + var context, args, result; + var timeout = null; var previous = 0; + options || (options = {}); var later = function() { - previous = new Date; + previous = options.leading === false ? 0 : _.now(); timeout = null; result = func.apply(context, args); + context = args = null; }; return function() { - var now = new Date; + var now = _.now(); + if (!previous && options.leading === false) previous = now; var remaining = wait - (now - previous); context = this; args = arguments; @@ -52152,7 +2213,8 @@ function assert(expression) { timeout = null; previous = now; result = func.apply(context, args); - } else if (!timeout) { + context = args = null; + } else if (!timeout && options.trailing !== false) { timeout = setTimeout(later, remaining); } return result; @@ -52164,17 +2226,34 @@ function assert(expression) { // N milliseconds. If `immediate` is passed, trigger the function on the // leading edge, instead of the trailing. _.debounce = function(func, wait, immediate) { - var timeout, result; - return function() { - var context = this, args = arguments; - var later = function() { + var timeout, args, context, timestamp, result; + + var later = function() { + var last = _.now() - timestamp; + if (last < wait) { + timeout = setTimeout(later, wait - last); + } else { timeout = null; - if (!immediate) result = func.apply(context, args); - }; + if (!immediate) { + result = func.apply(context, args); + context = args = null; + } + } + }; + + return function() { + context = this; + args = arguments; + timestamp = _.now(); var callNow = immediate && !timeout; - clearTimeout(timeout); - timeout = setTimeout(later, wait); - if (callNow) result = func.apply(context, args); + if (!timeout) { + timeout = setTimeout(later, wait); + } + if (callNow) { + result = func.apply(context, args); + context = args = null; + } + return result; }; }; @@ -52196,11 +2275,7 @@ function assert(expression) { // allowing you to adjust arguments, run code before and after, and // conditionally execute the original function. _.wrap = function(func, wrapper) { - return function() { - var args = [func]; - push.apply(args, arguments); - return wrapper.apply(this, args); - }; + return _.partial(wrapper, func); }; // Returns a function that is the composition of a list of functions, each @@ -52218,7 +2293,6 @@ function assert(expression) { // Returns a function that will only be executed after being called N times. _.after = function(times, func) { - if (times <= 0) return func(); return function() { if (--times < 1) { return func.apply(this, arguments); @@ -52231,31 +2305,43 @@ function assert(expression) { // Retrieve the names of an object's properties. // Delegates to **ECMAScript 5**'s native `Object.keys` - _.keys = nativeKeys || function(obj) { - if (obj !== Object(obj)) throw new TypeError('Invalid object'); + _.keys = function(obj) { + if (!_.isObject(obj)) return []; + if (nativeKeys) return nativeKeys(obj); var keys = []; - for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key; + for (var key in obj) if (_.has(obj, key)) keys.push(key); return keys; }; // Retrieve the values of an object's properties. _.values = function(obj) { - var values = []; - for (var key in obj) if (_.has(obj, key)) values.push(obj[key]); + var keys = _.keys(obj); + var length = keys.length; + var values = new Array(length); + for (var i = 0; i < length; i++) { + values[i] = obj[keys[i]]; + } return values; }; // Convert an object into a list of `[key, value]` pairs. _.pairs = function(obj) { - var pairs = []; - for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]); + var keys = _.keys(obj); + var length = keys.length; + var pairs = new Array(length); + for (var i = 0; i < length; i++) { + pairs[i] = [keys[i], obj[keys[i]]]; + } return pairs; }; // Invert the keys and values of an object. The values must be serializable. _.invert = function(obj) { var result = {}; - for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key; + var keys = _.keys(obj); + for (var i = 0, length = keys.length; i < length; i++) { + result[obj[keys[i]]] = keys[i]; + } return result; }; @@ -52306,7 +2392,7 @@ function assert(expression) { each(slice.call(arguments, 1), function(source) { if (source) { for (var prop in source) { - if (obj[prop] == null) obj[prop] = source[prop]; + if (obj[prop] === void 0) obj[prop] = source[prop]; } } }); @@ -52330,7 +2416,7 @@ function assert(expression) { // Internal recursive comparison function for `isEqual`. var eq = function(a, b, aStack, bStack) { // Identical objects are equal. `0 === -0`, but they aren't identical. - // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal. + // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). if (a === b) return a !== 0 || 1 / a == 1 / b; // A strict comparison is necessary because `null == undefined`. if (a == null || b == null) return a === b; @@ -52372,6 +2458,14 @@ function assert(expression) { // unique nested structures. if (aStack[length] == a) return bStack[length] == b; } + // Objects with different constructors are not equivalent, but `Object`s + // from different frames are. + var aCtor = a.constructor, bCtor = b.constructor; + if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) && + _.isFunction(bCtor) && (bCtor instanceof bCtor)) + && ('constructor' in a && 'constructor' in b)) { + return false; + } // Add the first object to the stack of traversed objects. aStack.push(a); bStack.push(b); @@ -52388,13 +2482,6 @@ function assert(expression) { } } } else { - // Objects with different constructors are not equivalent, but `Object`s - // from different frames are. - var aCtor = a.constructor, bCtor = b.constructor; - if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) && - _.isFunction(bCtor) && (bCtor instanceof bCtor))) { - return false; - } // Deep compare objects. for (var key in a) { if (_.has(a, key)) { @@ -52516,9 +2603,33 @@ function assert(expression) { return value; }; + _.constant = function(value) { + return function () { + return value; + }; + }; + + _.property = function(key) { + return function(obj) { + return obj[key]; + }; + }; + + // Returns a predicate for checking whether an object has a given set of `key:value` pairs. + _.matches = function(attrs) { + return function(obj) { + if (obj === attrs) return true; //avoid comparing an object to itself. + for (var key in attrs) { + if (attrs[key] !== obj[key]) + return false; + } + return true; + } + }; + // Run a function **n** times. _.times = function(n, iterator, context) { - var accum = Array(n); + var accum = Array(Math.max(0, n)); for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i); return accum; }; @@ -52532,6 +2643,9 @@ function assert(expression) { return min + Math.floor(Math.random() * (max - min + 1)); }; + // A (possibly faster) way to get the current timestamp as an integer. + _.now = Date.now || function() { return new Date().getTime(); }; + // List of HTML entities for escaping. var entityMap = { escape: { @@ -52539,8 +2653,7 @@ function assert(expression) { '<': '<', '>': '>', '"': '"', - "'": ''', - '/': '/' + "'": ''' } }; entityMap.unescape = _.invert(entityMap.escape); @@ -52561,17 +2674,17 @@ function assert(expression) { }; }); - // If the value of the named property is a function then invoke it; - // otherwise, return it. + // If the value of the named `property` is a function then invoke it with the + // `object` as context; otherwise, return it. _.result = function(object, property) { - if (object == null) return null; + if (object == null) return void 0; var value = object[property]; return _.isFunction(value) ? value.call(object) : value; }; // Add your own custom functions to the Underscore object. _.mixin = function(obj) { - each(_.functions(obj), function(name){ + each(_.functions(obj), function(name) { var func = _[name] = obj[name]; _.prototype[name] = function() { var args = [this._wrapped]; @@ -52729,9 +2842,3881 @@ function assert(expression) { }); + // AMD registration happens at the end for compatibility with AMD loaders + // that may not enforce next-turn semantics on modules. Even though general + // practice for AMD registration is to be anonymous, underscore registers + // as a named module because, like jQuery, it is a base library that is + // popular enough to be bundled in a third party lib, but not be part of + // an AMD load request. Those cases could generate an error when an + // anonymous define() is called outside of a loader request. + if (typeof define === 'function' && define.amd) { + define('underscore', [], function() { + return _; + }); + } }).call(this); -},{}],"FcDwKk":[function(require,module,exports){ +},{}],13:[function(require,module,exports){ +/* + * Lexical analysis and token construction. + */ + +"use strict"; + +var _ = require("underscore"); +var events = require("events"); +var reg = require("./reg.js"); +var state = require("./state.js").state; + +var unicodeData = require("../data/ascii-identifier-data.js"); +var asciiIdentifierStartTable = unicodeData.asciiIdentifierStartTable; +var asciiIdentifierPartTable = unicodeData.asciiIdentifierPartTable; +var nonAsciiIdentifierStartTable = require("../data/non-ascii-identifier-start.js"); +var nonAsciiIdentifierPartTable = require("../data/non-ascii-identifier-part-only.js"); + +// Some of these token types are from JavaScript Parser API +// while others are specific to JSHint parser. +// JS Parser API: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API + +var Token = { + Identifier: 1, + Punctuator: 2, + NumericLiteral: 3, + StringLiteral: 4, + Comment: 5, + Keyword: 6, + NullLiteral: 7, + BooleanLiteral: 8, + RegExp: 9, + TemplateHead: 10, + TemplateMiddle: 11, + TemplateTail: 12 +}; + +// Object that handles postponed lexing verifications that checks the parsed +// environment state. + +function asyncTrigger() { + var _checks = []; + + return { + push: function(fn) { + _checks.push(fn); + }, + + check: function() { + for (var check = 0; check < _checks.length; ++check) { + _checks[check](); + } + + _checks.splice(0, _checks.length); + } + }; +} + +/* + * Lexer for JSHint. + * + * This object does a char-by-char scan of the provided source code + * and produces a sequence of tokens. + * + * var lex = new Lexer("var i = 0;"); + * lex.start(); + * lex.token(); // returns the next token + * + * You have to use the token() method to move the lexer forward + * but you don't have to use its return value to get tokens. In addition + * to token() method returning the next token, the Lexer object also + * emits events. + * + * lex.on("Identifier", function(data) { + * if (data.name.indexOf("_") >= 0) { + * // Produce a warning. + * } + * }); + * + * Note that the token() method returns tokens in a JSLint-compatible + * format while the event emitter uses a slightly modified version of + * Mozilla's JavaScript Parser API. Eventually, we will move away from + * JSLint format. + */ +function Lexer(source) { + var lines = source; + + if (typeof lines === "string") { + lines = lines + .replace(/\r\n/g, "\n") + .replace(/\r/g, "\n") + .split("\n"); + } + + // If the first line is a shebang (#!), make it a blank and move on. + // Shebangs are used by Node scripts. + + if (lines[0] && lines[0].substr(0, 2) === "#!") { + if (lines[0].indexOf("node") !== -1) { + state.option.node = true; + } + lines[0] = ""; + } + + this.emitter = new events.EventEmitter(); + this.source = source; + this.setLines(lines); + this.prereg = true; + + this.line = 0; + this.char = 1; + this.from = 1; + this.input = ""; + this.inComment = false; + this.inTemplate = false; + this.templateLine = null; + this.templateChar = null; + + for (var i = 0; i < state.option.indent; i += 1) { + state.tab += " "; + } +} + +Lexer.prototype = { + _lines: [], + + getLines: function() { + this._lines = state.lines; + return this._lines; + }, + + setLines: function(val) { + this._lines = val; + state.lines = this._lines; + }, + + /* + * Return the next i character without actually moving the + * char pointer. + */ + peek: function(i) { + return this.input.charAt(i || 0); + }, + + /* + * Move the char pointer forward i times. + */ + skip: function(i) { + i = i || 1; + this.char += i; + this.input = this.input.slice(i); + }, + + /* + * Subscribe to a token event. The API for this method is similar + * Underscore.js i.e. you can subscribe to multiple events with + * one call: + * + * lex.on("Identifier Number", function(data) { + * // ... + * }); + */ + on: function(names, listener) { + names.split(" ").forEach(function(name) { + this.emitter.on(name, listener); + }.bind(this)); + }, + + /* + * Trigger a token event. All arguments will be passed to each + * listener. + */ + trigger: function() { + this.emitter.emit.apply(this.emitter, Array.prototype.slice.call(arguments)); + }, + + /* + * Postpone a token event. the checking condition is set as + * last parameter, and the trigger function is called in a + * stored callback. To be later called using the check() function + * by the parser. This avoids parser's peek() to give the lexer + * a false context. + */ + triggerAsync: function(type, args, checks, fn) { + checks.push(function() { + if (fn()) { + this.trigger(type, args); + } + }.bind(this)); + }, + + /* + * Extract a punctuator out of the next sequence of characters + * or return 'null' if its not possible. + * + * This method's implementation was heavily influenced by the + * scanPunctuator function in the Esprima parser's source code. + */ + scanPunctuator: function() { + var ch1 = this.peek(); + var ch2, ch3, ch4; + + switch (ch1) { + // Most common single-character punctuators + case ".": + if ((/^[0-9]$/).test(this.peek(1))) { + return null; + } + if (this.peek(1) === "." && this.peek(2) === ".") { + return { + type: Token.Punctuator, + value: "..." + }; + } + /* falls through */ + case "(": + case ")": + case ";": + case ",": + case "{": + case "}": + case "[": + case "]": + case ":": + case "~": + case "?": + return { + type: Token.Punctuator, + value: ch1 + }; + + // A pound sign (for Node shebangs) + case "#": + return { + type: Token.Punctuator, + value: ch1 + }; + + // We're at the end of input + case "": + return null; + } + + // Peek more characters + + ch2 = this.peek(1); + ch3 = this.peek(2); + ch4 = this.peek(3); + + // 4-character punctuator: >>>= + + if (ch1 === ">" && ch2 === ">" && ch3 === ">" && ch4 === "=") { + return { + type: Token.Punctuator, + value: ">>>=" + }; + } + + // 3-character punctuators: === !== >>> <<= >>= + + if (ch1 === "=" && ch2 === "=" && ch3 === "=") { + return { + type: Token.Punctuator, + value: "===" + }; + } + + if (ch1 === "!" && ch2 === "=" && ch3 === "=") { + return { + type: Token.Punctuator, + value: "!==" + }; + } + + if (ch1 === ">" && ch2 === ">" && ch3 === ">") { + return { + type: Token.Punctuator, + value: ">>>" + }; + } + + if (ch1 === "<" && ch2 === "<" && ch3 === "=") { + return { + type: Token.Punctuator, + value: "<<=" + }; + } + + if (ch1 === ">" && ch2 === ">" && ch3 === "=") { + return { + type: Token.Punctuator, + value: ">>=" + }; + } + + // Fat arrow punctuator + if (ch1 === "=" && ch2 === ">") { + return { + type: Token.Punctuator, + value: ch1 + ch2 + }; + } + + // 2-character punctuators: <= >= == != ++ -- << >> && || + // += -= *= %= &= |= ^= (but not /=, see below) + if (ch1 === ch2 && ("+-<>&|".indexOf(ch1) >= 0)) { + return { + type: Token.Punctuator, + value: ch1 + ch2 + }; + } + + if ("<>=!+-*%&|^".indexOf(ch1) >= 0) { + if (ch2 === "=") { + return { + type: Token.Punctuator, + value: ch1 + ch2 + }; + } + + return { + type: Token.Punctuator, + value: ch1 + }; + } + + // Special case: /=. + + if (ch1 === "/") { + if (ch2 === "=") { + return { + type: Token.Punctuator, + value: "/=" + }; + } + + return { + type: Token.Punctuator, + value: "/" + }; + } + + return null; + }, + + /* + * Extract a comment out of the next sequence of characters and/or + * lines or return 'null' if its not possible. Since comments can + * span across multiple lines this method has to move the char + * pointer. + * + * In addition to normal JavaScript comments (// and /*) this method + * also recognizes JSHint- and JSLint-specific comments such as + * /*jshint, /*jslint, /*globals and so on. + */ + scanComments: function() { + var ch1 = this.peek(); + var ch2 = this.peek(1); + var rest = this.input.substr(2); + var startLine = this.line; + var startChar = this.char; + + // Create a comment token object and make sure it + // has all the data JSHint needs to work with special + // comments. + + function commentToken(label, body, opt) { + var special = ["jshint", "jslint", "members", "member", "globals", "global", "exported"]; + var isSpecial = false; + var value = label + body; + var commentType = "plain"; + opt = opt || {}; + + if (opt.isMultiline) { + value += "*/"; + } + + body = body.replace(/\n/g, " "); + + special.forEach(function(str) { + if (isSpecial) { + return; + } + + // Don't recognize any special comments other than jshint for single-line + // comments. This introduced many problems with legit comments. + if (label === "//" && str !== "jshint") { + return; + } + + if (body.charAt(str.length) === " " && body.substr(0, str.length) === str) { + isSpecial = true; + label = label + str; + body = body.substr(str.length); + } + + if (!isSpecial && body.charAt(0) === " " && body.charAt(str.length + 1) === " " && + body.substr(1, str.length) === str) { + isSpecial = true; + label = label + " " + str; + body = body.substr(str.length + 1); + } + + if (!isSpecial) { + return; + } + + switch (str) { + case "member": + commentType = "members"; + break; + case "global": + commentType = "globals"; + break; + default: + commentType = str; + } + }); + + return { + type: Token.Comment, + commentType: commentType, + value: value, + body: body, + isSpecial: isSpecial, + isMultiline: opt.isMultiline || false, + isMalformed: opt.isMalformed || false + }; + } + + // End of unbegun comment. Raise an error and skip that input. + if (ch1 === "*" && ch2 === "/") { + this.trigger("error", { + code: "E018", + line: startLine, + character: startChar + }); + + this.skip(2); + return null; + } + + // Comments must start either with // or /* + if (ch1 !== "/" || (ch2 !== "*" && ch2 !== "/")) { + return null; + } + + // One-line comment + if (ch2 === "/") { + this.skip(this.input.length); // Skip to the EOL. + return commentToken("//", rest); + } + + var body = ""; + + /* Multi-line comment */ + if (ch2 === "*") { + this.inComment = true; + this.skip(2); + + while (this.peek() !== "*" || this.peek(1) !== "/") { + if (this.peek() === "") { // End of Line + body += "\n"; + + // If we hit EOF and our comment is still unclosed, + // trigger an error and end the comment implicitly. + if (!this.nextLine()) { + this.trigger("error", { + code: "E017", + line: startLine, + character: startChar + }); + + this.inComment = false; + return commentToken("/*", body, { + isMultiline: true, + isMalformed: true + }); + } + } else { + body += this.peek(); + this.skip(); + } + } + + this.skip(2); + this.inComment = false; + return commentToken("/*", body, { isMultiline: true }); + } + }, + + /* + * Extract a keyword out of the next sequence of characters or + * return 'null' if its not possible. + */ + scanKeyword: function() { + var result = /^[a-zA-Z_$][a-zA-Z0-9_$]*/.exec(this.input); + var keywords = [ + "if", "in", "do", "var", "for", "new", + "try", "let", "this", "else", "case", + "void", "with", "enum", "while", "break", + "catch", "throw", "const", "yield", "class", + "super", "return", "typeof", "delete", + "switch", "export", "import", "default", + "finally", "extends", "function", "continue", + "debugger", "instanceof" + ]; + + if (result && keywords.indexOf(result[0]) >= 0) { + return { + type: Token.Keyword, + value: result[0] + }; + } + + return null; + }, + + /* + * Extract a JavaScript identifier out of the next sequence of + * characters or return 'null' if its not possible. In addition, + * to Identifier this method can also produce BooleanLiteral + * (true/false) and NullLiteral (null). + */ + scanIdentifier: function() { + var id = ""; + var index = 0; + var type, char; + + function isNonAsciiIdentifierStart(code) { + return nonAsciiIdentifierStartTable.indexOf(code) > -1; + } + + function isNonAsciiIdentifierPart(code) { + return isNonAsciiIdentifierStart(code) || nonAsciiIdentifierPartTable.indexOf(code) > -1; + } + + function isHexDigit(str) { + return (/^[0-9a-fA-F]$/).test(str); + } + + var readUnicodeEscapeSequence = function() { + /*jshint validthis:true */ + index += 1; + + if (this.peek(index) !== "u") { + return null; + } + + var ch1 = this.peek(index + 1); + var ch2 = this.peek(index + 2); + var ch3 = this.peek(index + 3); + var ch4 = this.peek(index + 4); + var code; + + if (isHexDigit(ch1) && isHexDigit(ch2) && isHexDigit(ch3) && isHexDigit(ch4)) { + code = parseInt(ch1 + ch2 + ch3 + ch4, 16); + + if (asciiIdentifierPartTable[code] || isNonAsciiIdentifierPart(code)) { + index += 5; + return "\\u" + ch1 + ch2 + ch3 + ch4; + } + + return null; + } + + return null; + }.bind(this); + + var getIdentifierStart = function() { + /*jshint validthis:true */ + var chr = this.peek(index); + var code = chr.charCodeAt(0); + + if (code === 92) { + return readUnicodeEscapeSequence(); + } + + if (code < 128) { + if (asciiIdentifierStartTable[code]) { + index += 1; + return chr; + } + + return null; + } + + if (isNonAsciiIdentifierStart(code)) { + index += 1; + return chr; + } + + return null; + }.bind(this); + + var getIdentifierPart = function() { + /*jshint validthis:true */ + var chr = this.peek(index); + var code = chr.charCodeAt(0); + + if (code === 92) { + return readUnicodeEscapeSequence(); + } + + if (code < 128) { + if (asciiIdentifierPartTable[code]) { + index += 1; + return chr; + } + + return null; + } + + if (isNonAsciiIdentifierPart(code)) { + index += 1; + return chr; + } + + return null; + }.bind(this); + + function removeEscapeSequences(id) { + return id.replace(/\\u([0-9a-fA-F]{4})/g, function(m0, codepoint) { + return String.fromCharCode(parseInt(codepoint, 16)); + }); + } + + char = getIdentifierStart(); + if (char === null) { + return null; + } + + id = char; + for (;;) { + char = getIdentifierPart(); + + if (char === null) { + break; + } + + id += char; + } + + switch (id) { + case "true": + case "false": + type = Token.BooleanLiteral; + break; + case "null": + type = Token.NullLiteral; + break; + default: + type = Token.Identifier; + } + + return { + type: type, + value: removeEscapeSequences(id), + text: id, + tokenLength: id.length + }; + }, + + /* + * Extract a numeric literal out of the next sequence of + * characters or return 'null' if its not possible. This method + * supports all numeric literals described in section 7.8.3 + * of the EcmaScript 5 specification. + * + * This method's implementation was heavily influenced by the + * scanNumericLiteral function in the Esprima parser's source code. + */ + scanNumericLiteral: function() { + var index = 0; + var value = ""; + var length = this.input.length; + var char = this.peek(index); + var bad; + var isAllowedDigit = isDecimalDigit; + var base = 10; + var isLegacy = false; + + function isDecimalDigit(str) { + return (/^[0-9]$/).test(str); + } + + function isOctalDigit(str) { + return (/^[0-7]$/).test(str); + } + + function isBinaryDigit(str) { + return (/^[01]$/).test(str); + } + + function isHexDigit(str) { + return (/^[0-9a-fA-F]$/).test(str); + } + + function isIdentifierStart(ch) { + return (ch === "$") || (ch === "_") || (ch === "\\") || + (ch >= "a" && ch <= "z") || (ch >= "A" && ch <= "Z"); + } + + // Numbers must start either with a decimal digit or a point. + + if (char !== "." && !isDecimalDigit(char)) { + return null; + } + + if (char !== ".") { + value = this.peek(index); + index += 1; + char = this.peek(index); + + if (value === "0") { + // Base-16 numbers. + if (char === "x" || char === "X") { + isAllowedDigit = isHexDigit; + base = 16; + + index += 1; + value += char; + } + + // Base-8 numbers. + if (char === "o" || char === "O") { + isAllowedDigit = isOctalDigit; + base = 8; + + if (!state.option.esnext) { + this.trigger("warning", { + code: "W119", + line: this.line, + character: this.char, + data: [ "Octal integer literal" ] + }); + } + + index += 1; + value += char; + } + + // Base-2 numbers. + if (char === "b" || char === "B") { + isAllowedDigit = isBinaryDigit; + base = 2; + + if (!state.option.esnext) { + this.trigger("warning", { + code: "W119", + line: this.line, + character: this.char, + data: [ "Binary integer literal" ] + }); + } + + index += 1; + value += char; + } + + // Legacy base-8 numbers. + if (isOctalDigit(char)) { + isAllowedDigit = isOctalDigit; + base = 8; + isLegacy = true; + bad = false; + + index += 1; + value += char; + } + + // Decimal numbers that start with '0' such as '09' are illegal + // but we still parse them and return as malformed. + + if (!isOctalDigit(char) && isDecimalDigit(char)) { + index += 1; + value += char; + } + } + + while (index < length) { + char = this.peek(index); + + if (isLegacy && isDecimalDigit(char)) { + // Numbers like '019' (note the 9) are not valid octals + // but we still parse them and mark as malformed. + bad = true; + } else if (!isAllowedDigit(char)) { + break; + } + value += char; + index += 1; + } + + if (isAllowedDigit !== isDecimalDigit) { + if (!isLegacy && value.length <= 2) { // 0x + return { + type: Token.NumericLiteral, + value: value, + isMalformed: true + }; + } + + if (index < length) { + char = this.peek(index); + if (isIdentifierStart(char)) { + return null; + } + } + + return { + type: Token.NumericLiteral, + value: value, + base: base, + isLegacy: isLegacy, + isMalformed: false + }; + } + } + + // Decimal digits. + + if (char === ".") { + value += char; + index += 1; + + while (index < length) { + char = this.peek(index); + if (!isDecimalDigit(char)) { + break; + } + value += char; + index += 1; + } + } + + // Exponent part. + + if (char === "e" || char === "E") { + value += char; + index += 1; + char = this.peek(index); + + if (char === "+" || char === "-") { + value += this.peek(index); + index += 1; + } + + char = this.peek(index); + if (isDecimalDigit(char)) { + value += char; + index += 1; + + while (index < length) { + char = this.peek(index); + if (!isDecimalDigit(char)) { + break; + } + value += char; + index += 1; + } + } else { + return null; + } + } + + if (index < length) { + char = this.peek(index); + if (isIdentifierStart(char)) { + return null; + } + } + + return { + type: Token.NumericLiteral, + value: value, + base: base, + isMalformed: !isFinite(value) + }; + }, + + + // Assumes previously parsed character was \ (=== '\\') and was not skipped. + scanEscapeSequence: function(checks) { + var allowNewLine = false; + var jump = 1; + this.skip(); + var char = this.peek(); + + switch (char) { + case "'": + this.triggerAsync("warning", { + code: "W114", + line: this.line, + character: this.char, + data: [ "\\'" ] + }, checks, function() {return state.jsonMode; }); + break; + case "b": + char = "\\b"; + break; + case "f": + char = "\\f"; + break; + case "n": + char = "\\n"; + break; + case "r": + char = "\\r"; + break; + case "t": + char = "\\t"; + break; + case "0": + char = "\\0"; + + // Octal literals fail in strict mode. + // Check if the number is between 00 and 07. + var n = parseInt(this.peek(1), 10); + this.triggerAsync("warning", { + code: "W115", + line: this.line, + character: this.char + }, checks, + function() { return n >= 0 && n <= 7 && state.directive["use strict"]; }); + break; + case "u": + char = String.fromCharCode(parseInt(this.input.substr(1, 4), 16)); + jump = 5; + break; + case "v": + this.triggerAsync("warning", { + code: "W114", + line: this.line, + character: this.char, + data: [ "\\v" ] + }, checks, function() { return state.jsonMode; }); + + char = "\v"; + break; + case "x": + var x = parseInt(this.input.substr(1, 2), 16); + + this.triggerAsync("warning", { + code: "W114", + line: this.line, + character: this.char, + data: [ "\\x-" ] + }, checks, function() { return state.jsonMode; }); + + char = String.fromCharCode(x); + jump = 3; + break; + case "\\": + char = "\\\\"; + break; + case "\"": + char = "\\\""; + break; + case "/": + break; + case "": + allowNewLine = true; + char = ""; + break; + } + + return { char: char, jump: jump, allowNewLine: allowNewLine }; + }, + + /* + * Extract a template literal out of the next sequence of characters + * and/or lines or return 'null' if its not possible. Since template + * literals can span across multiple lines, this method has to move + * the char pointer. + */ + scanTemplateLiteral: function(checks) { + var tokenType; + var value = ''; + var ch; + + // String must start with a backtick. + if (!this.inTemplate) { + if (!state.option.esnext || this.peek() !== "`") { + return null; + } + this.templateLine = this.line; + this.templateChar = this.char; + this.skip(1); + } else if (this.peek() !== '}') { + // If we're in a template, and we don't have a '}', lex something else instead. + return null; + } + + while (this.peek() !== "`") { + while ((ch = this.peek()) === "") { + if (!this.nextLine()) { + // Unclosed template literal --- point to the starting line, or the EOF? + tokenType = this.inTemplate ? Token.TemplateHead : Token.TemplateMiddle; + this.inTemplate = false; + this.trigger("error", { + code: "E052", + line: this.templateLine, + character: this.templateChar + }); + return { + type: tokenType, + value: value, + isUnclosed: true + }; + } + } + + if (ch === '$' && this.peek(1) === '{') { + value += '${'; + tokenType = value.charAt(0) === '}' ? Token.TemplateMiddle : Token.TemplateHead; + // Either TokenHead or TokenMiddle --- depending on if the initial value + // is '}' or not. + this.skip(2); + this.inTemplate = true; + return { + type: tokenType, + value: value, + isUnclosed: false + }; + } else if (ch === '\\') { + var escape = this.scanEscapeSequence(checks); + value += escape.char; + this.skip(escape.jump); + } else if (ch === '`') { + break; + } else { + // Otherwise, append the value and continue. + value += ch; + this.skip(1); + } + } + + // Final value is either TokenTail or NoSubstititionTemplate --- essentially a string + tokenType = this.inTemplate ? Token.TemplateTail : Token.StringLiteral; + this.inTemplate = false; + this.skip(1); + + return { + type: tokenType, + value: value, + isUnclosed: false, + quote: "`" + }; + }, + + /* + * Extract a string out of the next sequence of characters and/or + * lines or return 'null' if its not possible. Since strings can + * span across multiple lines this method has to move the char + * pointer. + * + * This method recognizes pseudo-multiline JavaScript strings: + * + * var str = "hello\ + * world"; + */ + scanStringLiteral: function(checks) { + /*jshint loopfunc:true */ + var quote = this.peek(); + + // String must start with a quote. + if (quote !== "\"" && quote !== "'") { + return null; + } + + // In JSON strings must always use double quotes. + this.triggerAsync("warning", { + code: "W108", + line: this.line, + character: this.char // +1? + }, checks, function() { return state.jsonMode && quote !== "\""; }); + + var value = ""; + var startLine = this.line; + var startChar = this.char; + var allowNewLine = false; + + this.skip(); + + while (this.peek() !== quote) { + while (this.peek() === "") { // End Of Line + + // If an EOL is not preceded by a backslash, show a warning + // and proceed like it was a legit multi-line string where + // author simply forgot to escape the newline symbol. + // + // Another approach is to implicitly close a string on EOL + // but it generates too many false positives. + + if (!allowNewLine) { + this.trigger("warning", { + code: "W112", + line: this.line, + character: this.char + }); + } else { + allowNewLine = false; + + // Otherwise show a warning if multistr option was not set. + // For JSON, show warning no matter what. + + this.triggerAsync("warning", { + code: "W043", + line: this.line, + character: this.char + }, checks, function() { return !state.option.multistr; }); + + this.triggerAsync("warning", { + code: "W042", + line: this.line, + character: this.char + }, checks, function() { return state.jsonMode && state.option.multistr; }); + } + + // If we get an EOF inside of an unclosed string, show an + // error and implicitly close it at the EOF point. + + if (!this.nextLine()) { + this.trigger("error", { + code: "E029", + line: startLine, + character: startChar + }); + + return { + type: Token.StringLiteral, + value: value, + isUnclosed: true, + quote: quote + }; + } + } + + allowNewLine = false; + var char = this.peek(); + var jump = 1; // A length of a jump, after we're done + // parsing this character. + + if (char < " ") { + // Warn about a control character in a string. + this.trigger("warning", { + code: "W113", + line: this.line, + character: this.char, + data: [ "" ] + }); + } + + // Special treatment for some escaped characters. + if (char === "\\") { + var parsed = this.scanEscapeSequence(checks); + char = parsed.char; + jump = parsed.jump; + allowNewLine = parsed.allowNewLine; + } + + value += char; + this.skip(jump); + } + + this.skip(); + return { + type: Token.StringLiteral, + value: value, + isUnclosed: false, + quote: quote + }; + }, + + /* + * Extract a regular expression out of the next sequence of + * characters and/or lines or return 'null' if its not possible. + * + * This method is platform dependent: it accepts almost any + * regular expression values but then tries to compile and run + * them using system's RegExp object. This means that there are + * rare edge cases where one JavaScript engine complains about + * your regular expression while others don't. + */ + scanRegExp: function() { + var index = 0; + var length = this.input.length; + var char = this.peek(); + var value = char; + var body = ""; + var flags = []; + var malformed = false; + var isCharSet = false; + var terminated; + + var scanUnexpectedChars = function() { + // Unexpected control character + if (char < " ") { + malformed = true; + this.trigger("warning", { + code: "W048", + line: this.line, + character: this.char + }); + } + + // Unexpected escaped character + if (char === "<") { + malformed = true; + this.trigger("warning", { + code: "W049", + line: this.line, + character: this.char, + data: [ char ] + }); + } + }.bind(this); + + // Regular expressions must start with '/' + if (!this.prereg || char !== "/") { + return null; + } + + index += 1; + terminated = false; + + // Try to get everything in between slashes. A couple of + // cases aside (see scanUnexpectedChars) we don't really + // care whether the resulting expression is valid or not. + // We will check that later using the RegExp object. + + while (index < length) { + char = this.peek(index); + value += char; + body += char; + + if (isCharSet) { + if (char === "]") { + if (this.peek(index - 1) !== "\\" || this.peek(index - 2) === "\\") { + isCharSet = false; + } + } + + if (char === "\\") { + index += 1; + char = this.peek(index); + body += char; + value += char; + + scanUnexpectedChars(); + } + + index += 1; + continue; + } + + if (char === "\\") { + index += 1; + char = this.peek(index); + body += char; + value += char; + + scanUnexpectedChars(); + + if (char === "/") { + index += 1; + continue; + } + + if (char === "[") { + index += 1; + continue; + } + } + + if (char === "[") { + isCharSet = true; + index += 1; + continue; + } + + if (char === "/") { + body = body.substr(0, body.length - 1); + terminated = true; + index += 1; + break; + } + + index += 1; + } + + // A regular expression that was never closed is an + // error from which we cannot recover. + + if (!terminated) { + this.trigger("error", { + code: "E015", + line: this.line, + character: this.from + }); + + return void this.trigger("fatal", { + line: this.line, + from: this.from + }); + } + + // Parse flags (if any). + + while (index < length) { + char = this.peek(index); + if (!/[gim]/.test(char)) { + break; + } + flags.push(char); + value += char; + index += 1; + } + + // Check regular expression for correctness. + + try { + new RegExp(body, flags.join("")); + } catch (err) { + malformed = true; + this.trigger("error", { + code: "E016", + line: this.line, + character: this.char, + data: [ err.message ] // Platform dependent! + }); + } + + return { + type: Token.RegExp, + value: value, + flags: flags, + isMalformed: malformed + }; + }, + + /* + * Scan for any occurrence of non-breaking spaces. Non-breaking spaces + * can be mistakenly typed on OS X with option-space. Non UTF-8 web + * pages with non-breaking pages produce syntax errors. + */ + scanNonBreakingSpaces: function() { + return state.option.nonbsp ? + this.input.search(/(\u00A0)/) : -1; + }, + + /* + * Scan for characters that get silently deleted by one or more browsers. + */ + scanUnsafeChars: function() { + return this.input.search(reg.unsafeChars); + }, + + /* + * Produce the next raw token or return 'null' if no tokens can be matched. + * This method skips over all space characters. + */ + next: function(checks) { + this.from = this.char; + + // Move to the next non-space character. + var start; + if (/\s/.test(this.peek())) { + start = this.char; + + while (/\s/.test(this.peek())) { + this.from += 1; + this.skip(); + } + } + + // Methods that work with multi-line structures and move the + // character pointer. + + var match = this.scanComments() || + this.scanStringLiteral(checks) || + this.scanTemplateLiteral(checks); + + if (match) { + return match; + } + + // Methods that don't move the character pointer. + + match = + this.scanRegExp() || + this.scanPunctuator() || + this.scanKeyword() || + this.scanIdentifier() || + this.scanNumericLiteral(); + + if (match) { + this.skip(match.tokenLength || match.value.length); + return match; + } + + // No token could be matched, give up. + + return null; + }, + + /* + * Switch to the next line and reset all char pointers. Once + * switched, this method also checks for other minor warnings. + */ + nextLine: function() { + var char; + + if (this.line >= this.getLines().length) { + return false; + } + + this.input = this.getLines()[this.line]; + this.line += 1; + this.char = 1; + this.from = 1; + + var inputTrimmed = this.input.trim(); + + var startsWith = function() { + return _.some(arguments, function(prefix) { + return inputTrimmed.indexOf(prefix) === 0; + }); + }; + + var endsWith = function() { + return _.some(arguments, function(suffix) { + return inputTrimmed.indexOf(suffix, inputTrimmed.length - suffix.length) !== -1; + }); + }; + + // If we are ignoring linter errors, replace the input with empty string + // if it doesn't already at least start or end a multi-line comment + if (state.ignoreLinterErrors === true) { + if (!startsWith("/*", "//") && !(this.inComment && endsWith("*/"))) { + this.input = ""; + } + } + + char = this.scanNonBreakingSpaces(); + if (char >= 0) { + this.trigger("warning", { code: "W125", line: this.line, character: char + 1 }); + } + + this.input = this.input.replace(/\t/g, state.tab); + char = this.scanUnsafeChars(); + + if (char >= 0) { + this.trigger("warning", { code: "W100", line: this.line, character: char }); + } + + // If there is a limit on line length, warn when lines get too + // long. + + if (state.option.maxlen && state.option.maxlen < this.input.length) { + var inComment = this.inComment || + startsWith.call(inputTrimmed, "//") || + startsWith.call(inputTrimmed, "/*"); + + var shouldTriggerError = !inComment || !reg.maxlenException.test(inputTrimmed); + + if (shouldTriggerError) { + this.trigger("warning", { code: "W101", line: this.line, character: this.input.length }); + } + } + + return true; + }, + + /* + * This is simply a synonym for nextLine() method with a friendlier + * public name. + */ + start: function() { + this.nextLine(); + }, + + /* + * Produce the next token. This function is called by advance() to get + * the next token. It returns a token in a JSLint-compatible format. + */ + token: function() { + /*jshint loopfunc:true */ + var checks = asyncTrigger(); + var token; + + + function isReserved(token, isProperty) { + if (!token.reserved) { + return false; + } + var meta = token.meta; + + if (meta && meta.isFutureReservedWord && state.option.inES5()) { + // ES3 FutureReservedWord in an ES5 environment. + if (!meta.es5) { + return false; + } + + // Some ES5 FutureReservedWord identifiers are active only + // within a strict mode environment. + if (meta.strictOnly) { + if (!state.option.strict && !state.directive["use strict"]) { + return false; + } + } + + if (isProperty) { + return false; + } + } + + return true; + } + + // Produce a token object. + var create = function(type, value, isProperty, token) { + /*jshint validthis:true */ + var obj; + + if (type !== "(endline)" && type !== "(end)") { + this.prereg = false; + } + + if (type === "(punctuator)") { + switch (value) { + case ".": + case ")": + case "~": + case "#": + case "]": + case "++": + case "--": + this.prereg = false; + break; + default: + this.prereg = true; + } + + obj = Object.create(state.syntax[value] || state.syntax["(error)"]); + } + + if (type === "(identifier)") { + if (value === "return" || value === "case" || value === "typeof") { + this.prereg = true; + } + + if (_.has(state.syntax, value)) { + obj = Object.create(state.syntax[value] || state.syntax["(error)"]); + + // If this can't be a reserved keyword, reset the object. + if (!isReserved(obj, isProperty && type === "(identifier)")) { + obj = null; + } + } + } + + if (!obj) { + obj = Object.create(state.syntax[type]); + } + + obj.identifier = (type === "(identifier)"); + obj.type = obj.type || type; + obj.value = value; + obj.line = this.line; + obj.character = this.char; + obj.from = this.from; + if (obj.identifier && token) obj.raw_text = token.text || token.value; + + if (isProperty && obj.identifier) { + obj.isProperty = isProperty; + } + + obj.check = checks.check; + + return obj; + }.bind(this); + + for (;;) { + if (!this.input.length) { + return create(this.nextLine() ? "(endline)" : "(end)", ""); + } + + token = this.next(checks); + + if (!token) { + if (this.input.length) { + // Unexpected character. + this.trigger("error", { + code: "E024", + line: this.line, + character: this.char, + data: [ this.peek() ] + }); + + this.input = ""; + } + + continue; + } + + switch (token.type) { + case Token.StringLiteral: + this.triggerAsync("String", { + line: this.line, + char: this.char, + from: this.from, + value: token.value, + quote: token.quote + }, checks, function() { return true; }); + + return create("(string)", token.value); + + case Token.TemplateHead: + this.trigger("TemplateHead", { + line: this.line, + char: this.char, + from: this.from, + value: token.value + }); + return create("(template)", token.value); + + case Token.TemplateMiddle: + this.trigger("TemplateMiddle", { + line: this.line, + char: this.char, + from: this.from, + value: token.value + }); + return create("(template middle)", token.value); + + case Token.TemplateTail: + this.trigger("TemplateTail", { + line: this.line, + char: this.char, + from: this.from, + value: token.value + }); + return create("(template tail)", token.value); + + case Token.Identifier: + this.trigger("Identifier", { + line: this.line, + char: this.char, + from: this.form, + name: token.value, + raw_name: token.text, + isProperty: state.tokens.curr.id === "." + }); + + /* falls through */ + case Token.Keyword: + case Token.NullLiteral: + case Token.BooleanLiteral: + return create("(identifier)", token.value, state.tokens.curr.id === ".", token); + + case Token.NumericLiteral: + if (token.isMalformed) { + this.trigger("warning", { + code: "W045", + line: this.line, + character: this.char, + data: [ token.value ] + }); + } + + this.triggerAsync("warning", { + code: "W114", + line: this.line, + character: this.char, + data: [ "0x-" ] + }, checks, function() { return token.base === 16 && state.jsonMode; }); + + this.triggerAsync("warning", { + code: "W115", + line: this.line, + character: this.char + }, checks, function() { + return state.directive["use strict"] && token.base === 8 && token.isLegacy; + }); + + this.trigger("Number", { + line: this.line, + char: this.char, + from: this.from, + value: token.value, + base: token.base, + isMalformed: token.malformed + }); + + return create("(number)", token.value); + + case Token.RegExp: + return create("(regexp)", token.value); + + case Token.Comment: + state.tokens.curr.comment = true; + + if (token.isSpecial) { + return { + id: '(comment)', + value: token.value, + body: token.body, + type: token.commentType, + isSpecial: token.isSpecial, + line: this.line, + character: this.char, + from: this.from + }; + } + + break; + + case "": + break; + + default: + return create("(punctuator)", token.value); + } + } + } +}; + +exports.Lexer = Lexer; + +},{"../data/ascii-identifier-data.js":1,"../data/non-ascii-identifier-part-only.js":2,"../data/non-ascii-identifier-start.js":3,"./reg.js":17,"./state.js":18,"events":5,"underscore":12}],14:[function(require,module,exports){ +"use strict"; + +var _ = require("underscore"); + +var errors = { + // JSHint options + E001: "Bad option: '{a}'.", + E002: "Bad option value.", + + // JSHint input + E003: "Expected a JSON value.", + E004: "Input is neither a string nor an array of strings.", + E005: "Input is empty.", + E006: "Unexpected early end of program.", + + // Strict mode + E007: "Missing \"use strict\" statement.", + E008: "Strict violation.", + E009: "Option 'validthis' can't be used in a global scope.", + E010: "'with' is not allowed in strict mode.", + + // Constants + E011: "const '{a}' has already been declared.", + E012: "const '{a}' is initialized to 'undefined'.", + E013: "Attempting to override '{a}' which is a constant.", + + // Regular expressions + E014: "A regular expression literal can be confused with '/='.", + E015: "Unclosed regular expression.", + E016: "Invalid regular expression.", + + // Tokens + E017: "Unclosed comment.", + E018: "Unbegun comment.", + E019: "Unmatched '{a}'.", + E020: "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'.", + E021: "Expected '{a}' and instead saw '{b}'.", + E022: "Line breaking error '{a}'.", + E023: "Missing '{a}'.", + E024: "Unexpected '{a}'.", + E025: "Missing ':' on a case clause.", + E026: "Missing '}' to match '{' from line {a}.", + E027: "Missing ']' to match '[' from line {a}.", + E028: "Illegal comma.", + E029: "Unclosed string.", + + // Everything else + E030: "Expected an identifier and instead saw '{a}'.", + E031: "Bad assignment.", // FIXME: Rephrase + E032: "Expected a small integer or 'false' and instead saw '{a}'.", + E033: "Expected an operator and instead saw '{a}'.", + E034: "get/set are ES5 features.", + E035: "Missing property name.", + E036: "Expected to see a statement and instead saw a block.", + E037: null, + E038: null, + E039: "Function declarations are not invocable. Wrap the whole function invocation in parens.", + E040: "Each value should have its own case label.", + E041: "Unrecoverable syntax error.", + E042: "Stopping.", + E043: "Too many errors.", + E044: null, + E045: "Invalid for each loop.", + E046: "A yield statement shall be within a generator function (with syntax: `function*`)", + E047: null, // Vacant + E048: "Let declaration not directly within block.", + E049: "A {a} cannot be named '{b}'.", + E050: "Mozilla requires the yield expression to be parenthesized here.", + E051: "Regular parameters cannot come after default parameters.", + E052: "Unclosed template literal.", + E053: "Export declaration must be in global scope.", + E054: "Class properties must be methods. Expected '(' but instead saw '{a}'." +}; + +var warnings = { + W001: "'hasOwnProperty' is a really bad name.", + W002: "Value of '{a}' may be overwritten in IE 8 and earlier.", + W003: "'{a}' was used before it was defined.", + W004: "'{a}' is already defined.", + W005: "A dot following a number can be confused with a decimal point.", + W006: "Confusing minuses.", + W007: "Confusing plusses.", + W008: "A leading decimal point can be confused with a dot: '{a}'.", + W009: "The array literal notation [] is preferable.", + W010: "The object literal notation {} is preferable.", + W011: null, + W012: null, + W013: null, + W014: "Bad line breaking before '{a}'.", + W015: null, + W016: "Unexpected use of '{a}'.", + W017: "Bad operand.", + W018: "Confusing use of '{a}'.", + W019: "Use the isNaN function to compare with NaN.", + W020: "Read only.", + W021: "'{a}' is a function.", + W022: "Do not assign to the exception parameter.", + W023: "Expected an identifier in an assignment and instead saw a function invocation.", + W024: "Expected an identifier and instead saw '{a}' (a reserved word).", + W025: "Missing name in function declaration.", + W026: "Inner functions should be listed at the top of the outer function.", + W027: "Unreachable '{a}' after '{b}'.", + W028: "Label '{a}' on {b} statement.", + W030: "Expected an assignment or function call and instead saw an expression.", + W031: "Do not use 'new' for side effects.", + W032: "Unnecessary semicolon.", + W033: "Missing semicolon.", + W034: "Unnecessary directive \"{a}\".", + W035: "Empty block.", + W036: "Unexpected /*member '{a}'.", + W037: "'{a}' is a statement label.", + W038: "'{a}' used out of scope.", + W039: "'{a}' is not allowed.", + W040: "Possible strict violation.", + W041: "Use '{a}' to compare with '{b}'.", + W042: "Avoid EOL escaping.", + W043: "Bad escaping of EOL. Use option multistr if needed.", + W044: "Bad or unnecessary escaping.", /* TODO(caitp): remove W044 */ + W045: "Bad number '{a}'.", + W046: "Don't use extra leading zeros '{a}'.", + W047: "A trailing decimal point can be confused with a dot: '{a}'.", + W048: "Unexpected control character in regular expression.", + W049: "Unexpected escaped character '{a}' in regular expression.", + W050: "JavaScript URL.", + W051: "Variables should not be deleted.", + W052: "Unexpected '{a}'.", + W053: "Do not use {a} as a constructor.", + W054: "The Function constructor is a form of eval.", + W055: "A constructor name should start with an uppercase letter.", + W056: "Bad constructor.", + W057: "Weird construction. Is 'new' necessary?", + W058: "Missing '()' invoking a constructor.", + W059: "Avoid arguments.{a}.", + W060: "document.write can be a form of eval.", + W061: "eval can be harmful.", + W062: "Wrap an immediate function invocation in parens " + + "to assist the reader in understanding that the expression " + + "is the result of a function, and not the function itself.", + W063: "Math is not a function.", + W064: "Missing 'new' prefix when invoking a constructor.", + W065: "Missing radix parameter.", + W066: "Implied eval. Consider passing a function instead of a string.", + W067: "Bad invocation.", + W068: "Wrapping non-IIFE function literals in parens is unnecessary.", + W069: "['{a}'] is better written in dot notation.", + W070: "Extra comma. (it breaks older versions of IE)", + W071: "This function has too many statements. ({a})", + W072: "This function has too many parameters. ({a})", + W073: "Blocks are nested too deeply. ({a})", + W074: "This function's cyclomatic complexity is too high. ({a})", + W075: "Duplicate {a} '{b}'.", + W076: "Unexpected parameter '{a}' in get {b} function.", + W077: "Expected a single parameter in set {a} function.", + W078: "Setter is defined without getter.", + W079: "Redefinition of '{a}'.", + W080: "It's not necessary to initialize '{a}' to 'undefined'.", + W081: null, + W082: "Function declarations should not be placed in blocks. " + + "Use a function expression or move the statement to the top of " + + "the outer function.", + W083: "Don't make functions within a loop.", + W084: "Expected a conditional expression and instead saw an assignment.", + W085: "Don't use 'with'.", + W086: "Expected a 'break' statement before '{a}'.", + W087: "Forgotten 'debugger' statement?", + W088: "Creating global 'for' variable. Should be 'for (var {a} ...'.", + W089: "The body of a for in should be wrapped in an if statement to filter " + + "unwanted properties from the prototype.", + W090: "'{a}' is not a statement label.", + W091: "'{a}' is out of scope.", + W093: "Did you mean to return a conditional instead of an assignment?", + W094: "Unexpected comma.", + W095: "Expected a string and instead saw {a}.", + W096: "The '{a}' key may produce unexpected results.", + W097: "Use the function form of \"use strict\".", + W098: "'{a}' is defined but never used.", + W099: null, + W100: "This character may get silently deleted by one or more browsers.", + W101: "Line is too long.", + W102: null, + W103: "The '{a}' property is deprecated.", + W104: "'{a}' is available in ES6 (use esnext option) or Mozilla JS extensions (use moz).", + W105: "Unexpected {a} in '{b}'.", + W106: "Identifier '{a}' is not in camel case.", + W107: "Script URL.", + W108: "Strings must use doublequote.", + W109: "Strings must use singlequote.", + W110: "Mixed double and single quotes.", + W112: "Unclosed string.", + W113: "Control character in string: {a}.", + W114: "Avoid {a}.", + W115: "Octal literals are not allowed in strict mode.", + W116: "Expected '{a}' and instead saw '{b}'.", + W117: "'{a}' is not defined.", + W118: "'{a}' is only available in Mozilla JavaScript extensions (use moz option).", + W119: "'{a}' is only available in ES6 (use esnext option).", + W120: "You might be leaking a variable ({a}) here.", + W121: "Extending prototype of native object: '{a}'.", + W122: "Invalid typeof value '{a}'", + W123: "'{a}' is already defined in outer scope.", + W124: "A generator function shall contain a yield statement.", + W125: "This line contains non-breaking spaces: http://jshint.com/doc/options/#nonbsp", + W126: "Unnecessary grouping operator.", + W127: "Unexpected use of a comma operator.", + W128: "Empty array elements require elision=true." +}; + +var info = { + I001: "Comma warnings can be turned off with 'laxcomma'.", + I002: null, + I003: "ES5 option is now set per default" +}; + +exports.errors = {}; +exports.warnings = {}; +exports.info = {}; + +_.each(errors, function(desc, code) { + exports.errors[code] = { code: code, desc: desc }; +}); + +_.each(warnings, function(desc, code) { + exports.warnings[code] = { code: code, desc: desc }; +}); + +_.each(info, function(desc, code) { + exports.info[code] = { code: code, desc: desc }; +}); + +},{"underscore":12}],15:[function(require,module,exports){ +"use strict"; + +function NameStack() { + this._stack = []; +} + +Object.defineProperty(NameStack.prototype, "length", { + get: function() { + return this._stack.length; + } +}); + +/** + * Create a new entry in the stack. Useful for tracking names across + * expressions. + */ +NameStack.prototype.push = function() { + this._stack.push(null); +}; + +/** + * Discard the most recently-created name on the stack. + */ +NameStack.prototype.pop = function() { + this._stack.pop(); +}; + +/** + * Update the most recent name on the top of the stack. + * + * @param {object} token The token to consider as the source for the most + * recent name. + */ +NameStack.prototype.set = function(token) { + this._stack[this.length - 1] = token; +}; + +/** + * Generate a string representation of the most recent name. + * + * @returns {string} + */ +NameStack.prototype.infer = function() { + var nameToken = this._stack[this.length - 1]; + var prefix = ""; + var type; + + // During expected operation, the topmost entry on the stack will only + // reflect the current function's name when the function is declared without + // the `function` keyword (i.e. for in-line accessor methods). In other + // cases, the `function` expression itself will introduce an empty entry on + // the top of the stack, and this should be ignored. + if (!nameToken || nameToken.type === "class") { + nameToken = this._stack[this.length - 2]; + } + + if (!nameToken) { + return "(empty)"; + } + + type = nameToken.type; + + if (type !== "(string)" && type !== "(number)" && type !== "(identifier)" && type !== "default") { + return "(expression)"; + } + + if (nameToken.accessorType) { + prefix = nameToken.accessorType + " "; + } + + return prefix + nameToken.value; +}; + +module.exports = NameStack; + +},{}],16:[function(require,module,exports){ +"use strict"; + +// These are the JSHint boolean options. +exports.bool = { + enforcing: { + + /** + * This option prohibits the use of bitwise operators such as `^` (XOR), + * `|` (OR) and others. Bitwise operators are very rare in JavaScript + * programs and quite often `&` is simply a mistyped `&&`. + */ + bitwise : true, + + /** + * + * This options prohibits overwriting prototypes of native objects such as + * `Array`, `Date` and so on. + * + * // jshint freeze:true + * Array.prototype.count = function (value) { return 4; }; + * // -> Warning: Extending prototype of native object: 'Array'. + */ + freeze : true, + + /** + * This option allows you to force all variable names to use either + * camelCase style or UPPER_CASE with underscores. + */ + camelcase : true, + + /** + * This option requires you to always put curly braces around blocks in + * loops and conditionals. JavaScript allows you to omit curly braces when + * the block consists of only one statement, for example: + * + * while (day) + * shuffle(); + * + * However, in some circumstances, it can lead to bugs (you'd think that + * `sleep()` is a part of the loop while in reality it is not): + * + * while (day) + * shuffle(); + * sleep(); + */ + curly : true, + + /** + * This options prohibits the use of `==` and `!=` in favor of `===` and + * `!==`. The former try to coerce values before comparing them which can + * lead to some unexpected results. The latter don't do any coercion so + * they are generally safer. If you would like to learn more about type + * coercion in JavaScript, we recommend [Truth, Equality and + * JavaScript](http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/) + * by Angus Croll. + */ + eqeqeq : true, + + /** + * This option suppresses warnings about invalid `typeof` operator values. + * This operator has only [a limited set of possible return + * values](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof). + * By default, JSHint warns when you compare its result with an invalid + * value which often can be a typo. + * + * // 'fuction' instead of 'function' + * if (typeof a == "fuction") { // Invalid typeof value 'fuction' + * // ... + * } + * + * Do not use this option unless you're absolutely sure you don't want + * these checks. + */ + notypeof : true, + + /** + * This option tells JSHint that your code needs to adhere to ECMAScript 3 + * specification. Use this option if you need your program to be executable + * in older browsers—such as Internet Explorer 6/7/8/9—and other legacy + * JavaScript environments. + */ + es3 : true, + + /** + * This option enables syntax first defined in [the ECMAScript 5.1 + * specification](http://es5.github.io/). This includes allowing reserved + * keywords as object properties. + */ + es5 : true, + + /** + * This option requires all `for in` loops to filter object's items. The + * for in statement allows for looping through the names of all of the + * properties of an object including those inherited through the prototype + * chain. This behavior can lead to unexpected items in your object so it + * is generally safer to always filter inherited properties out as shown in + * the example: + * + * for (key in obj) { + * if (obj.hasOwnProperty(key)) { + * // We are sure that obj[key] belongs to the object and was not inherited. + * } + * } + * + * For more in-depth understanding of `for in` loops in JavaScript, read + * [Exploring JavaScript for-in + * loops](http://javascriptweblog.wordpress.com/2011/01/04/exploring-javascript-for-in-loops/) + * by Angus Croll. + */ + forin : true, + + /** + * This option suppresses warnings about declaring variables inside of + * control + * structures while accessing them later from the outside. Even though + * JavaScript has only two real scopes—global and function—such practice + * leads to confusion among people new to the language and hard-to-debug + * bugs. This is why, by default, JSHint warns about variables that are + * used outside of their intended scope. + * + * function test() { + * if (true) { + * var x = 0; + * } + * + * x += 1; // Default: 'x' used out of scope. + * // No warning when funcscope:true + * } + */ + funcscope : true, + + /** + * This option suppresses warnings about the use of global strict mode. + * Global strict mode can break third-party widgets so it is not + * recommended. + * + * For more info about strict mode see the `strict` option. + */ + globalstrict: true, + + /** + * This option prohibits the use of immediate function invocations without + * wrapping them in parentheses. Wrapping parentheses assists readers of + * your code in understanding that the expression is the result of a + * function, and not the function itself. + */ + immed : true, + + /** + * This option suppresses warnings about the `__iterator__` property. This + * property is not supported by all browsers so use it carefully. + */ + iterator : true, + + /** + * This option requires you to capitalize names of constructor functions. + * Capitalizing functions that are intended to be used with `new` operator + * is just a convention that helps programmers to visually distinguish + * constructor functions from other types of functions to help spot + * mistakes when using `this`. + * + * Not doing so won't break your code in any browsers or environments but + * it will be a bit harder to figure out—by reading the code—if the + * function was supposed to be used with or without new. And this is + * important because when the function that was intended to be used with + * `new` is used without it, `this` will point to the global object instead + * of a new object. + */ + newcap : true, + + /** + * This option prohibits the use of `arguments.caller` and + * `arguments.callee`. Both `.caller` and `.callee` make quite a few + * optimizations impossible so they were deprecated in future versions of + * JavaScript. In fact, ECMAScript 5 forbids the use of `arguments.callee` + * in strict mode. + */ + noarg : true, + + /** + * This option prohibits the use of the comma operator. When misused, the + * comma operator can obscure the value of a statement and promote + * incorrect code. + */ + nocomma : true, + + /** + * This option warns when you have an empty block in your code. JSLint was + * originally warning for all empty blocks and we simply made it optional. + * There were no studies reporting that empty blocks in JavaScript break + * your code in any way. + */ + noempty : true, + + /** + * This option warns about "non-breaking whitespace" characters. These + * characters can be entered with option-space on Mac computers and have a + * potential of breaking non-UTF8 web pages. + */ + nonbsp : true, + + /** + * This option prohibits the use of constructor functions for side-effects. + * Some people like to call constructor functions without assigning its + * result to any variable: + * + * new MyConstructor(); + * + * There is no advantage in this approach over simply calling + * `MyConstructor` since the object that the operator `new` creates isn't + * used anywhere so you should generally avoid constructors like this one. + */ + nonew : true, + + /** + * This option prohibits the use of explicitly undeclared variables. This + * option is very useful for spotting leaking and mistyped variables. + * + * // jshint undef:true + * + * function test() { + * var myVar = 'Hello, World'; + * console.log(myvar); // Oops, typoed here. JSHint with undef will complain + * } + * + * If your variable is defined in another file, you can use the `global` + * directive to tell JSHint about it. + */ + undef : true, + + /** + * This option prohibits the use of the grouping operator when it is not + * strictly required. Such usage commonly reflects a misunderstanding of + * unary operators, for example: + * + * // jshint singleGroups: true + * + * delete(obj.attr); // Warning: Unnecessary grouping operator. + */ + singleGroups: false, + + /** + * This option is a short hand for the most strict JSHint configuration. It + * enables all enforcing options and disables all relaxing options. + */ + enforceall : false + }, + relaxing: { + + /** + * This option suppresses warnings about missing semicolons. There is a lot + * of FUD about semicolon spread by quite a few people in the community. + * The common myths are that semicolons are required all the time (they are + * not) and that they are unreliable. JavaScript has rules about semicolons + * which are followed by *all* browsers so it is up to you to decide + * whether you should or should not use semicolons in your code. + * + * For more information about semicolons in JavaScript read [An Open Letter + * to JavaScript Leaders Regarding + * Semicolons](http://blog.izs.me/post/2353458699/an-open-letter-to-javascript-leaders-regarding) + * by Isaac Schlueter and [JavaScript Semicolon + * Insertion](http://inimino.org/~inimino/blog/javascript_semicolons). + */ + asi : true, + + /** + * This option suppresses warnings about multi-line strings. Multi-line + * strings can be dangerous in JavaScript because all hell breaks loose if + * you accidentally put a whitespace in between the escape character (`\`) + * and a new line. + * + * Note that even though this option allows correct multi-line strings, it + * still warns about multi-line strings without escape characters or with + * anything in between the escape character and a whitespace. + * + * // jshint multistr:true + * + * var text = "Hello\ + * World"; // All good. + * + * text = "Hello + * World"; // Warning, no escape character. + * + * text = "Hello\ + * World"; // Warning, there is a space after \ + */ + multistr : true, + + /** + * This option suppresses warnings about the `debugger` statements in your + * code. + */ + debug : true, + + /** + * This option suppresses warnings about the use of assignments in cases + * where comparisons are expected. More often than not, code like `if (a = + * 10) {}` is a typo. However, it can be useful in cases like this one: + * + * for (var i = 0, person; person = people[i]; i++) {} + * + * You can silence this error on a per-use basis by surrounding the assignment + * with parenthesis, such as: + * + * for (var i = 0, person; (person = people[i]); i++) {} + */ + boss : true, + + /** + * This option defines globals available when your core is running inside + * of the PhantomJS runtime environment. [PhantomJS](http://phantomjs.org/) + * is a headless WebKit scriptable with a JavaScript API. It has fast and + * native support for various web standards: DOM handling, CSS selector, + * JSON, Canvas, and SVG. + */ + phantom : true, + + /** + * This option suppresses warnings about the use of `eval`. The use of + * `eval` is discouraged because it can make your code vulnerable to + * various injection attacks and it makes it hard for JavaScript + * interpreter to do certain optimizations. + */ + evil : true, + + /** + * This option prohibits the use of unary increment and decrement + * operators. Some people think that `++` and `--` reduces the quality of + * their coding styles and there are programming languages—such as + * Python—that go completely without these operators. + */ + plusplus : true, + + /** + * This option suppresses warnings about the `__proto__` property. + */ + proto : true, + + /** + * This option suppresses warnings about the use of script-targeted + * URLs—such as `javascript:...`. + */ + scripturl : true, + + /** + * This option requires all functions to run in ECMAScript 5's strict mode. + * [Strict mode](https://developer.mozilla.org/en/JavaScript/Strict_mode) + * is a way to opt in to a restricted variant of JavaScript. Strict mode + * eliminates some JavaScript pitfalls that didn't cause errors by changing + * them to produce errors. It also fixes mistakes that made it difficult + * for the JavaScript engines to perform certain optimizations. + * + * *Note:* This option enables strict mode for function scope only. It + * *prohibits* the global scoped strict mode because it might break + * third-party widgets on your page. If you really want to use global + * strict mode, see the *globalstrict* option. + */ + strict : true, + + /** + * This option suppresses warnings about using `[]` notation when it can be + * expressed in dot notation: `person['name']` vs. `person.name`. + */ + sub : true, + + /** + * This option suppresses warnings about "weird" constructions like + * `new function () { ... }` and `new Object;`. Such constructions are + * sometimes used to produce singletons in JavaScript: + * + * var singleton = new function() { + * var privateVar; + * + * this.publicMethod = function () {} + * this.publicMethod2 = function () {} + * }; + */ + supernew : true, + + /** + * This option suppresses most of the warnings about possibly unsafe line + * breakings in your code. It doesn't suppress warnings about comma-first + * coding style. To suppress those you have to use `laxcomma` (see below). + */ + laxbreak : true, + + /** + * This option suppresses warnings about comma-first coding style: + * + * var obj = { + * name: 'Anton' + * , handle: 'valueof' + * , role: 'SW Engineer' + * }; + */ + laxcomma : true, + + /** + * This option suppresses warnings about possible strict violations when + * the code is running in strict mode and you use `this` in a + * non-constructor function. You should use this option—in a function scope + * only—when you are positive that your use of `this` is valid in the + * strict mode (for example, if you call your function using + * `Function.call`). + * + * **Note:** This option can be used only inside of a function scope. + * JSHint will fail with an error if you will try to set this option + * globally. + */ + validthis : true, + + /** + * This option suppresses warnings about the use of the `with` statement. + * The semantics of the `with` statement can cause confusion among + * developers and accidental definition of global variables. + * + * More info: + * + * * [with Statement Considered + * Harmful](http://yuiblog.com/blog/2006/04/11/with-statement-considered-harmful/) + */ + withstmt : true, + + /** + * This options tells JSHint that your code uses Mozilla JavaScript + * extensions. Unless you develop specifically for the Firefox web browser + * you don't need this option. + * + * More info: + * + * * [New in JavaScript + * 1.7](https://developer.mozilla.org/en-US/docs/JavaScript/New_in_JavaScript/1.7) + */ + moz : true, + + /** + * This option suppresses warnings about generator functions with no + * `yield` statement in them. + */ + noyield : true, + + /** + * This option suppresses warnings about `== null` comparisons. Such + * comparisons are often useful when you want to check if a variable is + * `null` or `undefined`. + */ + eqnull : true, + + /** + * This option suppresses warnings about missing semicolons, but only when + * the semicolon is omitted for the last statement in a one-line block: + * + * var name = (function() { return 'Anton' }()); + * + * This is a very niche use case that is useful only when you use automatic + * JavaScript code generators. + */ + lastsemic : true, + + /** + * This option suppresses warnings about functions inside of loops. + * Defining functions inside of loops can lead to bugs such as this one: + * + * var nums = []; + * + * for (var i = 0; i < 10; i++) { + * nums[i] = function (j) { + * return i + j; + * }; + * } + * + * nums[0](2); // Prints 12 instead of 2 + * + * To fix the code above you need to copy the value of `i`: + * + * var nums = []; + * + * for (var i = 0; i < 10; i++) { + * (function (i) { + * nums[i] = function (j) { + * return i + j; + * }; + * }(i)); + * } + */ + loopfunc : true, + + /** + * This option suppresses warnings about the use of expressions where + * normally you would expect to see assignments or function calls. Most of + * the time, such code is a typo. However, it is not forbidden by the spec + * and that's why this warning is optional. + */ + expr : true, + + /** + * This option tells JSHint that your code uses ECMAScript 6 specific + * syntax. Note that these features are not finalized yet and not all + * browsers implement them. + * + * More info: + * + * * [Draft Specification for ES.next (ECMA-262 Ed. + * 6)](http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts) + */ + esnext : true, + + /** + * This option tells JSHint that your code uses ES3 array elision elements, + * or empty elements (for example, `[1, , , 4, , , 7]`). + */ + elision : true, + }, + + // Third party globals + environments: { + + /** + * This option defines globals exposed by the + * [MooTools](http://mootools.net/) JavaScript framework. + */ + mootools : true, + + /** + * This option defines globals exposed by + * [CouchDB](http://couchdb.apache.org/). CouchDB is a document-oriented + * database that can be queried and indexed in a MapReduce fashion using + * JavaScript. + */ + couch : true, + + /** + * This option defines globals exposed by [the Jasmine unit testing + * framework](https://jasmine.github.io/). + */ + jasmine : true, + + /** + * This option defines globals exposed by the [jQuery](http://jquery.com/) + * JavaScript library. + */ + jquery : true, + + /** + * This option defines globals available when your code is running inside + * of the Node runtime environment. [Node.js](http://nodejs.org/) is a + * server-side JavaScript environment that uses an asynchronous + * event-driven model. This option also skips some warnings that make sense + * in the browser environments but don't make sense in Node such as + * file-level `use strict` pragmas and `console.log` statements. + */ + node : true, + + /** + * This option defines globals exposed by [the QUnit unit testing + * framework](http://qunitjs.com/). + */ + qunit : true, + + /** + * This option defines globals available when your code is running inside + * of the Rhino runtime environment. [Rhino](http://www.mozilla.org/rhino/) + * is an open-source implementation of JavaScript written entirely in Java. + */ + rhino : true, + + /** + * This option defines globals exposed by [the ShellJS + * library](http://documentup.com/arturadib/shelljs). + */ + shelljs : true, + + /** + * This option defines globals exposed by the + * [Prototype](http://www.prototypejs.org/) JavaScript framework. + */ + prototypejs : true, + + /** + * This option defines globals exposed by the [YUI](http://yuilibrary.com/) + * JavaScript framework. + */ + yui : true, + + /** + * This option defines globals exposed by the "BDD" and "TDD" UIs of the + * [Mocha unit testing framework](http://mochajs.org/). + */ + mocha : true, + + /** + * This option defines globals available when your code is running as a + * script for the [Windows Script + * Host](http://en.wikipedia.org/wiki/Windows_Script_Host). + */ + wsh : true, + + /** + * This option defines globals available when your code is running inside + * of a Web Worker. [Web + * Workers](https://developer.mozilla.org/en/Using_web_workers) provide a + * simple means for web content to run scripts in background threads. + */ + worker : true, + + /** + * This option defines non-standard but widely adopted globals such as + * `escape` and `unescape`. + */ + nonstandard : true, + + /** + * This option defines globals exposed by modern browsers: all the way from + * good old `document` and `navigator` to the HTML5 `FileReader` and other + * new developments in the browser world. + * + * **Note:** This option doesn't expose variables like `alert` or + * `console`. See option `devel` for more information. + */ + browser : true, + + /** + * This option defines globals available when using [the Browserify + * tool](http://browserify.org/) to build a project. + */ + browserify : true, + + /** + * This option defines globals that are usually used for logging poor-man's + * debugging: `console`, `alert`, etc. It is usually a good idea to not + * ship them in production because, for example, `console.log` breaks in + * legacy versions of Internet Explorer. + */ + devel : true, + + /** + * This option defines globals exposed by the [Dojo + * Toolkit](http://dojotoolkit.org/). + */ + dojo : true, + + /** + * This option defines globals for typed array constructors. + * + * More info: + * + * * [JavaScript typed + * arrays](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays) + */ + typed : true + }, + + // Obsolete options + obsolete: { + onecase : true, // if one case switch statements should be allowed + regexp : true, // if the . should not be allowed in regexp literals + regexdash : true // if unescaped first/last dash (-) inside brackets + // should be tolerated + } +}; + +// These are the JSHint options that can take any value +// (we use this object to detect invalid options) +exports.val = { + + /** + * This option lets you set the maximum length of a line. + */ + maxlen : false, + + /** + * This option sets a specific tab width for your code. + */ + indent : false, + + /** + * This options allows you to set the maximum amount of warnings JSHint will + * produce before giving up. Default is 50. + */ + maxerr : false, + + predef : false, // predef is deprecated and being replaced by globals + + /** + * This option can be used to specify a white list of global variables that + * are not formally defined in the source code. This is most useful when + * combined with the `undef` option in order to suppress warnings for + * project-specific global variables. + * + * Setting an entry to `true` enables reading and writing to that variable. + * Setting it to `false` will trigger JSHint to consider that variable + * read-only. + * + * See also the "environment" options: a set of options to be used as short + * hand for enabling global variables defined in common JavaScript + * environments. + */ + globals : false, + + /** + * This option enforces the consistency of quotation marks used throughout + * your code. It accepts three values: `true` if you don't want to enforce + * one particular style but want some consistency, `"single"` if you want to + * allow only single quotes and `"double"` if you want to allow only double + * quotes. + */ + quotmark : false, + + scope : false, + + /** + * This option lets you set the max number of statements allowed per function: + * + * // jshint maxstatements:4 + * + * function main() { + * var i = 0; + * var j = 0; + * + * // Function declarations count as one statement. Their bodies + * // don't get taken into account for the outer function. + * function inner() { + * var i2 = 1; + * var j2 = 1; + * + * return i2 + j2; + * } + * + * j = i + j; + * return j; // JSHint: Too many statements per function. (5) + * } + */ + maxstatements: false, + + /** + * This option lets you control how nested do you want your blocks to be: + * + * // jshint maxdepth:2 + * + * function main(meaning) { + * var day = true; + * + * if (meaning === 42) { + * while (day) { + * shuffle(); + * + * if (tired) { // JSHint: Blocks are nested too deeply (3). + * sleep(); + * } + * } + * } + * } + */ + maxdepth : false, + + /** + * This option lets you set the max number of formal parameters allowed per + * function: + * + * // jshint maxparams:3 + * + * function login(request, onSuccess) { + * // ... + * } + * + * // JSHint: Too many parameters per function (4). + * function logout(request, isManual, whereAmI, onSuccess) { + * // ... + * } + */ + maxparams : false, + + /** + * This option lets you control cyclomatic complexity throughout your code. + * Cyclomatic complexity measures the number of linearly independent paths + * through a program's source code. Read more about [cyclomatic complexity on + * Wikipedia](http://en.wikipedia.org/wiki/Cyclomatic_complexity). + */ + maxcomplexity: false, + + /** + * This option suppresses warnings about variable shadowing i.e. declaring a + * variable that had been already declared somewhere in the outer scope. + * + * - "inner" - check for variables defined in the same scope only + * - "outer" - check for variables defined in outer scopes as well + * - false - same as inner + * - true - allow variable shadowing + */ + shadow : false, + + /** + * This option warns when you define and never use your variables. It is very + * useful for general code cleanup, especially when used in addition to + * `undef`. + * + * // jshint unused:true + * + * function test(a, b) { + * var c, d = 2; + * + * return a + d; + * } + * + * test(1, 2); + * + * // Line 3: 'b' was defined but never used. + * // Line 4: 'c' was defined but never used. + * + * In addition to that, this option will warn you about unused global + * variables declared via the `global` directive. + * + * This can be set to `vars` to only check for variables, not function + * parameters, or `strict` to check all variables and parameters. The + * default (true) behavior is to allow unused parameters that are followed by + * a used parameter. + */ + unused : true, + + /** + * This option prohibits the use of a variable before it was defined. + * JavaScript has function scope only and, in addition to that, all variables + * are always moved—or hoisted— to the top of the function. This behavior can + * lead to some very nasty bugs and that's why it is safer to always use + * variable only after they have been explicitly defined. + * + * Setting this option to "nofunc" will allow function declarations to be + * ignored. + * + * For more in-depth understanding of scoping and hoisting in JavaScript, + * read [JavaScript Scoping and + * Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting) + * by Ben Cherry. + */ + latedef : false, + + ignore : false, // start/end ignoring lines of code, bypassing the lexer + // start - start ignoring lines, including the current line + // end - stop ignoring lines, starting on the next line + // line - ignore warnings / errors for just a single line + // (this option does not bypass the lexer) + ignoreDelimiters: false // array of start/end delimiters used to ignore + // certain chunks from code +}; + +// These are JSHint boolean options which are shared with JSLint +// where the definition in JSHint is opposite JSLint +exports.inverted = { + bitwise : true, + forin : true, + newcap : true, + plusplus: true, + regexp : true, + undef : true, + + // Inverted and renamed, use JSHint name here + eqeqeq : true, + strict : true +}; + +exports.validNames = Object.keys(exports.val) + .concat(Object.keys(exports.bool.relaxing)) + .concat(Object.keys(exports.bool.enforcing)) + .concat(Object.keys(exports.bool.obsolete)) + .concat(Object.keys(exports.bool.environments)); + +// These are JSHint boolean options which are shared with JSLint +// where the name has been changed but the effect is unchanged +exports.renamed = { + eqeq : "eqeqeq", + windows: "wsh", + sloppy : "strict" +}; + +exports.removed = { + nomen: true, + onevar: true, + passfail: true, + white: true, + gcl: true, + smarttabs: true, + trailing: true +}; + +},{}],17:[function(require,module,exports){ +/* + * Regular expressions. Some of these are stupidly long. + */ + +/*jshint maxlen:1000 */ + +"use strict"; + +// Unsafe comment or string (ax) +exports.unsafeString = + /@cc|<\/?|script|\]\s*\]|<\s*!|</i; + +// Unsafe characters that are silently deleted by one or more browsers (cx) +exports.unsafeChars = + /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; + +// Characters in strings that need escaping (nx and nxg) +exports.needEsc = + /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; + +exports.needEscGlobal = + /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; + +// Star slash (lx) +exports.starSlash = /\*\//; + +// Identifier (ix) +exports.identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; + +// JavaScript URL (jx) +exports.javascriptURL = /^(?:javascript|jscript|ecmascript|vbscript|livescript)\s*:/i; + +// Catches /* falls through */ comments (ft) +exports.fallsThrough = /^\s*\/\*\s*falls?\sthrough\s*\*\/\s*$/; + +// very conservative rule (eg: only one space between the start of the comment and the first character) +// to relax the maxlen option +exports.maxlenException = /^(?:(?:\/\/|\/\*|\*) ?)?[^ ]+$/; + +},{}],18:[function(require,module,exports){ +"use strict"; +var NameStack = require("./name-stack.js"); + +var state = { + syntax: {}, + + reset: function() { + this.tokens = { + prev: null, + next: null, + curr: null + }; + + this.option = {}; + this.ignored = {}; + this.directive = {}; + this.jsonMode = false; + this.jsonWarnings = []; + this.lines = []; + this.tab = ""; + this.cache = {}; // Node.JS doesn't have Map. Sniff. + this.ignoredLines = {}; + this.forinifcheckneeded = false; + this.nameStack = new NameStack(); + + // Blank out non-multi-line-commented lines when ignoring linter errors + this.ignoreLinterErrors = false; + } +}; + +exports.state = state; + +},{"./name-stack.js":15}],19:[function(require,module,exports){ +"use strict"; + +exports.register = function(linter) { + // Check for properties named __proto__. This special property was + // deprecated and then re-introduced for ES6. + + linter.on("Identifier", function style_scanProto(data) { + if (linter.getOption("proto")) { + return; + } + + if (data.name === "__proto__") { + linter.warn("W103", { + line: data.line, + char: data.char, + data: [ data.name ] + }); + } + }); + + // Check for properties named __iterator__. This is a special property + // available only in browsers with JavaScript 1.7 implementation. + + linter.on("Identifier", function style_scanIterator(data) { + if (linter.getOption("iterator")) { + return; + } + + if (data.name === "__iterator__") { + linter.warn("W104", { + line: data.line, + char: data.char, + data: [ data.name ] + }); + } + }); + + // Check that all identifiers are using camelCase notation. + // Exceptions: names like MY_VAR and _myVar. + + linter.on("Identifier", function style_scanCamelCase(data) { + if (!linter.getOption("camelcase")) { + return; + } + + if (data.name.replace(/^_+|_+$/g, "").indexOf("_") > -1 && !data.name.match(/^[A-Z0-9_]*$/)) { + linter.warn("W106", { + line: data.line, + char: data.from, + data: [ data.name ] + }); + } + }); + + // Enforce consistency in style of quoting. + + linter.on("String", function style_scanQuotes(data) { + var quotmark = linter.getOption("quotmark"); + var esnext = linter.getOption("esnext"); + var code; + + if (!quotmark) { + return; + } + + // If quotmark is enabled, return if this is a template literal. + if (esnext && data.quote === "`") { + return; + } + + // If quotmark is set to 'single' warn about all double-quotes. + + if (quotmark === "single" && data.quote !== "'") { + code = "W109"; + } + + // If quotmark is set to 'double' warn about all single-quotes. + + if (quotmark === "double" && data.quote !== "\"") { + code = "W108"; + } + + // If quotmark is set to true, remember the first quotation style + // and then warn about all others. + + if (quotmark === true) { + if (!linter.getCache("quotmark")) { + linter.setCache("quotmark", data.quote); + } + + if (linter.getCache("quotmark") !== data.quote) { + code = "W110"; + } + } + + if (code) { + linter.warn(code, { + line: data.line, + char: data.char, + }); + } + }); + + linter.on("Number", function style_scanNumbers(data) { + if (data.value.charAt(0) === ".") { + // Warn about a leading decimal point. + linter.warn("W008", { + line: data.line, + char: data.char, + data: [ data.value ] + }); + } + + if (data.value.substr(data.value.length - 1) === ".") { + // Warn about a trailing decimal point. + linter.warn("W047", { + line: data.line, + char: data.char, + data: [ data.value ] + }); + } + + if (/^00+/.test(data.value)) { + // Multiple leading zeroes. + linter.warn("W046", { + line: data.line, + char: data.char, + data: [ data.value ] + }); + } + }); + + // Warn about script URLs. + + linter.on("String", function style_scanJavaScriptURLs(data) { + var re = /^(?:javascript|jscript|ecmascript|vbscript|livescript)\s*:/i; + + if (linter.getOption("scripturl")) { + return; + } + + if (re.test(data.value)) { + linter.warn("W107", { + line: data.line, + char: data.char + }); + } + }); +}; + +},{}],20:[function(require,module,exports){ +// jshint -W001 + +"use strict"; + +// Identifiers provided by the ECMAScript standard. + +exports.reservedVars = { + arguments : false, + NaN : false +}; + +exports.ecmaIdentifiers = { + Array : false, + Boolean : false, + Date : false, + decodeURI : false, + decodeURIComponent : false, + encodeURI : false, + encodeURIComponent : false, + Error : false, + "eval" : false, + EvalError : false, + Function : false, + hasOwnProperty : false, + isFinite : false, + isNaN : false, + JSON : false, + Map : false, + Math : false, + Number : false, + Object : false, + Proxy : false, + Promise : false, + parseInt : false, + parseFloat : false, + RangeError : false, + ReferenceError : false, + RegExp : false, + Set : false, + String : false, + SyntaxError : false, + TypeError : false, + URIError : false, + WeakMap : false, + WeakSet : false +}; + +exports.newEcmaIdentifiers = { + Set : false, + Map : false, + WeakMap : false, + WeakSet : false, + Proxy : false, + Promise : false, + Reflect : false, + Symbol : false, + System : false +}; + +// Global variables commonly provided by a web browser environment. + +exports.browser = { + Audio : false, + Blob : false, + addEventListener : false, + applicationCache : false, + atob : false, + blur : false, + btoa : false, + cancelAnimationFrame : false, + CanvasGradient : false, + CanvasPattern : false, + CanvasRenderingContext2D: false, + CSS : false, + clearInterval : false, + clearTimeout : false, + close : false, + closed : false, + Comment : false, + CustomEvent : false, + DOMParser : false, + defaultStatus : false, + Document : false, + document : false, + DocumentFragment : false, + Element : false, + ElementTimeControl : false, + Event : false, + event : false, + FileReader : false, + FormData : false, + focus : false, + frames : false, + getComputedStyle : false, + HTMLElement : false, + HTMLAnchorElement : false, + HTMLBaseElement : false, + HTMLBlockquoteElement: false, + HTMLBodyElement : false, + HTMLBRElement : false, + HTMLButtonElement : false, + HTMLCanvasElement : false, + HTMLDirectoryElement : false, + HTMLDivElement : false, + HTMLDListElement : false, + HTMLFieldSetElement : false, + HTMLFontElement : false, + HTMLFormElement : false, + HTMLFrameElement : false, + HTMLFrameSetElement : false, + HTMLHeadElement : false, + HTMLHeadingElement : false, + HTMLHRElement : false, + HTMLHtmlElement : false, + HTMLIFrameElement : false, + HTMLImageElement : false, + HTMLInputElement : false, + HTMLIsIndexElement : false, + HTMLLabelElement : false, + HTMLLayerElement : false, + HTMLLegendElement : false, + HTMLLIElement : false, + HTMLLinkElement : false, + HTMLMapElement : false, + HTMLMenuElement : false, + HTMLMetaElement : false, + HTMLModElement : false, + HTMLObjectElement : false, + HTMLOListElement : false, + HTMLOptGroupElement : false, + HTMLOptionElement : false, + HTMLParagraphElement : false, + HTMLParamElement : false, + HTMLPreElement : false, + HTMLQuoteElement : false, + HTMLScriptElement : false, + HTMLSelectElement : false, + HTMLStyleElement : false, + HTMLTableCaptionElement: false, + HTMLTableCellElement : false, + HTMLTableColElement : false, + HTMLTableElement : false, + HTMLTableRowElement : false, + HTMLTableSectionElement: false, + HTMLTextAreaElement : false, + HTMLTitleElement : false, + HTMLUListElement : false, + HTMLVideoElement : false, + history : false, + Image : false, + Intl : false, + length : false, + localStorage : false, + location : false, + matchMedia : false, + MessageChannel : false, + MessageEvent : false, + MessagePort : false, + MouseEvent : false, + moveBy : false, + moveTo : false, + MutationObserver : false, + name : false, + Node : false, + NodeFilter : false, + NodeList : false, + Notification : false, + navigator : false, + onbeforeunload : true, + onblur : true, + onerror : true, + onfocus : true, + onload : true, + onresize : true, + onunload : true, + open : false, + openDatabase : false, + opener : false, + Option : false, + parent : false, + print : false, + Range : false, + requestAnimationFrame : false, + removeEventListener : false, + resizeBy : false, + resizeTo : false, + screen : false, + scroll : false, + scrollBy : false, + scrollTo : false, + sessionStorage : false, + setInterval : false, + setTimeout : false, + SharedWorker : false, + status : false, + SVGAElement : false, + SVGAltGlyphDefElement: false, + SVGAltGlyphElement : false, + SVGAltGlyphItemElement: false, + SVGAngle : false, + SVGAnimateColorElement: false, + SVGAnimateElement : false, + SVGAnimateMotionElement: false, + SVGAnimateTransformElement: false, + SVGAnimatedAngle : false, + SVGAnimatedBoolean : false, + SVGAnimatedEnumeration: false, + SVGAnimatedInteger : false, + SVGAnimatedLength : false, + SVGAnimatedLengthList: false, + SVGAnimatedNumber : false, + SVGAnimatedNumberList: false, + SVGAnimatedPathData : false, + SVGAnimatedPoints : false, + SVGAnimatedPreserveAspectRatio: false, + SVGAnimatedRect : false, + SVGAnimatedString : false, + SVGAnimatedTransformList: false, + SVGAnimationElement : false, + SVGCSSRule : false, + SVGCircleElement : false, + SVGClipPathElement : false, + SVGColor : false, + SVGColorProfileElement: false, + SVGColorProfileRule : false, + SVGComponentTransferFunctionElement: false, + SVGCursorElement : false, + SVGDefsElement : false, + SVGDescElement : false, + SVGDocument : false, + SVGElement : false, + SVGElementInstance : false, + SVGElementInstanceList: false, + SVGEllipseElement : false, + SVGExternalResourcesRequired: false, + SVGFEBlendElement : false, + SVGFEColorMatrixElement: false, + SVGFEComponentTransferElement: false, + SVGFECompositeElement: false, + SVGFEConvolveMatrixElement: false, + SVGFEDiffuseLightingElement: false, + SVGFEDisplacementMapElement: false, + SVGFEDistantLightElement: false, + SVGFEFloodElement : false, + SVGFEFuncAElement : false, + SVGFEFuncBElement : false, + SVGFEFuncGElement : false, + SVGFEFuncRElement : false, + SVGFEGaussianBlurElement: false, + SVGFEImageElement : false, + SVGFEMergeElement : false, + SVGFEMergeNodeElement: false, + SVGFEMorphologyElement: false, + SVGFEOffsetElement : false, + SVGFEPointLightElement: false, + SVGFESpecularLightingElement: false, + SVGFESpotLightElement: false, + SVGFETileElement : false, + SVGFETurbulenceElement: false, + SVGFilterElement : false, + SVGFilterPrimitiveStandardAttributes: false, + SVGFitToViewBox : false, + SVGFontElement : false, + SVGFontFaceElement : false, + SVGFontFaceFormatElement: false, + SVGFontFaceNameElement: false, + SVGFontFaceSrcElement: false, + SVGFontFaceUriElement: false, + SVGForeignObjectElement: false, + SVGGElement : false, + SVGGlyphElement : false, + SVGGlyphRefElement : false, + SVGGradientElement : false, + SVGHKernElement : false, + SVGICCColor : false, + SVGImageElement : false, + SVGLangSpace : false, + SVGLength : false, + SVGLengthList : false, + SVGLineElement : false, + SVGLinearGradientElement: false, + SVGLocatable : false, + SVGMPathElement : false, + SVGMarkerElement : false, + SVGMaskElement : false, + SVGMatrix : false, + SVGMetadataElement : false, + SVGMissingGlyphElement: false, + SVGNumber : false, + SVGNumberList : false, + SVGPaint : false, + SVGPathElement : false, + SVGPathSeg : false, + SVGPathSegArcAbs : false, + SVGPathSegArcRel : false, + SVGPathSegClosePath : false, + SVGPathSegCurvetoCubicAbs: false, + SVGPathSegCurvetoCubicRel: false, + SVGPathSegCurvetoCubicSmoothAbs: false, + SVGPathSegCurvetoCubicSmoothRel: false, + SVGPathSegCurvetoQuadraticAbs: false, + SVGPathSegCurvetoQuadraticRel: false, + SVGPathSegCurvetoQuadraticSmoothAbs: false, + SVGPathSegCurvetoQuadraticSmoothRel: false, + SVGPathSegLinetoAbs : false, + SVGPathSegLinetoHorizontalAbs: false, + SVGPathSegLinetoHorizontalRel: false, + SVGPathSegLinetoRel : false, + SVGPathSegLinetoVerticalAbs: false, + SVGPathSegLinetoVerticalRel: false, + SVGPathSegList : false, + SVGPathSegMovetoAbs : false, + SVGPathSegMovetoRel : false, + SVGPatternElement : false, + SVGPoint : false, + SVGPointList : false, + SVGPolygonElement : false, + SVGPolylineElement : false, + SVGPreserveAspectRatio: false, + SVGRadialGradientElement: false, + SVGRect : false, + SVGRectElement : false, + SVGRenderingIntent : false, + SVGSVGElement : false, + SVGScriptElement : false, + SVGSetElement : false, + SVGStopElement : false, + SVGStringList : false, + SVGStylable : false, + SVGStyleElement : false, + SVGSwitchElement : false, + SVGSymbolElement : false, + SVGTRefElement : false, + SVGTSpanElement : false, + SVGTests : false, + SVGTextContentElement: false, + SVGTextElement : false, + SVGTextPathElement : false, + SVGTextPositioningElement: false, + SVGTitleElement : false, + SVGTransform : false, + SVGTransformList : false, + SVGTransformable : false, + SVGURIReference : false, + SVGUnitTypes : false, + SVGUseElement : false, + SVGVKernElement : false, + SVGViewElement : false, + SVGViewSpec : false, + SVGZoomAndPan : false, + Text : false, + TextDecoder : false, + TextEncoder : false, + TimeEvent : false, + top : false, + URL : false, + WebGLActiveInfo : false, + WebGLBuffer : false, + WebGLContextEvent : false, + WebGLFramebuffer : false, + WebGLProgram : false, + WebGLRenderbuffer : false, + WebGLRenderingContext: false, + WebGLShader : false, + WebGLShaderPrecisionFormat: false, + WebGLTexture : false, + WebGLUniformLocation : false, + WebSocket : false, + window : false, + Worker : false, + XDomainRequest : false, + XMLHttpRequest : false, + XMLSerializer : false, + XPathEvaluator : false, + XPathException : false, + XPathExpression : false, + XPathNamespace : false, + XPathNSResolver : false, + XPathResult : false +}; + +exports.devel = { + alert : false, + confirm: false, + console: false, + Debug : false, + opera : false, + prompt : false +}; + +exports.worker = { + importScripts : true, + postMessage : true, + self : true, + FileReaderSync : true +}; + +// Widely adopted global names that are not part of ECMAScript standard +exports.nonstandard = { + escape : false, + unescape: false +}; + +// Globals provided by popular JavaScript environments. + +exports.couch = { + "require" : false, + respond : false, + getRow : false, + emit : false, + send : false, + start : false, + sum : false, + log : false, + exports : false, + module : false, + provides : false +}; + +exports.node = { + __filename : false, + __dirname : false, + GLOBAL : false, + global : false, + module : false, + require : false, + + // These globals are writeable because Node allows the following + // usage pattern: var Buffer = require("buffer").Buffer; + + Buffer : true, + console : true, + exports : true, + process : true, + setTimeout : true, + clearTimeout : true, + setInterval : true, + clearInterval : true, + setImmediate : true, // v0.9.1+ + clearImmediate: true // v0.9.1+ +}; + +exports.browserify = { + __filename : false, + __dirname : false, + global : false, + module : false, + require : false, + Buffer : true, + exports : true, + process : true +}; + +exports.phantom = { + phantom : true, + require : true, + WebPage : true, + console : true, // in examples, but undocumented + exports : true // v1.7+ +}; + +exports.qunit = { + asyncTest : false, + deepEqual : false, + equal : false, + expect : false, + module : false, + notDeepEqual : false, + notEqual : false, + notPropEqual : false, + notStrictEqual : false, + ok : false, + propEqual : false, + QUnit : false, + raises : false, + start : false, + stop : false, + strictEqual : false, + test : false, + "throws" : false +}; + +exports.rhino = { + defineClass : false, + deserialize : false, + gc : false, + help : false, + importClass : false, + importPackage: false, + "java" : false, + load : false, + loadClass : false, + Packages : false, + print : false, + quit : false, + readFile : false, + readUrl : false, + runCommand : false, + seal : false, + serialize : false, + spawn : false, + sync : false, + toint32 : false, + version : false +}; + +exports.shelljs = { + target : false, + echo : false, + exit : false, + cd : false, + pwd : false, + ls : false, + find : false, + cp : false, + rm : false, + mv : false, + mkdir : false, + test : false, + cat : false, + sed : false, + grep : false, + which : false, + dirs : false, + pushd : false, + popd : false, + env : false, + exec : false, + chmod : false, + config : false, + error : false, + tempdir : false +}; + +exports.typed = { + ArrayBuffer : false, + ArrayBufferView : false, + DataView : false, + Float32Array : false, + Float64Array : false, + Int16Array : false, + Int32Array : false, + Int8Array : false, + Uint16Array : false, + Uint32Array : false, + Uint8Array : false, + Uint8ClampedArray : false +}; + +exports.wsh = { + ActiveXObject : true, + Enumerator : true, + GetObject : true, + ScriptEngine : true, + ScriptEngineBuildVersion : true, + ScriptEngineMajorVersion : true, + ScriptEngineMinorVersion : true, + VBArray : true, + WSH : true, + WScript : true, + XDomainRequest : true +}; + +// Globals provided by popular JavaScript libraries. + +exports.dojo = { + dojo : false, + dijit : false, + dojox : false, + define : false, + "require": false +}; + +exports.jquery = { + "$" : false, + jQuery : false +}; + +exports.mootools = { + "$" : false, + "$$" : false, + Asset : false, + Browser : false, + Chain : false, + Class : false, + Color : false, + Cookie : false, + Core : false, + Document : false, + DomReady : false, + DOMEvent : false, + DOMReady : false, + Drag : false, + Element : false, + Elements : false, + Event : false, + Events : false, + Fx : false, + Group : false, + Hash : false, + HtmlTable : false, + IFrame : false, + IframeShim : false, + InputValidator: false, + instanceOf : false, + Keyboard : false, + Locale : false, + Mask : false, + MooTools : false, + Native : false, + Options : false, + OverText : false, + Request : false, + Scroller : false, + Slick : false, + Slider : false, + Sortables : false, + Spinner : false, + Swiff : false, + Tips : false, + Type : false, + typeOf : false, + URI : false, + Window : false +}; + +exports.prototypejs = { + "$" : false, + "$$" : false, + "$A" : false, + "$F" : false, + "$H" : false, + "$R" : false, + "$break" : false, + "$continue" : false, + "$w" : false, + Abstract : false, + Ajax : false, + Class : false, + Enumerable : false, + Element : false, + Event : false, + Field : false, + Form : false, + Hash : false, + Insertion : false, + ObjectRange : false, + PeriodicalExecuter: false, + Position : false, + Prototype : false, + Selector : false, + Template : false, + Toggle : false, + Try : false, + Autocompleter : false, + Builder : false, + Control : false, + Draggable : false, + Draggables : false, + Droppables : false, + Effect : false, + Sortable : false, + SortableObserver : false, + Sound : false, + Scriptaculous : false +}; + +exports.yui = { + YUI : false, + Y : false, + YUI_config: false +}; + +exports.mocha = { + // BDD + describe : false, + it : false, + before : false, + after : false, + beforeEach : false, + afterEach : false, + // TDD + suite : false, + test : false, + setup : false, + teardown : false, + suiteSetup : false, + suiteTeardown : false +}; + +exports.jasmine = { + jasmine : false, + describe : false, + it : false, + xit : false, + beforeEach : false, + afterEach : false, + setFixtures : false, + loadFixtures: false, + spyOn : false, + expect : false, + // Jasmine 1.3 + runs : false, + waitsFor : false, + waits : false, + // Jasmine 2.1 + beforeAll : false, + afterAll : false, + fail : false, + fdescribe : false, + fit : false +}; + +},{}],"jshint":[function(require,module,exports){ /*! * JSHint, by JSHint Community. * @@ -52774,6 +6759,7 @@ var Lexer = require("./lex.js").Lexer; var reg = require("./reg.js"); var state = require("./state.js").state; var style = require("./style.js"); +var options = require("./options.js"); // We need this module here because environments such as IE and Rhino // don't necessarilly expose the 'console' API and browserify uses @@ -52784,7745 +6770,5278 @@ var console = require("console-browserify"); // variable. That function will be invoked immediately, and its return value is // the JSHINT function itself. -var JSHINT = (function () { - "use strict"; +var JSHINT = (function() { + "use strict"; - var anonname, // The guessed name for anonymous functions. - api, // Extension API + var api, // Extension API - // These are operators that should not be used with the ! operator. - bang = { - "<" : true, - "<=" : true, - "==" : true, - "===": true, - "!==": true, - "!=" : true, - ">" : true, - ">=" : true, - "+" : true, - "-" : true, - "*" : true, - "/" : true, - "%" : true - }, + // These are operators that should not be used with the ! operator. + bang = { + "<" : true, + "<=" : true, + "==" : true, + "===": true, + "!==": true, + "!=" : true, + ">" : true, + ">=" : true, + "+" : true, + "-" : true, + "*" : true, + "/" : true, + "%" : true + }, - // These are the JSHint boolean options. - boolOptions = { - asi : true, // if automatic semicolon insertion should be tolerated - bitwise : true, // if bitwise operators should not be allowed - boss : true, // if advanced usage of assignments should be allowed - browser : true, // if the standard browser globals should be predefined - camelcase : true, // if identifiers should be required in camel case - couch : true, // if CouchDB globals should be predefined - curly : true, // if curly braces around all blocks should be required - debug : true, // if debugger statements should be allowed - devel : true, // if logging globals should be predefined (console, alert, etc.) - dojo : true, // if Dojo Toolkit globals should be predefined - eqeqeq : true, // if === should be required - eqnull : true, // if == null comparisons should be tolerated - notypeof : true, // if should report typos in typeof comparisons - es3 : true, // if ES3 syntax should be allowed - es5 : true, // if ES5 syntax should be allowed (is now set per default) - esnext : true, // if es.next specific syntax should be allowed - moz : true, // if mozilla specific syntax should be allowed - evil : true, // if eval should be allowed - expr : true, // if ExpressionStatement should be allowed as Programs - forin : true, // if for in statements must filter - funcscope : true, // if only function scope should be used for scope tests - gcl : true, // if JSHint should be compatible with Google Closure Linter - globalstrict: true, // if global "use strict"; should be allowed (also enables 'strict') - immed : true, // if immediate invocations must be wrapped in parens - iterator : true, // if the `__iterator__` property should be allowed - jquery : true, // if jQuery globals should be predefined - lastsemic : true, // if semicolons may be ommitted for the trailing - // statements inside of a one-line blocks. - laxbreak : true, // if line breaks should not be checked - laxcomma : true, // if line breaks should not be checked around commas - loopfunc : true, // if functions should be allowed to be defined within - // loops - mootools : true, // if MooTools globals should be predefined - multistr : true, // allow multiline strings - freeze : true, // if modifying native object prototypes should be disallowed - newcap : true, // if constructor names must be capitalized - noarg : true, // if arguments.caller and arguments.callee should be - // disallowed - node : true, // if the Node.js environment globals should be - // predefined - noempty : true, // if empty blocks should be disallowed - nonew : true, // if using `new` for side-effects should be disallowed - nonstandard : true, // if non-standard (but widely adopted) globals should - // be predefined - nomen : true, // if names should be checked - onevar : true, // if only one var statement per function should be - // allowed - passfail : true, // if the scan should stop on first error - phantom : true, // if PhantomJS symbols should be allowed - plusplus : true, // if increment/decrement should not be allowed - proto : true, // if the `__proto__` property should be allowed - prototypejs : true, // if Prototype and Scriptaculous globals should be - // predefined - rhino : true, // if the Rhino environment globals should be predefined - shelljs : true, // if ShellJS globals should be predefined - typed : true, // if typed array globals should be predefined - undef : true, // if variables should be declared before used - scripturl : true, // if script-targeted URLs should be tolerated - smarttabs : true, // if smarttabs should be tolerated - // (http://www.emacswiki.org/emacs/SmartTabs) - strict : true, // require the "use strict"; pragma - sub : true, // if all forms of subscript notation are tolerated - supernew : true, // if `new function () { ... };` and `new Object;` - // should be tolerated - trailing : true, // if trailing whitespace rules apply - validthis : true, // if 'this' inside a non-constructor function is valid. - // This is a function scoped option only. - withstmt : true, // if with statements should be allowed - white : true, // if strict whitespace rules apply - worker : true, // if Web Worker script symbols should be allowed - wsh : true, // if the Windows Scripting Host environment globals - // should be predefined - yui : true, // YUI variables should be predefined + declared, // Globals that were declared using /*global ... */ syntax. + exported, // Variables that are used outside of the current file. - // Obsolete options - onecase : true, // if one case switch statements should be allowed - regexp : true, // if the . should not be allowed in regexp literals - regexdash : true // if unescaped first/last dash (-) inside brackets - // should be tolerated - }, + functionicity = [ + "closure", "exception", "global", "label", + "outer", "unused", "var" + ], - // These are the JSHint options that can take any value - // (we use this object to detect invalid options) - valOptions = { - maxlen : false, - indent : false, - maxerr : false, - predef : false, //predef is deprecated and being replaced by globals - globals : false, - quotmark : false, //'single'|'double'|true - scope : false, - maxstatements: false, // {int} max statements per function - maxdepth : false, // {int} max nested block depth per function - maxparams : false, // {int} max params per function - maxcomplexity: false, // {int} max cyclomatic complexity per function - shadow : false, // if variable shadowing should be tolerated - // "inner" - check for variables defined in the same scope only - // "outer" - check for variables defined in outer scopes as well - // false - same as inner - // true - allow variable shadowing - unused : true, // warn if variables are unused. Available options: - // false - don't check for unused variables - // true - "vars" + check last function param - // "vars" - skip checking unused function params - // "strict" - "vars" + check all function params - latedef : false, // warn if the variable is used before its definition - // false - don't emit any warnings - // true - warn if any variable is used before its definition - // "nofunc" - warn for any variable but function declarations - ignore : false // start/end ignoring lines of code, bypassing the lexer - // start - start ignoring lines, including the current line - // end - stop ignoring lines, starting on the next line - // line - ignore warnings / errors for just a single line - // (this option does not bypass the lexer) - }, + funct, // The current function + functions, // All of the functions - // These are JSHint boolean options which are shared with JSLint - // where the definition in JSHint is opposite JSLint - invertedOptions = { - bitwise : true, - forin : true, - newcap : true, - nomen : true, - plusplus: true, - regexp : true, - undef : true, - white : true, + global, // The global scope + implied, // Implied globals + inblock, + indent, + lookahead, + lex, + member, + membersOnly, + predefined, // Global variables defined by option - // Inverted and renamed, use JSHint name here - eqeqeq : true, - onevar : true, - strict : true - }, + scope, // The current scope + stack, + unuseds, + urls, - // These are JSHint boolean options which are shared with JSLint - // where the name has been changed but the effect is unchanged - renamedOptions = { - eqeq : "eqeqeq", - vars : "onevar", - windows: "wsh", - sloppy : "strict" - }, + extraModules = [], + emitter = new events.EventEmitter(); - declared, // Globals that were declared using /*global ... */ syntax. - exported, // Variables that are used outside of the current file. + function checkOption(name, t) { + name = name.trim(); - functionicity = [ - "closure", "exception", "global", "label", - "outer", "unused", "var" - ], - - funct, // The current function - functions, // All of the functions - - global, // The global scope - implied, // Implied globals - inblock, - indent, - lookahead, - lex, - member, - membersOnly, - noreach, - predefined, // Global variables defined by option - - scope, // The current scope - stack, - unuseds, - urls, - warnings, - - extraModules = [], - emitter = new events.EventEmitter(); - - function checkOption(name, t) { - name = name.trim(); - - if (/^[+-]W\d{3}$/g.test(name)) { - return true; - } - - if (valOptions[name] === undefined && boolOptions[name] === undefined) { - if (t.type !== "jslint") { - error("E001", t, name); - return false; - } - } - - return true; + if (/^[+-]W\d{3}$/g.test(name)) { + return true; } - function isString(obj) { - return Object.prototype.toString.call(obj) === "[object String]"; - } - - function isIdentifier(tkn, value) { - if (!tkn) - return false; - - if (!tkn.identifier || tkn.value !== value) - return false; - - return true; - } - - function isReserved(token) { - if (!token.reserved) { - return false; - } - var meta = token.meta; - - if (meta && meta.isFutureReservedWord && state.option.inES5()) { - // ES3 FutureReservedWord in an ES5 environment. - if (!meta.es5) { - return false; - } - - // Some ES5 FutureReservedWord identifiers are active only - // within a strict mode environment. - if (meta.strictOnly) { - if (!state.option.strict && !state.directive["use strict"]) { - return false; - } - } - - if (token.isProperty) { - return false; - } - } - - return true; - } - - function supplant(str, data) { - return str.replace(/\{([^{}]*)\}/g, function (a, b) { - var r = data[b]; - return typeof r === "string" || typeof r === "number" ? r : a; - }); - } - - function combine(dest, src) { - Object.keys(src).forEach(function (name) { - if (JSHINT.blacklist.hasOwnProperty(name)) return; - dest[name] = src[name]; - }); - } - - function assume() { - if (state.option.es5) { - warning("I003"); - } - if (state.option.couch) { - combine(predefined, vars.couch); - } - - if (state.option.rhino) { - combine(predefined, vars.rhino); - } - - if (state.option.shelljs) { - combine(predefined, vars.shelljs); - combine(predefined, vars.node); - } - if (state.option.typed) { - combine(predefined, vars.typed); - } - - if (state.option.phantom) { - combine(predefined, vars.phantom); - } - - if (state.option.prototypejs) { - combine(predefined, vars.prototypejs); - } - - if (state.option.node) { - combine(predefined, vars.node); - combine(predefined, vars.typed); - } - - if (state.option.devel) { - combine(predefined, vars.devel); - } - - if (state.option.dojo) { - combine(predefined, vars.dojo); - } - - if (state.option.browser) { - combine(predefined, vars.browser); - combine(predefined, vars.typed); - } - - if (state.option.nonstandard) { - combine(predefined, vars.nonstandard); - } - - if (state.option.jquery) { - combine(predefined, vars.jquery); - } - - if (state.option.mootools) { - combine(predefined, vars.mootools); - } - - if (state.option.worker) { - combine(predefined, vars.worker); - } - - if (state.option.wsh) { - combine(predefined, vars.wsh); - } - - if (state.option.globalstrict && state.option.strict !== false) { - state.option.strict = true; - } - - if (state.option.yui) { - combine(predefined, vars.yui); - } - - // Let's assume that chronologically ES3 < ES5 < ES6/ESNext < Moz - - state.option.inMoz = function (strict) { - if (strict) { - return state.option.moz && !state.option.esnext; - } - return state.option.moz; - }; - - state.option.inESNext = function (strict) { - if (strict) { - return !state.option.moz && state.option.esnext; - } - return state.option.moz || state.option.esnext; - }; - - state.option.inES5 = function (/* strict */) { - return !state.option.es3; - }; - - state.option.inES3 = function (strict) { - if (strict) { - return !state.option.moz && !state.option.esnext && state.option.es3; - } - return state.option.es3; - }; - } - - // Produce an error warning. - function quit(code, line, chr) { - var percentage = Math.floor((line / state.lines.length) * 100); - var message = messages.errors[code].desc; - - throw { - name: "JSHintError", - line: line, - character: chr, - message: message + " (" + percentage + "% scanned).", - raw: message, - code: code - }; - } - - function isundef(scope, code, token, a) { - return JSHINT.undefs.push([scope, code, token, a]); - } - - function warning(code, t, a, b, c, d) { - var ch, l, w, msg; - - if (/^W\d{3}$/.test(code)) { - if (state.ignored[code]) - return; - - msg = messages.warnings[code]; - } else if (/E\d{3}/.test(code)) { - msg = messages.errors[code]; - } else if (/I\d{3}/.test(code)) { - msg = messages.info[code]; - } - - t = t || state.tokens.next; - if (t.id === "(end)") { // `~ - t = state.tokens.curr; - } - - l = t.line || 0; - ch = t.from || 0; - - w = { - id: "(error)", - raw: msg.desc, - code: msg.code, - evidence: state.lines[l - 1] || "", - line: l, - character: ch, - scope: JSHINT.scope, - a: a, - b: b, - c: c, - d: d - }; - - w.reason = supplant(msg.desc, w); - JSHINT.errors.push(w); - - if (state.option.passfail) { - quit("E042", l, ch); - } - - warnings += 1; - if (warnings >= state.option.maxerr) { - quit("E043", l, ch); - } - - return w; - } - - function warningAt(m, l, ch, a, b, c, d) { - return warning(m, { - line: l, - from: ch - }, a, b, c, d); - } - - function error(m, t, a, b, c, d) { - warning(m, t, a, b, c, d); - } - - function errorAt(m, l, ch, a, b, c, d) { - return error(m, { - line: l, - from: ch - }, a, b, c, d); - } - - // Tracking of "internal" scripts, like eval containing a static string - function addInternalSrc(elem, src) { - var i; - i = { - id: "(internal)", - elem: elem, - value: src - }; - JSHINT.internals.push(i); - return i; - } - - // name: string - // opts: { type: string, token: token, islet: bool } - function addlabel(name, opts) { - opts = opts || {}; - - var type = opts.type; - var token = opts.token; - var islet = opts.islet; - - // Define label in the current function in the current scope. - if (type === "exception") { - if (_.has(funct["(context)"], name)) { - if (funct[name] !== true && !state.option.node) { - warning("W002", state.tokens.next, name); - } - } - } - - if (_.has(funct, name) && !funct["(global)"]) { - if (funct[name] === true) { - if (state.option.latedef) { - if ((state.option.latedef === true && _.contains([funct[name], type], "unction")) || - !_.contains([funct[name], type], "unction")) { - warning("W003", state.tokens.next, name); - } - } - } else { - if ((!state.option.shadow || _.contains([ "inner", "outer" ], state.option.shadow)) && - type !== "exception" || funct["(blockscope)"].getlabel(name)) { - warning("W004", state.tokens.next, name); - } - } - } - - if (funct["(context)"] && _.has(funct["(context)"], name) && type !== "function") { - if (state.option.shadow === "outer") { - warning("W123", state.tokens.next, name); - } - } - - // if the identifier is from a let, adds it only to the current blockscope - if (islet) { - funct["(blockscope)"].current.add(name, type, state.tokens.curr); - } else { - funct["(blockscope)"].shadow(name); - funct[name] = type; - - if (token) { - funct["(tokens)"][name] = token; - } - - setprop(funct, name, { unused: opts.unused || false }); - - if (funct["(global)"]) { - global[name] = funct; - if (_.has(implied, name)) { - if (state.option.latedef) { - if ((state.option.latedef === true && _.contains([funct[name], type], "unction")) || - !_.contains([funct[name], type], "unction")) { - warning("W003", state.tokens.next, name); - } - } - - delete implied[name]; - } - } else { - scope[name] = funct; - } - } - } - - function doOption() { - var nt = state.tokens.next; - var body = nt.body.split(",").map(function (s) { return s.trim(); }); - var predef = {}; - - if (nt.type === "globals") { - body.forEach(function (g) { - g = g.split(":"); - var key = (g[0] || "").trim(); - var val = (g[1] || "").trim(); - - if (key.charAt(0) === "-") { - key = key.slice(1); - val = false; - - JSHINT.blacklist[key] = key; - delete predefined[key]; - } else { - predef[key] = (val === "true"); - } - }); - - combine(predefined, predef); - - for (var key in predef) { - if (_.has(predef, key)) { - declared[key] = nt; - } - } - } - - if (nt.type === "exported") { - body.forEach(function (e) { - exported[e] = true; - }); - } - - if (nt.type === "members") { - membersOnly = membersOnly || {}; - - body.forEach(function (m) { - var ch1 = m.charAt(0); - var ch2 = m.charAt(m.length - 1); - - if (ch1 === ch2 && (ch1 === "\"" || ch1 === "'")) { - m = m - .substr(1, m.length - 2) - .replace("\\\"", "\""); - } - - membersOnly[m] = false; - }); - } - - var numvals = [ - "maxstatements", - "maxparams", - "maxdepth", - "maxcomplexity", - "maxerr", - "maxlen", - "indent" - ]; - - if (nt.type === "jshint" || nt.type === "jslint") { - body.forEach(function (g) { - g = g.split(":"); - var key = (g[0] || "").trim(); - var val = (g[1] || "").trim(); - - if (!checkOption(key, nt)) { - return; - } - - if (numvals.indexOf(key) >= 0) { - - // GH988 - numeric options can be disabled by setting them to `false` - if (val !== "false") { - val = +val; - - if (typeof val !== "number" || !isFinite(val) || val <= 0 || Math.floor(val) !== val) { - error("E032", nt, g[1].trim()); - return; - } - - if (key === "indent") { - state.option["(explicitIndent)"] = true; - } - state.option[key] = val; - } else { - if (key === "indent") { - state.option["(explicitIndent)"] = false; - } else { - state.option[key] = false; - } - } - - return; - } - - if (key === "validthis") { - // `validthis` is valid only within a function scope. - - if (funct["(global)"]) - return void error("E009"); - - if (val !== "true" && val !== "false") - return void error("E002", nt); - - state.option.validthis = (val === "true"); - return; - } - - if (key === "quotmark") { - switch (val) { - case "true": - case "false": - state.option.quotmark = (val === "true"); - break; - case "double": - case "single": - state.option.quotmark = val; - break; - default: - error("E002", nt); - } - return; - } - - if (key === "shadow") { - switch (val) { - case "true": - state.option.shadow = true; - break; - case "outer": - state.option.shadow = "outer"; - break; - case "false": - case "inner": - state.option.shadow = "inner"; - break; - default: - error("E002", nt); - } - return; - } - - if (key === "unused") { - switch (val) { - case "true": - state.option.unused = true; - break; - case "false": - state.option.unused = false; - break; - case "vars": - case "strict": - state.option.unused = val; - break; - default: - error("E002", nt); - } - return; - } - - if (key === "latedef") { - switch (val) { - case "true": - state.option.latedef = true; - break; - case "false": - state.option.latedef = false; - break; - case "nofunc": - state.option.latedef = "nofunc"; - break; - default: - error("E002", nt); - } - return; - } - - if (key === "ignore") { - switch (val) { - case "start": - state.ignoreLinterErrors = true; - break; - case "end": - state.ignoreLinterErrors = false; - break; - case "line": - // Any errors or warnings that happened on the current line, make them go away. - JSHINT.errors = _.reject(JSHINT.errors, function (error) { - // nt.line returns to the current line - return error.line === nt.line; - }); - break; - default: - error("E002", nt); - } - return; - } - - var match = /^([+-])(W\d{3})$/g.exec(key); - if (match) { - // ignore for -W..., unignore for +W... - state.ignored[match[2]] = (match[1] === "-"); - return; - } - - var tn; - if (val === "true" || val === "false") { - if (nt.type === "jslint") { - tn = renamedOptions[key] || key; - state.option[tn] = (val === "true"); - - if (invertedOptions[tn] !== undefined) { - state.option[tn] = !state.option[tn]; - } - } else { - state.option[key] = (val === "true"); - } - - if (key === "newcap") { - state.option["(explicitNewcap)"] = true; - } - return; - } - - error("E002", nt); - }); - - assume(); - } - } - - // We need a peek function. If it has an argument, it peeks that much farther - // ahead. It is used to distinguish - // for ( var i in ... - // from - // for ( var i = ... - - function peek(p) { - var i = p || 0, j = 0, t; - - while (j <= i) { - t = lookahead[j]; - if (!t) { - t = lookahead[j] = lex.token(); - } - j += 1; - } - return t; - } - - // Produce the next token. It looks for programming errors. - - function advance(id, t) { - switch (state.tokens.curr.id) { - case "(number)": - if (state.tokens.next.id === ".") { - warning("W005", state.tokens.curr); - } - break; - case "-": - if (state.tokens.next.id === "-" || state.tokens.next.id === "--") { - warning("W006"); - } - break; - case "+": - if (state.tokens.next.id === "+" || state.tokens.next.id === "++") { - warning("W007"); - } - break; - } - - if (state.tokens.curr.type === "(string)" || state.tokens.curr.identifier) { - anonname = state.tokens.curr.value; - } - - if (id && state.tokens.next.id !== id) { - if (t) { - if (state.tokens.next.id === "(end)") { - error("E019", t, t.id); - } else { - error("E020", state.tokens.next, id, t.id, t.line, state.tokens.next.value); - } - } else if (state.tokens.next.type !== "(identifier)" || state.tokens.next.value !== id) { - warning("W116", state.tokens.next, id, state.tokens.next.value); - } - } - - state.tokens.prev = state.tokens.curr; - state.tokens.curr = state.tokens.next; - for (;;) { - state.tokens.next = lookahead.shift() || lex.token(); - - if (!state.tokens.next) { // No more tokens left, give up - quit("E041", state.tokens.curr.line); - } - - if (state.tokens.next.id === "(end)" || state.tokens.next.id === "(error)") { - return; - } - - if (state.tokens.next.check) { - state.tokens.next.check(); - } - - if (state.tokens.next.isSpecial) { - doOption(); - } else { - if (state.tokens.next.id !== "(endline)") { - break; - } - } - } - } - - function isInfix(token) { - return token.infix || (!token.identifier && !!token.led); - } - - function isEndOfExpr() { - var curr = state.tokens.curr; - var next = state.tokens.next; - if (next.id === ";" || next.id === "}" || next.id === ":") { - return true; - } - if (isInfix(next) === isInfix(curr) || (curr.id === "yield" && state.option.inMoz(true))) { - return curr.line !== next.line; - } + if (options.validNames.indexOf(name) === -1) { + if (t.type !== "jslint" && !_.has(options.removed, name)) { + error("E001", t, name); return false; + } } - // This is the heart of JSHINT, the Pratt parser. In addition to parsing, it - // is looking for ad hoc lint patterns. We add .fud to Pratt's model, which is - // like .nud except that it is only used on the first token of a statement. - // Having .fud makes it much easier to define statement-oriented languages like - // JavaScript. I retained Pratt's nomenclature. + return true; + } - // .nud Null denotation - // .fud First null denotation - // .led Left denotation - // lbp Left binding power - // rbp Right binding power + function isString(obj) { + return Object.prototype.toString.call(obj) === "[object String]"; + } - // They are elements of the parsing method called Top Down Operator Precedence. + function isIdentifier(tkn, value) { + if (!tkn) + return false; - function expression(rbp, initial) { - var left, isArray = false, isObject = false, isLetExpr = false; + if (!tkn.identifier || tkn.value !== value) + return false; - // if current expression is a let expression - if (!initial && state.tokens.next.value === "let" && peek(0).value === "(") { - if (!state.option.inMoz(true)) { - warning("W118", state.tokens.next, "let expressions"); + return true; + } + + function isReserved(token) { + if (!token.reserved) { + return false; + } + var meta = token.meta; + + if (meta && meta.isFutureReservedWord && state.option.inES5()) { + // ES3 FutureReservedWord in an ES5 environment. + if (!meta.es5) { + return false; + } + + // Some ES5 FutureReservedWord identifiers are active only + // within a strict mode environment. + if (meta.strictOnly) { + if (!state.option.strict && !state.directive["use strict"]) { + return false; + } + } + + if (token.isProperty) { + return false; + } + } + + return true; + } + + function supplant(str, data) { + return str.replace(/\{([^{}]*)\}/g, function(a, b) { + var r = data[b]; + return typeof r === "string" || typeof r === "number" ? r : a; + }); + } + + function combine(dest, src) { + Object.keys(src).forEach(function(name) { + if (_.has(JSHINT.blacklist, name)) return; + dest[name] = src[name]; + }); + } + + function processenforceall() { + if (state.option.enforceall) { + for (var enforceopt in options.bool.enforcing) { + if (state.option[enforceopt] === undefined) { + state.option[enforceopt] = true; + } + } + for (var relaxopt in options.bool.relaxing) { + if (state.option[relaxopt] === undefined) { + state.option[relaxopt] = false; + } + } + } + } + + function assume() { + if (state.option.es5) { + warning("I003"); + } + + processenforceall(); + + if (state.option.esnext) { + combine(predefined, vars.newEcmaIdentifiers); + } + + if (state.option.couch) { + combine(predefined, vars.couch); + } + + if (state.option.qunit) { + combine(predefined, vars.qunit); + } + + if (state.option.rhino) { + combine(predefined, vars.rhino); + } + + if (state.option.shelljs) { + combine(predefined, vars.shelljs); + combine(predefined, vars.node); + } + if (state.option.typed) { + combine(predefined, vars.typed); + } + + if (state.option.phantom) { + combine(predefined, vars.phantom); + } + + if (state.option.prototypejs) { + combine(predefined, vars.prototypejs); + } + + if (state.option.node) { + combine(predefined, vars.node); + combine(predefined, vars.typed); + } + + if (state.option.devel) { + combine(predefined, vars.devel); + } + + if (state.option.dojo) { + combine(predefined, vars.dojo); + } + + if (state.option.browser) { + combine(predefined, vars.browser); + combine(predefined, vars.typed); + } + + if (state.option.browserify) { + combine(predefined, vars.browser); + combine(predefined, vars.typed); + combine(predefined, vars.browserify); + } + + if (state.option.nonstandard) { + combine(predefined, vars.nonstandard); + } + + if (state.option.jasmine) { + combine(predefined, vars.jasmine); + } + + if (state.option.jquery) { + combine(predefined, vars.jquery); + } + + if (state.option.mootools) { + combine(predefined, vars.mootools); + } + + if (state.option.worker) { + combine(predefined, vars.worker); + } + + if (state.option.wsh) { + combine(predefined, vars.wsh); + } + + if (state.option.globalstrict && state.option.strict !== false) { + state.option.strict = true; + } + + if (state.option.yui) { + combine(predefined, vars.yui); + } + + if (state.option.mocha) { + combine(predefined, vars.mocha); + } + + // Let's assume that chronologically ES3 < ES5 < ES6/ESNext < Moz + + state.option.inMoz = function(strict) { + if (strict) { + return state.option.moz && !state.option.esnext; + } + return state.option.moz; + }; + + state.option.inESNext = function(strict) { + if (strict) { + return !state.option.moz && state.option.esnext; + } + return state.option.moz || state.option.esnext; + }; + + state.option.inES5 = function(/* strict */) { + return !state.option.es3; + }; + + state.option.inES3 = function(strict) { + if (strict) { + return !state.option.moz && !state.option.esnext && state.option.es3; + } + return state.option.es3; + }; + } + + // Produce an error warning. + function quit(code, line, chr) { + var percentage = Math.floor((line / state.lines.length) * 100); + var message = messages.errors[code].desc; + + throw { + name: "JSHintError", + line: line, + character: chr, + message: message + " (" + percentage + "% scanned).", + raw: message, + code: code + }; + } + + function isundef(scope, code, token, a) { + return JSHINT.undefs.push([scope, code, token, a]); + } + + function removeIgnoredMessages() { + var ignored = state.ignoredLines; + + if (_.isEmpty(ignored)) return; + JSHINT.errors = _.reject(JSHINT.errors, function(err) { return ignored[err.line] }); + } + + function warning(code, t, a, b, c, d) { + var ch, l, w, msg; + + if (/^W\d{3}$/.test(code)) { + if (state.ignored[code]) + return; + + msg = messages.warnings[code]; + } else if (/E\d{3}/.test(code)) { + msg = messages.errors[code]; + } else if (/I\d{3}/.test(code)) { + msg = messages.info[code]; + } + + t = t || state.tokens.next; + if (t.id === "(end)") { // `~ + t = state.tokens.curr; + } + + l = t.line || 0; + ch = t.from || 0; + + w = { + id: "(error)", + raw: msg.desc, + code: msg.code, + evidence: state.lines[l - 1] || "", + line: l, + character: ch, + scope: JSHINT.scope, + a: a, + b: b, + c: c, + d: d + }; + + w.reason = supplant(msg.desc, w); + JSHINT.errors.push(w); + + removeIgnoredMessages(); + + if (JSHINT.errors.length >= state.option.maxerr) + quit("E043", l, ch); + + return w; + } + + function warningAt(m, l, ch, a, b, c, d) { + return warning(m, { + line: l, + from: ch + }, a, b, c, d); + } + + function error(m, t, a, b, c, d) { + warning(m, t, a, b, c, d); + } + + function errorAt(m, l, ch, a, b, c, d) { + return error(m, { + line: l, + from: ch + }, a, b, c, d); + } + + // Tracking of "internal" scripts, like eval containing a static string + function addInternalSrc(elem, src) { + var i; + i = { + id: "(internal)", + elem: elem, + value: src + }; + JSHINT.internals.push(i); + return i; + } + + // name: string + // opts: { type: string, token: token, islet: bool } + function addlabel(name, opts) { + opts = opts || {}; + + var type = opts.type; + var token = opts.token; + var islet = opts.islet; + + // Define label in the current function in the current scope. + if (type === "exception") { + if (_.has(funct["(context)"], name)) { + if (funct[name] !== true && !state.option.node) { + warning("W002", state.tokens.next, name); + } + } + } + + if (_.has(funct, name) && !funct["(global)"]) { + if (funct[name] === true) { + if (state.option.latedef) { + if ((state.option.latedef === true && _.contains([funct[name], type], "unction")) || + !_.contains([funct[name], type], "unction")) { + warning("W003", state.tokens.next, name); + } + } + } else { + if ((!state.option.shadow || _.contains([ "inner", "outer" ], state.option.shadow)) && + type !== "exception" || funct["(blockscope)"].getlabel(name)) { + warning("W004", state.tokens.next, name); + } + } + } + + if (funct["(context)"] && _.has(funct["(context)"], name) && type !== "function") { + if (state.option.shadow === "outer") { + warning("W123", state.tokens.next, name); + } + } + + // if the identifier is from a let, adds it only to the current blockscope + if (islet) { + funct["(blockscope)"].current.add(name, type, state.tokens.curr); + if (funct["(blockscope)"].atTop() && exported[name]) { + state.tokens.curr.exported = true; + } + } else { + funct["(blockscope)"].shadow(name); + funct[name] = type; + + if (token) { + funct["(tokens)"][name] = token; + } + + setprop(funct, name, { unused: opts.unused || false }); + + if (funct["(global)"]) { + global[name] = funct; + if (_.has(implied, name)) { + if (state.option.latedef) { + if ((state.option.latedef === true && _.contains([funct[name], type], "unction")) || + !_.contains([funct[name], type], "unction")) { + warning("W003", state.tokens.next, name); } - isLetExpr = true; - // create a new block scope we use only for the current expression - funct["(blockscope)"].stack(); - advance("let"); - advance("("); - state.syntax["let"].fud.call(state.syntax["let"].fud, false); - advance(")"); + } + + delete implied[name]; } + } else { + scope[name] = funct; + } + } + } - if (state.tokens.next.id === "(end)") - error("E006", state.tokens.curr); + function doOption() { + var nt = state.tokens.next; + var body = nt.body.split(",").map(function(s) { return s.trim(); }); + var predef = {}; - advance(); + if (nt.type === "globals") { + body.forEach(function(g) { + g = g.split(":"); + var key = (g[0] || "").trim(); + var val = (g[1] || "").trim(); - if (initial) { - anonname = "anonymous"; - funct["(verb)"] = state.tokens.curr.value; - } + if (key.charAt(0) === "-") { + key = key.slice(1); + val = false; - if (initial === true && state.tokens.curr.fud) { - left = state.tokens.curr.fud(); + JSHINT.blacklist[key] = key; + delete predefined[key]; } else { - if (state.tokens.curr.nud) { - left = state.tokens.curr.nud(); - } else { - error("E030", state.tokens.curr, state.tokens.curr.id); - } - - while (rbp < state.tokens.next.lbp && !isEndOfExpr()) { - isArray = state.tokens.curr.value === "Array"; - isObject = state.tokens.curr.value === "Object"; - - // #527, new Foo.Array(), Foo.Array(), new Foo.Object(), Foo.Object() - // Line breaks in IfStatement heads exist to satisfy the checkJSHint - // "Line too long." error. - if (left && (left.value || (left.first && left.first.value))) { - // If the left.value is not "new", or the left.first.value is a "." - // then safely assume that this is not "new Array()" and possibly - // not "new Object()"... - if (left.value !== "new" || - (left.first && left.first.value && left.first.value === ".")) { - isArray = false; - // ...In the case of Object, if the left.value and state.tokens.curr.value - // are not equal, then safely assume that this not "new Object()" - if (left.value !== state.tokens.curr.value) { - isObject = false; - } - } - } - - advance(); - - if (isArray && state.tokens.curr.id === "(" && state.tokens.next.id === ")") { - warning("W009", state.tokens.curr); - } - - if (isObject && state.tokens.curr.id === "(" && state.tokens.next.id === ")") { - warning("W010", state.tokens.curr); - } - - if (left && state.tokens.curr.led) { - left = state.tokens.curr.led(left); - } else { - error("E033", state.tokens.curr, state.tokens.curr.id); - } - } + predef[key] = (val === "true"); } - if (isLetExpr) { - funct["(blockscope)"].unstack(); + }); + + combine(predefined, predef); + + for (var key in predef) { + if (_.has(predef, key)) { + declared[key] = nt; } - return left; + } } - -// Functions for conformance of style. - - function adjacent(left, right) { - left = left || state.tokens.curr; - right = right || state.tokens.next; - if (state.option.white) { - if (left.character !== right.from && left.line === right.line) { - left.from += (left.character - left.from); - warning("W011", left, left.value); - } - } + if (nt.type === "exported") { + body.forEach(function(e) { + exported[e] = true; + }); } - function nobreak(left, right) { - left = left || state.tokens.curr; - right = right || state.tokens.next; - if (state.option.white && (left.character !== right.from || left.line !== right.line)) { - warning("W012", right, right.value); + if (nt.type === "members") { + membersOnly = membersOnly || {}; + + body.forEach(function(m) { + var ch1 = m.charAt(0); + var ch2 = m.charAt(m.length - 1); + + if (ch1 === ch2 && (ch1 === "\"" || ch1 === "'")) { + m = m + .substr(1, m.length - 2) + .replace("\\\"", "\""); } + + membersOnly[m] = false; + }); } - function nospace(left, right) { - left = left || state.tokens.curr; - right = right || state.tokens.next; - if (state.option.white && !left.comment) { - if (left.line === right.line) { - adjacent(left, right); - } + var numvals = [ + "maxstatements", + "maxparams", + "maxdepth", + "maxcomplexity", + "maxerr", + "maxlen", + "indent" + ]; + + if (nt.type === "jshint" || nt.type === "jslint") { + body.forEach(function(g) { + g = g.split(":"); + var key = (g[0] || "").trim(); + var val = (g[1] || "").trim(); + + if (!checkOption(key, nt)) { + return; } + + if (numvals.indexOf(key) >= 0) { + // GH988 - numeric options can be disabled by setting them to `false` + if (val !== "false") { + val = +val; + + if (typeof val !== "number" || !isFinite(val) || val <= 0 || Math.floor(val) !== val) { + error("E032", nt, g[1].trim()); + return; + } + + state.option[key] = val; + } else { + state.option[key] = key === "indent" ? 4 : false; + } + + return; + } + + if (key === "validthis") { + // `validthis` is valid only within a function scope. + + if (funct["(global)"]) + return void error("E009"); + + if (val !== "true" && val !== "false") + return void error("E002", nt); + + state.option.validthis = (val === "true"); + return; + } + + if (key === "quotmark") { + switch (val) { + case "true": + case "false": + state.option.quotmark = (val === "true"); + break; + case "double": + case "single": + state.option.quotmark = val; + break; + default: + error("E002", nt); + } + return; + } + + if (key === "shadow") { + switch (val) { + case "true": + state.option.shadow = true; + break; + case "outer": + state.option.shadow = "outer"; + break; + case "false": + case "inner": + state.option.shadow = "inner"; + break; + default: + error("E002", nt); + } + return; + } + + if (key === "unused") { + switch (val) { + case "true": + state.option.unused = true; + break; + case "false": + state.option.unused = false; + break; + case "vars": + case "strict": + state.option.unused = val; + break; + default: + error("E002", nt); + } + return; + } + + if (key === "latedef") { + switch (val) { + case "true": + state.option.latedef = true; + break; + case "false": + state.option.latedef = false; + break; + case "nofunc": + state.option.latedef = "nofunc"; + break; + default: + error("E002", nt); + } + return; + } + + if (key === "ignore") { + switch (val) { + case "start": + state.ignoreLinterErrors = true; + break; + case "end": + state.ignoreLinterErrors = false; + break; + case "line": + state.ignoredLines[nt.line] = true; + removeIgnoredMessages(); + break; + default: + error("E002", nt); + } + return; + } + + var match = /^([+-])(W\d{3})$/g.exec(key); + if (match) { + // ignore for -W..., unignore for +W... + state.ignored[match[2]] = (match[1] === "-"); + return; + } + + var tn; + if (val === "true" || val === "false") { + if (nt.type === "jslint") { + tn = options.renamed[key] || key; + state.option[tn] = (val === "true"); + + if (options.inverted[tn] !== undefined) { + state.option[tn] = !state.option[tn]; + } + } else { + state.option[key] = (val === "true"); + } + + if (key === "newcap") { + state.option["(explicitNewcap)"] = true; + } + return; + } + + error("E002", nt); + }); + + assume(); + } + } + + // We need a peek function. If it has an argument, it peeks that much farther + // ahead. It is used to distinguish + // for ( var i in ... + // from + // for ( var i = ... + + function peek(p) { + var i = p || 0, j = 0, t; + + while (j <= i) { + t = lookahead[j]; + if (!t) { + t = lookahead[j] = lex.token(); + } + j += 1; + } + return t; + } + + function peekIgnoreEOL() { + var i = 0; + var t; + do { + t = peek(i++); + } while (t.id === "(endline)"); + return t; + } + + // Produce the next token. It looks for programming errors. + + function advance(id, t) { + + switch (state.tokens.curr.id) { + case "(number)": + if (state.tokens.next.id === ".") { + warning("W005", state.tokens.curr); + } + break; + case "-": + if (state.tokens.next.id === "-" || state.tokens.next.id === "--") { + warning("W006"); + } + break; + case "+": + if (state.tokens.next.id === "+" || state.tokens.next.id === "++") { + warning("W007"); + } + break; } - function nonadjacent(left, right) { - if (state.option.white) { - left = left || state.tokens.curr; - right = right || state.tokens.next; - - if (left.value === ";" && right.value === ";") { - return; - } - - if (left.line === right.line && left.character === right.from) { - left.from += (left.character - left.from); - warning("W013", left, left.value); - } - } - } - - function nobreaknonadjacent(left, right) { - left = left || state.tokens.curr; - right = right || state.tokens.next; - if (!state.option.laxbreak && left.line !== right.line) { - warning("W014", right, right.value); - } else if (state.option.white) { - left = left || state.tokens.curr; - right = right || state.tokens.next; - if (left.character === right.from) { - left.from += (left.character - left.from); - warning("W013", left, left.value); - } - } - } - - function indentation(bias) { - if (!state.option.white && !state.option["(explicitIndent)"]) { - return; - } - + if (id && state.tokens.next.id !== id) { + if (t) { if (state.tokens.next.id === "(end)") { - return; - } - - var i = indent + (bias || 0); - if (state.tokens.next.from !== i) { - warning("W015", state.tokens.next, state.tokens.next.value, i, state.tokens.next.from); - } - } - - function nolinebreak(t) { - t = t || state.tokens.curr; - if (t.line !== state.tokens.next.line) { - warning("E022", t, t.value); - } - } - - function nobreakcomma(left, right) { - if (left.line !== right.line) { - if (!state.option.laxcomma) { - if (comma.first) { - warning("I001"); - comma.first = false; - } - warning("W014", left, right.value); - } - } else if (!left.comment && left.character !== right.from && state.option.white) { - left.from += (left.character - left.from); - warning("W011", left, left.value); - } - } - - function comma(opts) { - opts = opts || {}; - - if (!opts.peek) { - nobreakcomma(state.tokens.curr, state.tokens.next); - advance(","); + error("E019", t, t.id); } else { - nobreakcomma(state.tokens.prev, state.tokens.curr); + error("E020", state.tokens.next, id, t.id, t.line, state.tokens.next.value); } - - // TODO: This is a temporary solution to fight against false-positives in - // arrays and objects with trailing commas (see GH-363). The best solution - // would be to extract all whitespace rules out of parser. - - if (state.tokens.next.value !== "]" && state.tokens.next.value !== "}") { - nonadjacent(state.tokens.curr, state.tokens.next); + } else if (state.tokens.next.type !== "(identifier)" || state.tokens.next.value !== id) { + // parameter destructuring with rest operator + if (state.tokens.next.value === "...") { + if (!state.option.esnext) { + warning("W119", state.tokens.next, "spread/rest operator"); + } + } else { + warning("W116", state.tokens.next, id, state.tokens.next.value); } + } + } - if (state.tokens.next.identifier && !(opts.property && state.option.inES5())) { - // Keywords that cannot follow a comma operator. - switch (state.tokens.next.value) { - case "break": - case "case": - case "catch": - case "continue": - case "default": - case "do": - case "else": - case "finally": - case "for": - case "if": - case "in": - case "instanceof": - case "return": - case "switch": - case "throw": - case "try": - case "var": - case "let": - case "while": - case "with": - error("E024", state.tokens.next, state.tokens.next.value); - return false; - } + state.tokens.prev = state.tokens.curr; + state.tokens.curr = state.tokens.next; + for (;;) { + state.tokens.next = lookahead.shift() || lex.token(); + + if (!state.tokens.next) { // No more tokens left, give up + quit("E041", state.tokens.curr.line); + } + + if (state.tokens.next.id === "(end)" || state.tokens.next.id === "(error)") { + return; + } + + if (state.tokens.next.check) { + state.tokens.next.check(); + } + + if (state.tokens.next.isSpecial) { + doOption(); + } else { + if (state.tokens.next.id !== "(endline)") { + break; } + } + } + } - if (state.tokens.next.type === "(punctuator)") { - switch (state.tokens.next.value) { - case "}": - case "]": - case ",": - if (opts.allowTrailing) { - return true; - } + function isInfix(token) { + return token.infix || (!token.identifier && !!token.led); + } - /* falls through */ - case ")": - error("E024", state.tokens.next, state.tokens.next.value); - return false; + function isEndOfExpr() { + var curr = state.tokens.curr; + var next = state.tokens.next; + if (next.id === ";" || next.id === "}" || next.id === ":") { + return true; + } + if (isInfix(next) === isInfix(curr) || (curr.id === "yield" && state.option.inMoz(true))) { + return curr.line !== next.line; + } + return false; + } + + function isBeginOfExpr(prev) { + return !prev.left && prev.arity !== "unary"; + } + + // This is the heart of JSHINT, the Pratt parser. In addition to parsing, it + // is looking for ad hoc lint patterns. We add .fud to Pratt's model, which is + // like .nud except that it is only used on the first token of a statement. + // Having .fud makes it much easier to define statement-oriented languages like + // JavaScript. I retained Pratt's nomenclature. + + // .nud Null denotation + // .fud First null denotation + // .led Left denotation + // lbp Left binding power + // rbp Right binding power + + // They are elements of the parsing method called Top Down Operator Precedence. + + function expression(rbp, initial) { + var left, isArray = false, isObject = false, isLetExpr = false; + + state.nameStack.push(); + + // if current expression is a let expression + if (!initial && state.tokens.next.value === "let" && peek(0).value === "(") { + if (!state.option.inMoz(true)) { + warning("W118", state.tokens.next, "let expressions"); + } + isLetExpr = true; + // create a new block scope we use only for the current expression + funct["(blockscope)"].stack(); + advance("let"); + advance("("); + state.syntax["let"].fud.call(state.syntax["let"].fud, false); + advance(")"); + } + + if (state.tokens.next.id === "(end)") + error("E006", state.tokens.curr); + + if (state.tokens.next.type === "(template)") { + doTemplateLiteral(); + } + + var isDangerous = + state.option.asi && + state.tokens.prev.line < state.tokens.curr.line && + _.contains(["]", ")"], state.tokens.prev.id) && + _.contains(["[", "("], state.tokens.curr.id); + + if (isDangerous) + warning("W014", state.tokens.curr, state.tokens.curr.id); + + advance(); + + if (initial) { + funct["(verb)"] = state.tokens.curr.value; + state.tokens.curr.beginsStmt = true; + } + + if (initial === true && state.tokens.curr.fud) { + left = state.tokens.curr.fud(); + } else { + if (state.tokens.curr.nud) { + left = state.tokens.curr.nud(); + } else { + error("E030", state.tokens.curr, state.tokens.curr.id); + } + + while (rbp < state.tokens.next.lbp && !isEndOfExpr()) { + isArray = state.tokens.curr.value === "Array"; + isObject = state.tokens.curr.value === "Object"; + + // #527, new Foo.Array(), Foo.Array(), new Foo.Object(), Foo.Object() + // Line breaks in IfStatement heads exist to satisfy the checkJSHint + // "Line too long." error. + if (left && (left.value || (left.first && left.first.value))) { + // If the left.value is not "new", or the left.first.value is a "." + // then safely assume that this is not "new Array()" and possibly + // not "new Object()"... + if (left.value !== "new" || + (left.first && left.first.value && left.first.value === ".")) { + isArray = false; + // ...In the case of Object, if the left.value and state.tokens.curr.value + // are not equal, then safely assume that this not "new Object()" + if (left.value !== state.tokens.curr.value) { + isObject = false; } - } - return true; - } - - // Functional constructors for making the symbols that will be inherited by - // tokens. - - function symbol(s, p) { - var x = state.syntax[s]; - if (!x || typeof x !== "object") { - state.syntax[s] = x = { - id: s, - lbp: p, - value: s - }; - } - return x; - } - - function delim(s) { - return symbol(s, 0); - } - - function stmt(s, f) { - var x = delim(s); - x.identifier = x.reserved = true; - x.fud = f; - return x; - } - - function blockstmt(s, f) { - var x = stmt(s, f); - x.block = true; - return x; - } - - function reserveName(x) { - var c = x.id.charAt(0); - if ((c >= "a" && c <= "z") || (c >= "A" && c <= "Z")) { - x.identifier = x.reserved = true; - } - return x; - } - - function prefix(s, f) { - var x = symbol(s, 150); - reserveName(x); - - x.nud = (typeof f === "function") ? f : function () { - this.right = expression(150); - this.arity = "unary"; - - if (this.id === "++" || this.id === "--") { - if (state.option.plusplus) { - warning("W016", this, this.id); - } else if (this.right && (!this.right.identifier || isReserved(this.right)) && - this.right.id !== "." && this.right.id !== "[") { - warning("W017", this); - } - } - - return this; - }; - - return x; - } - - function type(s, f) { - var x = delim(s); - x.type = s; - x.nud = f; - return x; - } - - function reserve(name, func) { - var x = type(name, func); - x.identifier = true; - x.reserved = true; - return x; - } - - function FutureReservedWord(name, meta) { - var x = type(name, (meta && meta.nud) || function () { - return this; - }); - - meta = meta || {}; - meta.isFutureReservedWord = true; - - x.value = name; - x.identifier = true; - x.reserved = true; - x.meta = meta; - - return x; - } - - function reservevar(s, v) { - return reserve(s, function () { - if (typeof v === "function") { - v(this); - } - return this; - }); - } - - function infix(s, f, p, w) { - var x = symbol(s, p); - reserveName(x); - x.infix = true; - x.led = function (left) { - if (!w) { - nobreaknonadjacent(state.tokens.prev, state.tokens.curr); - nonadjacent(state.tokens.curr, state.tokens.next); - } - if (s === "in" && left.id === "!") { - warning("W018", left, "!"); - } - if (typeof f === "function") { - return f(left, this); - } else { - this.left = left; - this.right = expression(p); - return this; - } - }; - return x; - } - - - function application(s) { - var x = symbol(s, 42); - - x.led = function (left) { - if (!state.option.inESNext()) { - warning("W104", state.tokens.curr, "arrow function syntax (=>)"); - } - - nobreaknonadjacent(state.tokens.prev, state.tokens.curr); - nonadjacent(state.tokens.curr, state.tokens.next); - - this.left = left; - this.right = doFunction(undefined, undefined, false, left); - return this; - }; - return x; - } - - function relation(s, f) { - var x = symbol(s, 100); - - x.led = function (left) { - nobreaknonadjacent(state.tokens.prev, state.tokens.curr); - nonadjacent(state.tokens.curr, state.tokens.next); - var right = expression(100); - - if (isIdentifier(left, "NaN") || isIdentifier(right, "NaN")) { - warning("W019", this); - } else if (f) { - f.apply(this, [left, right]); - } - - if (!left || !right) { - quit("E041", state.tokens.curr.line); - } - - if (left.id === "!") { - warning("W018", left, "!"); - } - - if (right.id === "!") { - warning("W018", right, "!"); - } - - this.left = left; - this.right = right; - return this; - }; - return x; - } - - function isPoorRelation(node) { - return node && - ((node.type === "(number)" && +node.value === 0) || - (node.type === "(string)" && node.value === "") || - (node.type === "null" && !state.option.eqnull) || - node.type === "true" || - node.type === "false" || - node.type === "undefined"); - } - - // Checks whether the 'typeof' operator is used with the correct - // value. For docs on 'typeof' see: - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof - - function isTypoTypeof(left, right) { - if (state.option.notypeof) - return false; - - if (!left || !right) - return false; - - var values = [ - "undefined", "object", "boolean", "number", - "string", "function", "xml", "object" - ]; - - if (right.type === "(identifier)" && right.value === "typeof" && left.type === "(string)") - return !_.contains(values, left.value); - - return false; - } - - function findNativePrototype(left) { - var natives = [ - "Array", "ArrayBuffer", "Boolean", "Collator", "DataView", "Date", - "DateTimeFormat", "Error", "EvalError", "Float32Array", "Float64Array", - "Function", "Infinity", "Intl", "Int16Array", "Int32Array", "Int8Array", - "Iterator", "Number", "NumberFormat", "Object", "RangeError", - "ReferenceError", "RegExp", "StopIteration", "String", "SyntaxError", - "TypeError", "Uint16Array", "Uint32Array", "Uint8Array", "Uint8ClampedArray", - "URIError" - ]; - - function walkPrototype(obj) { - if (typeof obj !== "object") return; - return obj.right === "prototype" ? obj : walkPrototype(obj.left); - } - - function walkNative(obj) { - while (!obj.identifier && typeof obj.left === "object") - obj = obj.left; - - if (obj.identifier && natives.indexOf(obj.value) >= 0) - return obj.value; - } - - var prototype = walkPrototype(left); - if (prototype) return walkNative(prototype); - } - - function assignop(s, f, p) { - var x = infix(s, typeof f === "function" ? f : function (left, that) { - that.left = left; - - if (left) { - if (state.option.freeze) { - var nativeObject = findNativePrototype(left); - if (nativeObject) - warning("W121", left, nativeObject); - } - - if (predefined[left.value] === false && - scope[left.value]["(global)"] === true) { - warning("W020", left); - } else if (left["function"]) { - warning("W021", left, left.value); - } - - if (funct[left.value] === "const") { - error("E013", left, left.value); - } - - if (left.id === ".") { - if (!left.left) { - warning("E031", that); - } else if (left.left.value === "arguments" && !state.directive["use strict"]) { - warning("E031", that); - } - - that.right = expression(10); - return that; - } else if (left.id === "[") { - if (state.tokens.curr.left.first) { - state.tokens.curr.left.first.forEach(function (t) { - if (funct[t.value] === "const") { - error("E013", t, t.value); - } - }); - } else if (!left.left) { - warning("E031", that); - } else if (left.left.value === "arguments" && !state.directive["use strict"]) { - warning("E031", that); - } - that.right = expression(10); - return that; - } else if (left.identifier && !isReserved(left)) { - if (funct[left.value] === "exception") { - warning("W022", left); - } - that.right = expression(10); - return that; - } - - if (left === state.syntax["function"]) { - warning("W023", state.tokens.curr); - } - } - - error("E031", that); - }, p); - - x.exps = true; - x.assign = true; - return x; - } - - - function bitwise(s, f, p) { - var x = symbol(s, p); - reserveName(x); - x.led = (typeof f === "function") ? f : function (left) { - if (state.option.bitwise) { - warning("W016", this, this.id); - } - this.left = left; - this.right = expression(p); - return this; - }; - return x; - } - - - function bitwiseassignop(s) { - return assignop(s, function (left, that) { - if (state.option.bitwise) { - warning("W016", that, that.id); - } - nonadjacent(state.tokens.prev, state.tokens.curr); - nonadjacent(state.tokens.curr, state.tokens.next); - if (left) { - if (left.id === "." || left.id === "[" || - (left.identifier && !isReserved(left))) { - expression(10); - return that; - } - if (left === state.syntax["function"]) { - warning("W023", state.tokens.curr); - } - return that; - } - error("E031", that); - }, 20); - } - - - function suffix(s) { - var x = symbol(s, 150); - - x.led = function (left) { - if (state.option.plusplus) { - warning("W016", this, this.id); - } else if ((!left.identifier || isReserved(left)) && left.id !== "." && left.id !== "[") { - warning("W017", this); - } - - this.left = left; - return this; - }; - return x; - } - - // fnparam means that this identifier is being defined as a function - // argument (see identifier()) - // prop means that this identifier is that of an object property - - function optionalidentifier(fnparam, prop) { - if (!state.tokens.next.identifier) { - return; + } } advance(); - var curr = state.tokens.curr; - var val = state.tokens.curr.value; - - if (!isReserved(curr)) { - return val; + if (isArray && state.tokens.curr.id === "(" && state.tokens.next.id === ")") { + warning("W009", state.tokens.curr); } - if (prop) { - if (state.option.inES5()) { - return val; - } + if (isObject && state.tokens.curr.id === "(" && state.tokens.next.id === ")") { + warning("W010", state.tokens.curr); } - if (fnparam && val === "undefined") { - return val; + if (left && state.tokens.curr.led) { + left = state.tokens.curr.led(left); + } else { + error("E033", state.tokens.curr, state.tokens.curr.id); + } + } + } + if (isLetExpr) { + funct["(blockscope)"].unstack(); + } + + state.nameStack.pop(); + + return left; + } + + + // Functions for conformance of style. + + function nobreaknonadjacent(left, right) { + left = left || state.tokens.curr; + right = right || state.tokens.next; + if (!state.option.laxbreak && left.line !== right.line) { + warning("W014", right, right.value); + } + } + + function nolinebreak(t) { + t = t || state.tokens.curr; + if (t.line !== state.tokens.next.line) { + warning("E022", t, t.value); + } + } + + function nobreakcomma(left, right) { + if (left.line !== right.line) { + if (!state.option.laxcomma) { + if (comma.first) { + warning("I001"); + comma.first = false; + } + warning("W014", left, right.value); + } + } + } + + function comma(opts) { + opts = opts || {}; + + if (state.option.nocomma) { + warning("W127"); + } + + if (!opts.peek) { + nobreakcomma(state.tokens.curr, state.tokens.next); + advance(","); + } else { + nobreakcomma(state.tokens.prev, state.tokens.curr); + } + + if (state.tokens.next.identifier && !(opts.property && state.option.inES5())) { + // Keywords that cannot follow a comma operator. + switch (state.tokens.next.value) { + case "break": + case "case": + case "catch": + case "continue": + case "default": + case "do": + case "else": + case "finally": + case "for": + case "if": + case "in": + case "instanceof": + case "return": + case "switch": + case "throw": + case "try": + case "var": + case "let": + case "while": + case "with": + error("E024", state.tokens.next, state.tokens.next.value); + return false; + } + } + + if (state.tokens.next.type === "(punctuator)") { + switch (state.tokens.next.value) { + case "}": + case "]": + case ",": + if (opts.allowTrailing) { + return true; } - // Display an info message about reserved words as properties - // and ES5 but do it only once. - if (prop && !api.getCache("displayed:I002")) { - api.setCache("displayed:I002", true); - warning("I002"); + /* falls through */ + case ")": + error("E024", state.tokens.next, state.tokens.next.value); + return false; + } + } + return true; + } + + // Functional constructors for making the symbols that will be inherited by + // tokens. + + function symbol(s, p) { + var x = state.syntax[s]; + if (!x || typeof x !== "object") { + state.syntax[s] = x = { + id: s, + lbp: p, + value: s + }; + } + return x; + } + + function delim(s) { + var x = symbol(s, 0); + x.delim = true; + return x; + } + + function stmt(s, f) { + var x = delim(s); + x.identifier = x.reserved = true; + x.fud = f; + return x; + } + + function blockstmt(s, f) { + var x = stmt(s, f); + x.block = true; + return x; + } + + function reserveName(x) { + var c = x.id.charAt(0); + if ((c >= "a" && c <= "z") || (c >= "A" && c <= "Z")) { + x.identifier = x.reserved = true; + } + return x; + } + + function prefix(s, f) { + var x = symbol(s, 150); + reserveName(x); + + x.nud = (typeof f === "function") ? f : function() { + this.arity = "unary"; + this.right = expression(150); + + if (this.id === "++" || this.id === "--") { + if (state.option.plusplus) { + warning("W016", this, this.id); + } else if (this.right && (!this.right.identifier || isReserved(this.right)) && + this.right.id !== "." && this.right.id !== "[") { + warning("W017", this); + } + } + + return this; + }; + + return x; + } + + function type(s, f) { + var x = delim(s); + x.type = s; + x.nud = f; + return x; + } + + function reserve(name, func) { + var x = type(name, func); + x.identifier = true; + x.reserved = true; + return x; + } + + function FutureReservedWord(name, meta) { + var x = type(name, (meta && meta.nud) || function() { + return this; + }); + + meta = meta || {}; + meta.isFutureReservedWord = true; + + x.value = name; + x.identifier = true; + x.reserved = true; + x.meta = meta; + + return x; + } + + function reservevar(s, v) { + return reserve(s, function() { + if (typeof v === "function") { + v(this); + } + return this; + }); + } + + function infix(s, f, p, w) { + var x = symbol(s, p); + reserveName(x); + x.infix = true; + x.led = function(left) { + if (!w) { + nobreaknonadjacent(state.tokens.prev, state.tokens.curr); + } + if (s === "in" && left.id === "!") { + warning("W018", left, "!"); + } + if (typeof f === "function") { + return f(left, this); + } else { + this.left = left; + this.right = expression(p); + return this; + } + }; + return x; + } + + + function application(s) { + var x = symbol(s, 42); + + x.led = function(left) { + nobreaknonadjacent(state.tokens.prev, state.tokens.curr); + + this.left = left; + this.right = doFunction(undefined, undefined, false, { loneArg: left }); + return this; + }; + return x; + } + + function relation(s, f) { + var x = symbol(s, 100); + + x.led = function(left) { + nobreaknonadjacent(state.tokens.prev, state.tokens.curr); + var right = expression(100); + + if (isIdentifier(left, "NaN") || isIdentifier(right, "NaN")) { + warning("W019", this); + } else if (f) { + f.apply(this, [left, right]); + } + + if (!left || !right) { + quit("E041", state.tokens.curr.line); + } + + if (left.id === "!") { + warning("W018", left, "!"); + } + + if (right.id === "!") { + warning("W018", right, "!"); + } + + this.left = left; + this.right = right; + return this; + }; + return x; + } + + function isPoorRelation(node) { + return node && + ((node.type === "(number)" && +node.value === 0) || + (node.type === "(string)" && node.value === "") || + (node.type === "null" && !state.option.eqnull) || + node.type === "true" || + node.type === "false" || + node.type === "undefined"); + } + + // Checks whether the 'typeof' operator is used with the correct + // value. For docs on 'typeof' see: + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof + + function isTypoTypeof(left, right) { + if (state.option.notypeof) + return false; + + if (!left || !right) + return false; + + var values = [ + "undefined", "object", "boolean", "number", + "string", "function", "xml", "object", "unknown" + ]; + + if (right.type === "(identifier)" && right.value === "typeof" && left.type === "(string)") + return !_.contains(values, left.value); + + return false; + } + + function findNativePrototype(left) { + var natives = [ + "Array", "ArrayBuffer", "Boolean", "Collator", "DataView", "Date", + "DateTimeFormat", "Error", "EvalError", "Float32Array", "Float64Array", + "Function", "Infinity", "Intl", "Int16Array", "Int32Array", "Int8Array", + "Iterator", "Number", "NumberFormat", "Object", "RangeError", + "ReferenceError", "RegExp", "StopIteration", "String", "SyntaxError", + "TypeError", "Uint16Array", "Uint32Array", "Uint8Array", "Uint8ClampedArray", + "URIError" + ]; + + function walkPrototype(obj) { + if (typeof obj !== "object") return; + return obj.right === "prototype" ? obj : walkPrototype(obj.left); + } + + function walkNative(obj) { + while (!obj.identifier && typeof obj.left === "object") + obj = obj.left; + + if (obj.identifier && natives.indexOf(obj.value) >= 0) + return obj.value; + } + + var prototype = walkPrototype(left); + if (prototype) return walkNative(prototype); + } + + function assignop(s, f, p) { + var x = infix(s, typeof f === "function" ? f : function(left, that) { + that.left = left; + + if (left) { + if (state.option.freeze) { + var nativeObject = findNativePrototype(left); + if (nativeObject) + warning("W121", left, nativeObject); } - warning("W024", state.tokens.curr, state.tokens.curr.id); + if (predefined[left.value] === false && + scope[left.value]["(global)"] === true) { + warning("W020", left); + } else if (left["function"]) { + warning("W021", left, left.value); + } + + if (funct[left.value] === "const") { + error("E013", left, left.value); + } + + if (left.id === ".") { + if (!left.left) { + warning("E031", that); + } else if (left.left.value === "arguments" && !state.directive["use strict"]) { + warning("E031", that); + } + + state.nameStack.set(state.tokens.prev); + that.right = expression(10); + return that; + } else if (left.id === "[") { + if (state.tokens.curr.left.first) { + state.tokens.curr.left.first.forEach(function(t) { + if (t && funct[t.value] === "const") { + error("E013", t, t.value); + } + }); + } else if (!left.left) { + warning("E031", that); + } else if (left.left.value === "arguments" && !state.directive["use strict"]) { + warning("E031", that); + } + + state.nameStack.set(left.right); + + that.right = expression(10); + return that; + } else if (left.identifier && !isReserved(left)) { + if (funct[left.value] === "exception") { + warning("W022", left); + } + state.nameStack.set(left); + that.right = expression(10); + return that; + } + + if (left === state.syntax["function"]) { + warning("W023", state.tokens.curr); + } + } + + error("E031", that); + }, p); + + x.exps = true; + x.assign = true; + return x; + } + + + function bitwise(s, f, p) { + var x = symbol(s, p); + reserveName(x); + x.led = (typeof f === "function") ? f : function(left) { + if (state.option.bitwise) { + warning("W016", this, this.id); + } + this.left = left; + this.right = expression(p); + return this; + }; + return x; + } + + + function bitwiseassignop(s) { + return assignop(s, function(left, that) { + if (state.option.bitwise) { + warning("W016", that, that.id); + } + + if (left) { + if (left.id === "." || left.id === "[" || + (left.identifier && !isReserved(left))) { + expression(10); + return that; + } + if (left === state.syntax["function"]) { + warning("W023", state.tokens.curr); + } + return that; + } + error("E031", that); + }, 20); + } + + + function suffix(s) { + var x = symbol(s, 150); + + x.led = function(left) { + if (state.option.plusplus) { + warning("W016", this, this.id); + } else if ((!left.identifier || isReserved(left)) && left.id !== "." && left.id !== "[") { + warning("W017", this); + } + + this.left = left; + return this; + }; + return x; + } + + // fnparam means that this identifier is being defined as a function + // argument (see identifier()) + // prop means that this identifier is that of an object property + // exported means that the identifier is part of a valid ES6 `export` declaration + + function optionalidentifier(fnparam, prop, preserve, exported) { + if (!state.tokens.next.identifier) { + return; + } + + if (!preserve) { + advance(); + } + + var curr = state.tokens.curr; + var val = state.tokens.curr.value; + + if (exported) { + state.tokens.curr.exported = true; + } + + if (!isReserved(curr)) { + return val; + } + + if (prop) { + if (state.option.inES5()) { return val; + } } - // fnparam means that this identifier is being defined as a function - // argument - // prop means that this identifier is that of an object property - function identifier(fnparam, prop) { - var i = optionalidentifier(fnparam, prop); - if (i) { - return i; - } - if (state.tokens.curr.id === "function" && state.tokens.next.id === "(") { - warning("W025"); - } else { - error("E030", state.tokens.next, state.tokens.next.value); - } + if (fnparam && val === "undefined") { + return val; } + warning("W024", state.tokens.curr, state.tokens.curr.id); + return val; + } - function reachable(s) { - var i = 0, t; - if (state.tokens.next.id !== ";" || noreach) { - return; - } - for (;;) { - do { - t = peek(i); - i += 1; - } while (t.id != "(end)" && t.id === "(comment)"); - - if (t.reach) { - return; - } - if (t.id !== "(endline)") { - if (t.id === "function") { - if (state.option.latedef === true) { - warning("W026", t); - } - break; - } - - warning("W027", t, t.value, s); - break; - } - } + // fnparam means that this identifier is being defined as a function + // argument + // prop means that this identifier is that of an object property + // `exported` means that the identifier token should be exported. + function identifier(fnparam, prop, exported) { + var i = optionalidentifier(fnparam, prop, false, exported); + if (i) { + return i; } + // parameter destructuring with rest operator + if (state.tokens.next.value === "...") { + if (!state.option.esnext) { + warning("W119", state.tokens.next, "spread/rest operator"); + } + } else { + error("E030", state.tokens.next, state.tokens.next.value); - function statement(noindent) { - var values; - var i = indent, r, s = scope, t = state.tokens.next; + // The token should be consumed after a warning is issued so the parser + // can continue as though an identifier were found. The semicolon token + // should not be consumed in this way so that the parser interprets it as + // a statement delimeter; + if (state.tokens.next.id !== ";") { + advance(); + } + } + } - if (t.id === ";") { - advance(";"); - return; + + function reachable(controlToken) { + var i = 0, t; + if (state.tokens.next.id !== ";" || controlToken.inBracelessBlock) { + return; + } + for (;;) { + do { + t = peek(i); + i += 1; + } while (t.id != "(end)" && t.id === "(comment)"); + + if (t.reach) { + return; + } + if (t.id !== "(endline)") { + if (t.id === "function") { + if (state.option.latedef === true) { + warning("W026", t); + } + break; } - // Is this a labelled statement? - var res = isReserved(t); + warning("W027", t, t.value, controlToken.value); + break; + } + } + } - // We're being more tolerant here: if someone uses - // a FutureReservedWord as a label, we warn but proceed - // anyway. - - if (res && t.meta && t.meta.isFutureReservedWord && peek().id === ":") { - warning("W024", t, t.id); - res = false; + function parseFinalSemicolon() { + if (state.tokens.next.id !== ";") { + if (!state.option.asi) { + // If this is the last statement in a block that ends on + // the same line *and* option lastsemic is on, ignore the warning. + // Otherwise, complain about missing semicolon. + if (!state.option.lastsemic || state.tokens.next.id !== "}" || + state.tokens.next.line !== state.tokens.curr.line) { + warningAt("W033", state.tokens.curr.line, state.tokens.curr.character); } + } + } else { + advance(";"); + } + } - // detect a destructuring assignment - if (_.has(["[", "{"], t.value)) { - if (lookupBlockType().isDestAssign) { - if (!state.option.inESNext()) { - warning("W104", state.tokens.curr, "destructuring expression"); - } - values = destructuringExpression(); - values.forEach(function (tok) { - isundef(funct, "W117", tok.token, tok.id); - }); - advance("="); - destructuringExpressionMatch(values, expression(10, true)); - advance(";"); - return; - } - } - if (t.identifier && !res && peek().id === ":") { - advance(); - advance(":"); - scope = Object.create(s); - addlabel(t.value, { type: "label" }); + function statement() { + var i = indent, r, s = scope, t = state.tokens.next; - if (!state.tokens.next.labelled && state.tokens.next.value !== "{") { - warning("W028", state.tokens.next, t.value, state.tokens.next.value); - } - - state.tokens.next.label = t.value; - t = state.tokens.next; - } - - // Is it a lonely block? - - if (t.id === "{") { - // Is it a switch case block? - // - // switch (foo) { - // case bar: { <= here. - // ... - // } - // } - var iscase = (funct["(verb)"] === "case" && state.tokens.curr.value === ":"); - block(true, true, false, false, iscase); - return; - } - - // Parse the statement. - - if (!noindent) { - indentation(); - } - r = expression(0, true); - - // Look for the final semicolon. - - if (!t.block) { - if (!state.option.expr && (!r || !r.exps)) { - warning("W030", state.tokens.curr); - } else if (state.option.nonew && r && r.left && r.id === "(" && r.left.id === "new") { - warning("W031", t); - } - - if (state.tokens.next.id !== ";") { - if (!state.option.asi) { - // If this is the last statement in a block that ends on - // the same line *and* option lastsemic is on, ignore the warning. - // Otherwise, complain about missing semicolon. - if (!state.option.lastsemic || state.tokens.next.id !== "}" || - state.tokens.next.line !== state.tokens.curr.line) { - warningAt("W033", state.tokens.curr.line, state.tokens.curr.character); - } - } - } else { - adjacent(state.tokens.curr, state.tokens.next); - advance(";"); - nonadjacent(state.tokens.curr, state.tokens.next); - } - } - - // Restore the indentation. - - indent = i; - scope = s; - return r; + if (t.id === ";") { + advance(";"); + return; } + // Is this a labelled statement? + var res = isReserved(t); - function statements(startLine) { - var a = [], p; + // We're being more tolerant here: if someone uses + // a FutureReservedWord as a label, we warn but proceed + // anyway. - while (!state.tokens.next.reach && state.tokens.next.id !== "(end)") { - if (state.tokens.next.id === ";") { - p = peek(); - - if (!p || (p.id !== "(" && p.id !== "[")) { - warning("W032"); - } - - advance(";"); - } else { - a.push(statement(startLine === state.tokens.next.line)); - } - } - return a; + if (res && t.meta && t.meta.isFutureReservedWord && peek().id === ":") { + warning("W024", t, t.id); + res = false; } - - /* - * read all directives - * recognizes a simple form of asi, but always - * warns, if it is used - */ - function directives() { - var i, p, pn; - - for (;;) { - if (state.tokens.next.id === "(string)") { - p = peek(0); - if (p.id === "(endline)") { - i = 1; - do { - pn = peek(i); - i = i + 1; - } while (pn.id === "(endline)"); - - if (pn.id !== ";") { - if (pn.id !== "(string)" && pn.id !== "(number)" && - pn.id !== "(regexp)" && pn.identifier !== true && - pn.id !== "}") { - break; - } - warning("W033", state.tokens.next); - } else { - p = pn; - } - } else if (p.id === "}") { - // Directive with no other statements, warn about missing semicolon - warning("W033", p); - } else if (p.id !== ";") { - break; - } - - indentation(); - advance(); - if (state.directive[state.tokens.curr.value]) { - warning("W034", state.tokens.curr, state.tokens.curr.value); - } - - if (state.tokens.curr.value === "use strict") { - if (!state.option["(explicitNewcap)"]) - state.option.newcap = true; - state.option.undef = true; - } - - // there's no directive negation, so always set to true - state.directive[state.tokens.curr.value] = true; - - if (p.id === ";") { - advance(";"); - } - continue; - } - break; - } - } - - - /* - * Parses a single block. A block is a sequence of statements wrapped in - * braces. - * - * ordinary - true for everything but function bodies and try blocks. - * stmt - true if block can be a single statement (e.g. in if/for/while). - * isfunc - true if block is a function body - * isfatarrow - true if its a body of a fat arrow function - * iscase - true if block is a switch case block - */ - function block(ordinary, stmt, isfunc, isfatarrow, iscase) { - var a, - b = inblock, - old_indent = indent, - m, - s = scope, - t, - line, - d; - - inblock = ordinary; - - if (!ordinary || !state.option.funcscope) - scope = Object.create(scope); - - nonadjacent(state.tokens.curr, state.tokens.next); - t = state.tokens.next; - - var metrics = funct["(metrics)"]; - metrics.nestedBlockDepth += 1; - metrics.verifyMaxNestedBlockDepthPerFunction(); - - if (state.tokens.next.id === "{") { - advance("{"); - - // create a new block scope - funct["(blockscope)"].stack(); - - line = state.tokens.curr.line; - if (state.tokens.next.id !== "}") { - indent += state.option.indent; - while (!ordinary && state.tokens.next.from > indent) { - indent += state.option.indent; - } - - if (isfunc) { - m = {}; - for (d in state.directive) { - if (_.has(state.directive, d)) { - m[d] = state.directive[d]; - } - } - directives(); - - if (state.option.strict && funct["(context)"]["(global)"]) { - if (!m["use strict"] && !state.directive["use strict"]) { - warning("E007"); - } - } - } - - a = statements(line); - - metrics.statementCount += a.length; - - if (isfunc) { - state.directive = m; - } - - indent -= state.option.indent; - if (line !== state.tokens.next.line) { - indentation(); - } - } else if (line !== state.tokens.next.line) { - indentation(); - } - advance("}", t); - - funct["(blockscope)"].unstack(); - - indent = old_indent; - } else if (!ordinary) { - if (isfunc) { - m = {}; - if (stmt && !isfatarrow && !state.option.inMoz(true)) { - error("W118", state.tokens.curr, "function closure expressions"); - } - - if (!stmt) { - for (d in state.directive) { - if (_.has(state.directive, d)) { - m[d] = state.directive[d]; - } - } - } - expression(10); - - if (state.option.strict && funct["(context)"]["(global)"]) { - if (!m["use strict"] && !state.directive["use strict"]) { - warning("E007"); - } - } - } else { - error("E021", state.tokens.next, "{", state.tokens.next.value); - } - } else { - - // check to avoid let declaration not within a block - funct["(nolet)"] = true; - - if (!stmt || state.option.curly) { - warning("W116", state.tokens.next, "{", state.tokens.next.value); - } - - noreach = true; - indent += state.option.indent; - // test indentation only if statement is in new line - a = [statement(state.tokens.next.line === state.tokens.curr.line)]; - indent -= state.option.indent; - noreach = false; - - delete funct["(nolet)"]; - } - // Don't clear and let it propagate out if it is "break", "return", or "throw" in switch case - if (!(iscase && ["break", "return", "throw"].indexOf(funct["(verb)"]) != -1)) { - funct["(verb)"] = null; - } - - if (!ordinary || !state.option.funcscope) scope = s; - inblock = b; - if (ordinary && state.option.noempty && (!a || a.length === 0)) { - warning("W035"); - } - metrics.nestedBlockDepth -= 1; - return a; - } - - - function countMember(m) { - if (membersOnly && typeof membersOnly[m] !== "boolean") { - warning("W036", state.tokens.curr, m); - } - if (typeof member[m] === "number") { - member[m] += 1; - } else { - member[m] = 1; - } - } - - - function note_implied(tkn) { - var name = tkn.value; - var desc = Object.getOwnPropertyDescriptor(implied, name); - - if (!desc) - implied[name] = [tkn.line]; - else - desc.value.push(tkn.line); - } - - - // Build the syntax table by declaring the syntactic elements of the language. - - type("(number)", function () { - return this; - }); - - type("(string)", function () { - return this; - }); - - state.syntax["(identifier)"] = { - type: "(identifier)", - lbp: 0, - identifier: true, - - nud: function () { - var v = this.value; - var s = scope[v]; - var f; - var block; - - if (typeof s === "function") { - // Protection against accidental inheritance. - s = undefined; - } else if (!funct["(blockscope)"].current.has(v) && typeof s === "boolean") { - f = funct; - funct = functions[0]; - addlabel(v, { type: "var" }); - s = funct; - funct = f; - } - - block = funct["(blockscope)"].getlabel(v); - - // The name is in scope and defined in the current function. - if (funct === s || block) { - // Change 'unused' to 'var', and reject labels. - // the name is in a block scope. - switch (block ? block[v]["(type)"] : funct[v]) { - case "unused": - if (block) block[v]["(type)"] = "var"; - else funct[v] = "var"; - break; - case "unction": - if (block) block[v]["(type)"] = "function"; - else funct[v] = "function"; - this["function"] = true; - break; - case "const": - setprop(funct, v, { unused: false }); - break; - case "function": - this["function"] = true; - break; - case "label": - warning("W037", state.tokens.curr, v); - break; - } - } else if (funct["(global)"]) { - // The name is not defined in the function. If we are in the global - // scope, then we have an undefined variable. - // - // Operators typeof and delete do not raise runtime errors even if - // the base object of a reference is null so no need to display warning - // if we're inside of typeof or delete. - - if (typeof predefined[v] !== "boolean") { - // Attempting to subscript a null reference will throw an - // error, even within the typeof and delete operators - if (!(anonname === "typeof" || anonname === "delete") || - (state.tokens.next && (state.tokens.next.value === "." || - state.tokens.next.value === "["))) { - - // if we're in a list comprehension, variables are declared - // locally and used before being defined. So we check - // the presence of the given variable in the comp array - // before declaring it undefined. - - if (!funct["(comparray)"].check(v)) { - isundef(funct, "W117", state.tokens.curr, v); - } - } - } - - note_implied(state.tokens.curr); - } else { - // If the name is already defined in the current - // function, but not as outer, then there is a scope error. - - switch (funct[v]) { - case "closure": - case "function": - case "var": - case "unused": - warning("W038", state.tokens.curr, v); - break; - case "label": - warning("W037", state.tokens.curr, v); - break; - case "outer": - case "global": - break; - default: - // If the name is defined in an outer function, make an outer entry, - // and if it was unused, make it var. - if (s === true) { - funct[v] = true; - } else if (s === null) { - warning("W039", state.tokens.curr, v); - note_implied(state.tokens.curr); - } else if (typeof s !== "object") { - // Operators typeof and delete do not raise runtime errors even - // if the base object of a reference is null so no need to - // - // display warning if we're inside of typeof or delete. - // Attempting to subscript a null reference will throw an - // error, even within the typeof and delete operators - if (!(anonname === "typeof" || anonname === "delete") || - (state.tokens.next && - (state.tokens.next.value === "." || state.tokens.next.value === "["))) { - - isundef(funct, "W117", state.tokens.curr, v); - } - funct[v] = true; - note_implied(state.tokens.curr); - } else { - switch (s[v]) { - case "function": - case "unction": - this["function"] = true; - s[v] = "closure"; - funct[v] = s["(global)"] ? "global" : "outer"; - break; - case "var": - case "unused": - s[v] = "closure"; - funct[v] = s["(global)"] ? "global" : "outer"; - break; - case "const": - setprop(s, v, { unused: false }); - break; - case "closure": - funct[v] = s["(global)"] ? "global" : "outer"; - break; - case "label": - warning("W037", state.tokens.curr, v); - } - } - } - } - return this; - }, - - led: function () { - error("E033", state.tokens.next, state.tokens.next.value); - } - }; - - type("(regexp)", function () { - return this; - }); - - // ECMAScript parser - - delim("(endline)"); - delim("(begin)"); - delim("(end)").reach = true; - delim("(error)").reach = true; - delim("}").reach = true; - delim(")"); - delim("]"); - delim("\"").reach = true; - delim("'").reach = true; - delim(";"); - delim(":").reach = true; - delim("#"); - - reserve("else"); - reserve("case").reach = true; - reserve("catch"); - reserve("default").reach = true; - reserve("finally"); - reservevar("arguments", function (x) { - if (state.directive["use strict"] && funct["(global)"]) { - warning("E008", x); - } - }); - reservevar("eval"); - reservevar("false"); - reservevar("Infinity"); - reservevar("null"); - reservevar("this", function (x) { - if (state.directive["use strict"] && !state.option.validthis && ((funct["(statement)"] && - funct["(name)"].charAt(0) > "Z") || funct["(global)"])) { - warning("W040", x); - } - }); - reservevar("true"); - reservevar("undefined"); - - assignop("=", "assign", 20); - assignop("+=", "assignadd", 20); - assignop("-=", "assignsub", 20); - assignop("*=", "assignmult", 20); - assignop("/=", "assigndiv", 20).nud = function () { - error("E014"); - }; - assignop("%=", "assignmod", 20); - - bitwiseassignop("&=", "assignbitand", 20); - bitwiseassignop("|=", "assignbitor", 20); - bitwiseassignop("^=", "assignbitxor", 20); - bitwiseassignop("<<=", "assignshiftleft", 20); - bitwiseassignop(">>=", "assignshiftright", 20); - bitwiseassignop(">>>=", "assignshiftrightunsigned", 20); - infix(",", function (left, that) { - var expr; - that.exprs = [left]; - if (!comma({peek: true})) { - return that; - } - while (true) { - if (!(expr = expression(10))) { - break; - } - that.exprs.push(expr); - if (state.tokens.next.value !== "," || !comma()) { - break; - } - } - return that; - }, 10, true); - - infix("?", function (left, that) { - increaseComplexityCount(); - that.left = left; - that.right = expression(10); - advance(":"); - that["else"] = expression(10); - return that; - }, 30); - - var orPrecendence = 40; - infix("||", function (left, that) { - increaseComplexityCount(); - that.left = left; - that.right = expression(orPrecendence); - return that; - }, orPrecendence); - infix("&&", "and", 50); - bitwise("|", "bitor", 70); - bitwise("^", "bitxor", 80); - bitwise("&", "bitand", 90); - relation("==", function (left, right) { - var eqnull = state.option.eqnull && (left.value === "null" || right.value === "null"); - - switch (true) { - case !eqnull && state.option.eqeqeq: - this.from = this.character; - warning("W116", this, "===", "=="); - break; - case isPoorRelation(left): - warning("W041", this, "===", left.value); - break; - case isPoorRelation(right): - warning("W041", this, "===", right.value); - break; - case isTypoTypeof(right, left): - warning("W122", this, right.value); - break; - case isTypoTypeof(left, right): - warning("W122", this, left.value); - break; - } - - return this; - }); - relation("===", function (left, right) { - if (isTypoTypeof(right, left)) { - warning("W122", this, right.value); - } else if (isTypoTypeof(left, right)) { - warning("W122", this, left.value); - } - return this; - }); - relation("!=", function (left, right) { - var eqnull = state.option.eqnull && - (left.value === "null" || right.value === "null"); - - if (!eqnull && state.option.eqeqeq) { - this.from = this.character; - warning("W116", this, "!==", "!="); - } else if (isPoorRelation(left)) { - warning("W041", this, "!==", left.value); - } else if (isPoorRelation(right)) { - warning("W041", this, "!==", right.value); - } else if (isTypoTypeof(right, left)) { - warning("W122", this, right.value); - } else if (isTypoTypeof(left, right)) { - warning("W122", this, left.value); - } - return this; - }); - relation("!==", function (left, right) { - if (isTypoTypeof(right, left)) { - warning("W122", this, right.value); - } else if (isTypoTypeof(left, right)) { - warning("W122", this, left.value); - } - return this; - }); - relation("<"); - relation(">"); - relation("<="); - relation(">="); - bitwise("<<", "shiftleft", 120); - bitwise(">>", "shiftright", 120); - bitwise(">>>", "shiftrightunsigned", 120); - infix("in", "in", 120); - infix("instanceof", "instanceof", 120); - infix("+", function (left, that) { - var right = expression(130); - if (left && right && left.id === "(string)" && right.id === "(string)") { - left.value += right.value; - left.character = right.character; - if (!state.option.scripturl && reg.javascriptURL.test(left.value)) { - warning("W050", left); - } - return left; - } - that.left = left; - that.right = right; - return that; - }, 130); - prefix("+", "num"); - prefix("+++", function () { - warning("W007"); - this.right = expression(150); - this.arity = "unary"; - return this; - }); - infix("+++", function (left) { - warning("W007"); - this.left = left; - this.right = expression(130); - return this; - }, 130); - infix("-", "sub", 130); - prefix("-", "neg"); - prefix("---", function () { - warning("W006"); - this.right = expression(150); - this.arity = "unary"; - return this; - }); - infix("---", function (left) { - warning("W006"); - this.left = left; - this.right = expression(130); - return this; - }, 130); - infix("*", "mult", 140); - infix("/", "div", 140); - infix("%", "mod", 140); - - suffix("++", "postinc"); - prefix("++", "preinc"); - state.syntax["++"].exps = true; - - suffix("--", "postdec"); - prefix("--", "predec"); - state.syntax["--"].exps = true; - prefix("delete", function () { - var p = expression(10); - if (!p || (p.id !== "." && p.id !== "[")) { - warning("W051"); - } - this.first = p; - return this; - }).exps = true; - - prefix("~", function () { - if (state.option.bitwise) { - warning("W052", this, "~"); - } - expression(150); - return this; - }); - - prefix("...", function () { + // detect a module import declaration + if (t.value === "module" && t.type === "(identifier)") { + if (peek().type === "(identifier)") { if (!state.option.inESNext()) { - warning("W104", this, "spread/rest operator"); - } - if (!state.tokens.next.identifier) { - error("E030", state.tokens.next, state.tokens.next.value); - } - expression(150); - return this; - }); - - prefix("!", function () { - this.right = expression(150); - this.arity = "unary"; - - if (!this.right) { // '!' followed by nothing? Give up. - quit("E041", this.line || 0); - } - - if (bang[this.right.id] === true) { - warning("W018", this, "!"); - } - return this; - }); - - prefix("typeof", "typeof"); - prefix("new", function () { - var c = expression(155), i; - if (c && c.id !== "function") { - if (c.identifier) { - c["new"] = true; - switch (c.value) { - case "Number": - case "String": - case "Boolean": - case "Math": - case "JSON": - warning("W053", state.tokens.prev, c.value); - break; - case "Function": - if (!state.option.evil) { - warning("W054"); - } - break; - case "Date": - case "RegExp": - case "this": - break; - default: - if (c.id !== "function") { - i = c.value.substr(0, 1); - if (state.option.newcap && (i < "A" || i > "Z") && !_.has(global, c.value)) { - warning("W055", state.tokens.curr); - } - } - } - } else { - if (c.id !== "." && c.id !== "[" && c.id !== "(") { - warning("W056", state.tokens.curr); - } - } - } else { - if (!state.option.supernew) - warning("W057", this); - } - adjacent(state.tokens.curr, state.tokens.next); - if (state.tokens.next.id !== "(" && !state.option.supernew) { - warning("W058", state.tokens.curr, state.tokens.curr.value); - } - this.first = c; - return this; - }); - state.syntax["new"].exps = true; - - prefix("void").exps = true; - - infix(".", function (left, that) { - adjacent(state.tokens.prev, state.tokens.curr); - nobreak(); - var m = identifier(false, true); - - if (typeof m === "string") { - countMember(m); - } - - that.left = left; - that.right = m; - - if (m && m === "hasOwnProperty" && state.tokens.next.value === "=") { - warning("W001"); - } - - if (left && left.value === "arguments" && (m === "callee" || m === "caller")) { - if (state.option.noarg) - warning("W059", left, m); - else if (state.directive["use strict"]) - error("E008"); - } else if (!state.option.evil && left && left.value === "document" && - (m === "write" || m === "writeln")) { - warning("W060", left); - } - - if (!state.option.evil && (m === "eval" || m === "execScript")) { - warning("W061"); - } - - return that; - }, 160, true); - - infix("(", function (left, that) { - if (state.tokens.prev.id !== "}" && state.tokens.prev.id !== ")") { - nobreak(state.tokens.prev, state.tokens.curr); - } - - nospace(); - if (state.option.immed && left && !left.immed && left.id === "function") { - warning("W062"); - } - - var n = 0; - var p = []; - - if (left) { - if (left.type === "(identifier)") { - if (left.value.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)) { - if ("Number String Boolean Date Object".indexOf(left.value) === -1) { - if (left.value === "Math") { - warning("W063", left); - } else if (state.option.newcap) { - warning("W064", left); - } - } - } - } - } - - if (state.tokens.next.id !== ")") { - for (;;) { - p[p.length] = expression(10); - n += 1; - if (state.tokens.next.id !== ",") { - break; - } - comma(); - } - } - - advance(")"); - nospace(state.tokens.prev, state.tokens.curr); - - if (typeof left === "object") { - if (state.option.inES3() && left.value === "parseInt" && n === 1) { - warning("W065", state.tokens.curr); - } - if (!state.option.evil) { - if (left.value === "eval" || left.value === "Function" || - left.value === "execScript") { - warning("W061", left); - - if (p[0] && [0].id === "(string)") { - addInternalSrc(left, p[0].value); - } - } else if (p[0] && p[0].id === "(string)" && - (left.value === "setTimeout" || - left.value === "setInterval")) { - warning("W066", left); - addInternalSrc(left, p[0].value); - - // window.setTimeout/setInterval - } else if (p[0] && p[0].id === "(string)" && - left.value === "." && - left.left.value === "window" && - (left.right === "setTimeout" || - left.right === "setInterval")) { - warning("W066", left); - addInternalSrc(left, p[0].value); - } - } - if (!left.identifier && left.id !== "." && left.id !== "[" && - left.id !== "(" && left.id !== "&&" && left.id !== "||" && - left.id !== "?") { - warning("W067", left); - } - } - - that.left = left; - return that; - }, 155, true).exps = true; - - prefix("(", function () { - nospace(); - var bracket, brackets = []; - var pn, pn1, i = 0; - var ret; - - do { - pn = peek(i); - i += 1; - pn1 = peek(i); - i += 1; - } while (pn.value !== ")" && pn1.value !== "=>" && pn1.value !== ";" && pn1.type !== "(end)"); - - if (state.tokens.next.id === "function") { - state.tokens.next.immed = true; - } - - var exprs = []; - - if (state.tokens.next.id !== ")") { - for (;;) { - if (pn1.value === "=>" && state.tokens.next.value === "{") { - bracket = state.tokens.next; - bracket.left = destructuringExpression(); - brackets.push(bracket); - for (var t in bracket.left) { - exprs.push(bracket.left[t].token); - } - } else { - exprs.push(expression(10)); - } - if (state.tokens.next.id !== ",") { - break; - } - comma(); - } - } - - advance(")", this); - nospace(state.tokens.prev, state.tokens.curr); - if (state.option.immed && exprs[0] && exprs[0].id === "function") { - if (state.tokens.next.id !== "(" && - (state.tokens.next.id !== "." || (peek().value !== "call" && peek().value !== "apply"))) { - warning("W068", this); - } - } - - if (state.tokens.next.value === "=>") { - return exprs; - } - if (!exprs.length) { - return; - } - if (exprs.length > 1) { - ret = Object.create(state.syntax[","]); - ret.exprs = exprs; - } else { - ret = exprs[0]; - } - if (ret) { - ret.paren = true; - } - return ret; - }); - - application("=>"); - - infix("[", function (left, that) { - nobreak(state.tokens.prev, state.tokens.curr); - nospace(); - var e = expression(10), s; - if (e && e.type === "(string)") { - if (!state.option.evil && (e.value === "eval" || e.value === "execScript")) { - warning("W061", that); - } - - countMember(e.value); - if (!state.option.sub && reg.identifier.test(e.value)) { - s = state.syntax[e.value]; - if (!s || !isReserved(s)) { - warning("W069", state.tokens.prev, e.value); - } - } - } - advance("]", that); - - if (e && e.value === "hasOwnProperty" && state.tokens.next.value === "=") { - warning("W001"); - } - - nospace(state.tokens.prev, state.tokens.curr); - that.left = left; - that.right = e; - return that; - }, 160, true); - - function comprehensiveArrayExpression() { - var res = {}; - res.exps = true; - funct["(comparray)"].stack(); - - // Handle reversed for expressions, used in spidermonkey - var reversed = false; - if (state.tokens.next.value !== "for") { - reversed = true; - if (!state.option.inMoz(true)) { - warning("W116", state.tokens.next, "for", state.tokens.next.value); - } - funct["(comparray)"].setState("use"); - res.right = expression(10); - } - - advance("for"); - if (state.tokens.next.value === "each") { - advance("each"); - if (!state.option.inMoz(true)) { - warning("W118", state.tokens.curr, "for each"); - } - } - advance("("); - funct["(comparray)"].setState("define"); - res.left = expression(130); - if (_.contains(["in", "of"], state.tokens.next.value)) { - advance(); - } else { - error("E045", state.tokens.curr); - } - funct["(comparray)"].setState("generate"); - expression(10); - - advance(")"); - if (state.tokens.next.value === "if") { - advance("if"); - advance("("); - funct["(comparray)"].setState("filter"); - res.filter = expression(10); - advance(")"); - } - - if (!reversed) { - funct["(comparray)"].setState("use"); - res.right = expression(10); - } - - advance("]"); - funct["(comparray)"].unstack(); - return res; - } - - prefix("[", function () { - var blocktype = lookupBlockType(true); - if (blocktype.isCompArray) { - if (!state.option.inESNext()) { - warning("W119", state.tokens.curr, "array comprehension"); - } - return comprehensiveArrayExpression(); - } else if (blocktype.isDestAssign && !state.option.inESNext()) { - warning("W104", state.tokens.curr, "destructuring assignment"); - } - var b = state.tokens.curr.line !== state.tokens.next.line; - this.first = []; - if (b) { - indent += state.option.indent; - if (state.tokens.next.from === indent + state.option.indent) { - indent += state.option.indent; - } - } - while (state.tokens.next.id !== "(end)") { - while (state.tokens.next.id === ",") { - if (!state.option.inES5()) - warning("W070"); - advance(","); - } - if (state.tokens.next.id === "]") { - break; - } - if (b && state.tokens.curr.line !== state.tokens.next.line) { - indentation(); - } - this.first.push(expression(10)); - if (state.tokens.next.id === ",") { - comma({ allowTrailing: true }); - if (state.tokens.next.id === "]" && !state.option.inES5(true)) { - warning("W070", state.tokens.curr); - break; - } - } else { - break; - } - } - if (b) { - indent -= state.option.indent; - indentation(); - } - advance("]", this); - return this; - }, 160); - - - function property_name() { - var id = optionalidentifier(false, true); - - if (!id) { - if (state.tokens.next.id === "(string)") { - id = state.tokens.next.value; - advance(); - } else if (state.tokens.next.id === "(number)") { - id = state.tokens.next.value.toString(); - advance(); - } - } - - if (id === "hasOwnProperty") { - warning("W001"); - } - - return id; - } - - function functionparams(parsed) { - var curr, next; - var params = []; - var ident; - var tokens = []; - var t; - var pastDefault = false; - - if (parsed) { - if (Array.isArray(parsed)) { - for (var i in parsed) { - curr = parsed[i]; - if (_.contains(["{", "["], curr.id)) { - for (t in curr.left) { - t = tokens[t]; - if (t.id) { - params.push(t.id); - addlabel(t.id, { type: "unused", token: t.token }); - } - } - } else if (curr.value === "...") { - if (!state.option.inESNext()) { - warning("W104", curr, "spread/rest operator"); - } - continue; - } else { - params.push(curr.value); - addlabel(curr.value, { type: "unused", token: curr }); - } - } - return params; - } else { - if (parsed.identifier === true) { - addlabel(parsed.value, { type: "unused", token: parsed }); - return [parsed]; - } - } - } - - next = state.tokens.next; - - advance("("); - nospace(); - - if (state.tokens.next.id === ")") { - advance(")"); - return; - } - - for (;;) { - if (_.contains(["{", "["], state.tokens.next.id)) { - tokens = destructuringExpression(); - for (t in tokens) { - t = tokens[t]; - if (t.id) { - params.push(t.id); - addlabel(t.id, { type: "unused", token: t.token }); - } - } - } else if (state.tokens.next.value === "...") { - if (!state.option.inESNext()) { - warning("W104", state.tokens.next, "spread/rest operator"); - } - advance("..."); - nospace(); - ident = identifier(true); - params.push(ident); - addlabel(ident, { type: "unused", token: state.tokens.curr }); - } else { - ident = identifier(true); - params.push(ident); - addlabel(ident, { type: "unused", token: state.tokens.curr }); - } - - // it is a syntax error to have a regular argument after a default argument - if (pastDefault) { - if (state.tokens.next.id !== "=") { - error("E051", state.tokens.current); - } - } - if (state.tokens.next.id === "=") { - if (!state.option.inESNext()) { - warning("W119", state.tokens.next, "default parameters"); - } - advance("="); - pastDefault = true; - expression(10); - } - if (state.tokens.next.id === ",") { - comma(); - } else { - advance(")", next); - nospace(state.tokens.prev, state.tokens.curr); - return params; - } - } - } - - function setprop(funct, name, values) { - if (!funct["(properties)"][name]) { - funct["(properties)"][name] = { unused: false }; - } - - _.extend(funct["(properties)"][name], values); - } - - function getprop(funct, name, prop) { - if (!funct["(properties)"][name]) - return null; - - return funct["(properties)"][name][prop] || null; - } - - function functor(name, token, scope, overwrites) { - var funct = { - "(name)" : name, - "(breakage)" : 0, - "(loopage)" : 0, - "(scope)" : scope, - "(tokens)" : {}, - "(properties)": {}, - - "(catch)" : false, - "(global)" : false, - - "(line)" : null, - "(character)" : null, - "(metrics)" : null, - "(statement)" : null, - "(context)" : null, - "(blockscope)": null, - "(comparray)" : null, - "(generator)" : null, - "(params)" : null - }; - - if (token) { - _.extend(funct, { - "(line)" : token.line, - "(character)": token.character, - "(metrics)" : createMetrics(token) - }); - } - - _.extend(funct, overwrites); - - if (funct["(context)"]) { - funct["(blockscope)"] = funct["(context)"]["(blockscope)"]; - funct["(comparray)"] = funct["(context)"]["(comparray)"]; - } - - return funct; - } - - function doFunction(name, statement, generator, fatarrowparams) { - var f; - var oldOption = state.option; - var oldIgnored = state.ignored; - var oldScope = scope; - - state.option = Object.create(state.option); - state.ignored = Object.create(state.ignored); - scope = Object.create(scope); - - funct = functor(name || "\"" + anonname + "\"", state.tokens.next, scope, { - "(statement)": statement, - "(context)": funct, - "(generator)": generator ? true : null - }); - - f = funct; - state.tokens.curr.funct = funct; - - functions.push(funct); - - if (name) { - addlabel(name, { type: "function" }); - } - - funct["(params)"] = functionparams(fatarrowparams); - funct["(metrics)"].verifyMaxParametersPerFunction(funct["(params)"]); - - // So we parse fat-arrow functions after we encounter =>. So basically - // doFunction is called with the left side of => as its last argument. - // This means that the parser, at that point, had already added its - // arguments to the undefs array and here we undo that. - - JSHINT.undefs = _.filter(JSHINT.undefs, function (item) { - return !_.contains(_.union(fatarrowparams), item[2]); - }); - - block(false, true, true, fatarrowparams ? true : false); - - if (generator && funct["(generator)"] !== "yielded") { - error("E047", state.tokens.curr); - } - - funct["(metrics)"].verifyMaxStatementsPerFunction(); - funct["(metrics)"].verifyMaxComplexityPerFunction(); - funct["(unusedOption)"] = state.option.unused; - - scope = oldScope; - state.option = oldOption; - state.ignored = oldIgnored; - funct["(last)"] = state.tokens.curr.line; - funct["(lastcharacter)"] = state.tokens.curr.character; - - _.map(Object.keys(funct), function (key) { - if (key[0] === "(") return; - funct["(blockscope)"].unshadow(key); - }); - - funct = funct["(context)"]; - - return f; - } - - function createMetrics(functionStartToken) { - return { - statementCount: 0, - nestedBlockDepth: -1, - ComplexityCount: 1, - - verifyMaxStatementsPerFunction: function () { - if (state.option.maxstatements && - this.statementCount > state.option.maxstatements) { - warning("W071", functionStartToken, this.statementCount); - } - }, - - verifyMaxParametersPerFunction: function (params) { - params = params || []; - - if (state.option.maxparams && params.length > state.option.maxparams) { - warning("W072", functionStartToken, params.length); - } - }, - - verifyMaxNestedBlockDepthPerFunction: function () { - if (state.option.maxdepth && - this.nestedBlockDepth > 0 && - this.nestedBlockDepth === state.option.maxdepth + 1) { - warning("W073", null, this.nestedBlockDepth); - } - }, - - verifyMaxComplexityPerFunction: function () { - var max = state.option.maxcomplexity; - var cc = this.ComplexityCount; - if (max && cc > max) { - warning("W074", functionStartToken, cc); - } - } - }; - } - - function increaseComplexityCount() { - funct["(metrics)"].ComplexityCount += 1; - } - - // Parse assignments that were found instead of conditionals. - // For example: if (a = 1) { ... } - - function checkCondAssignment(expr) { - var id, paren; - if (expr) { - id = expr.id; - paren = expr.paren; - if (id === "," && (expr = expr.exprs[expr.exprs.length - 1])) { - id = expr.id; - paren = paren || expr.paren; - } - } - switch (id) { - case "=": - case "+=": - case "-=": - case "*=": - case "%=": - case "&=": - case "|=": - case "^=": - case "/=": - if (!paren && !state.option.boss) { - warning("W084"); - } - } - } - - - (function (x) { - x.nud = function (isclassdef) { - var b, f, i, p, t, g; - var props = {}; // All properties, including accessors - var tag = ""; - - function saveProperty(name, tkn) { - if (props[name] && _.has(props, name)) - warning("W075", state.tokens.next, i); - else - props[name] = {}; - - props[name].basic = true; - props[name].basictkn = tkn; - } - - function saveSetter(name, tkn) { - if (props[name] && _.has(props, name)) { - if (props[name].basic || props[name].setter) - warning("W075", state.tokens.next, i); - } else { - props[name] = {}; - } - - props[name].setter = true; - props[name].setterToken = tkn; - } - - function saveGetter(name) { - if (props[name] && _.has(props, name)) { - if (props[name].basic || props[name].getter) - warning("W075", state.tokens.next, i); - } else { - props[name] = {}; - } - - props[name].getter = true; - props[name].getterToken = state.tokens.curr; - } - - b = state.tokens.curr.line !== state.tokens.next.line; - if (b) { - indent += state.option.indent; - if (state.tokens.next.from === indent + state.option.indent) { - indent += state.option.indent; - } - } - - for (;;) { - if (state.tokens.next.id === "}") { - break; - } - - if (b) { - indentation(); - } - - if (isclassdef && state.tokens.next.value === "static") { - advance("static"); - tag = "static "; - } - - if (state.tokens.next.value === "get" && peek().id !== ":") { - advance("get"); - - if (!state.option.inES5(!isclassdef)) { - error("E034"); - } - - i = property_name(); - - // ES6 allows for get() {...} and set() {...} method - // definition shorthand syntax, so we don't produce an error - // if the esnext option is enabled. - if (!i && !state.option.inESNext()) { - error("E035"); - } - - // It is a Syntax Error if PropName of MethodDefinition is - // "constructor" and SpecialMethod of MethodDefinition is true. - if (isclassdef && i === "constructor") { - error("E049", state.tokens.next, "class getter method", i); - } - - // We don't want to save this getter unless it's an actual getter - // and not an ES6 concise method - if (i) { - saveGetter(tag + i); - } - - t = state.tokens.next; - adjacent(state.tokens.curr, state.tokens.next); - f = doFunction(); - p = f["(params)"]; - - // Don't warn about getter/setter pairs if this is an ES6 concise method - if (i && p) { - warning("W076", t, p[0], i); - } - - adjacent(state.tokens.curr, state.tokens.next); - } else if (state.tokens.next.value === "set" && peek().id !== ":") { - advance("set"); - - if (!state.option.inES5(!isclassdef)) { - error("E034"); - } - - i = property_name(); - - // ES6 allows for get() {...} and set() {...} method - // definition shorthand syntax, so we don't produce an error - // if the esnext option is enabled. - if (!i && !state.option.inESNext()) { - error("E035"); - } - - // It is a Syntax Error if PropName of MethodDefinition is - // "constructor" and SpecialMethod of MethodDefinition is true. - if (isclassdef && i === "constructor") { - error("E049", state.tokens.next, "class setter method", i); - } - - // We don't want to save this getter unless it's an actual getter - // and not an ES6 concise method - if (i) { - saveSetter(tag + i, state.tokens.next); - } - - t = state.tokens.next; - adjacent(state.tokens.curr, state.tokens.next); - f = doFunction(); - p = f["(params)"]; - - // Don't warn about getter/setter pairs if this is an ES6 concise method - if (i && (!p || p.length !== 1)) { - warning("W077", t, i); - } - } else { - g = false; - if (state.tokens.next.value === "*" && state.tokens.next.type === "(punctuator)") { - if (!state.option.inESNext()) { - warning("W104", state.tokens.next, "generator functions"); - } - advance("*"); - g = true; - } - i = property_name(); - saveProperty(tag + i, state.tokens.next); - - if (typeof i !== "string") { - break; - } - - if (state.tokens.next.value === "(") { - if (!state.option.inESNext()) { - warning("W104", state.tokens.curr, "concise methods"); - } - doFunction(i, undefined, g); - } else if (!isclassdef) { - advance(":"); - nonadjacent(state.tokens.curr, state.tokens.next); - expression(10); - } - } - // It is a Syntax Error if PropName of MethodDefinition is "prototype". - if (isclassdef && i === "prototype") { - error("E049", state.tokens.next, "class method", i); - } - - countMember(i); - if (isclassdef) { - tag = ""; - continue; - } - if (state.tokens.next.id === ",") { - comma({ allowTrailing: true, property: true }); - if (state.tokens.next.id === ",") { - warning("W070", state.tokens.curr); - } else if (state.tokens.next.id === "}" && !state.option.inES5(true)) { - warning("W070", state.tokens.curr); - } - } else { - break; - } - } - if (b) { - indent -= state.option.indent; - indentation(); - } - advance("}", this); - - // Check for lonely setters if in the ES5 mode. - if (state.option.inES5()) { - for (var name in props) { - if (_.has(props, name) && props[name].setter && !props[name].getter) { - warning("W078", props[name].setterToken); - } - } - } - return this; - }; - x.fud = function () { - error("E036", state.tokens.curr); - }; - }(delim("{"))); - - function destructuringExpression() { - var id, ids; - var identifiers = []; - if (!state.option.inESNext()) { - warning("W104", state.tokens.curr, "destructuring expression"); - } - var nextInnerDE = function () { - var ident; - if (_.contains(["[", "{"], state.tokens.next.value)) { - ids = destructuringExpression(); - for (var id in ids) { - id = ids[id]; - identifiers.push({ id: id.id, token: id.token }); - } - } else if (state.tokens.next.value === ",") { - identifiers.push({ id: null, token: state.tokens.curr }); - } else { - ident = identifier(); - if (ident) - identifiers.push({ id: ident, token: state.tokens.curr }); - } - }; - if (state.tokens.next.value === "[") { - advance("["); - nextInnerDE(); - while (state.tokens.next.value !== "]") { - advance(","); - nextInnerDE(); - } - advance("]"); - } else if (state.tokens.next.value === "{") { - advance("{"); - id = identifier(); - if (state.tokens.next.value === ":") { - advance(":"); - nextInnerDE(); - } else { - identifiers.push({ id: id, token: state.tokens.curr }); - } - while (state.tokens.next.value !== "}") { - advance(","); - id = identifier(); - if (state.tokens.next.value === ":") { - advance(":"); - nextInnerDE(); - } else { - identifiers.push({ id: id, token: state.tokens.curr }); - } - } - advance("}"); - } - return identifiers; - } - - function destructuringExpressionMatch(tokens, value) { - var first = value.first; - - if (!first) - return; - - _.zip(tokens, Array.isArray(first) ? first : [ first ]).forEach(function (val) { - var token = val[0]; - var value = val[1]; - - if (token && value) - token.first = value; - else if (token && token.first && !value) - warning("W080", token.first, token.first.value); - }); - } - - var conststatement = stmt("const", function (prefix) { - var tokens; - var value; - var lone; // State variable to know if it is a lone identifier, or a destructuring statement. - - if (!state.option.inESNext()) - warning("W104", state.tokens.curr, "const"); - - this.first = []; - for (;;) { - var names = []; - nonadjacent(state.tokens.curr, state.tokens.next); - if (_.contains(["{", "["], state.tokens.next.value)) { - tokens = destructuringExpression(); - lone = false; - } else { - tokens = [ { id: identifier(), token: state.tokens.curr } ]; - lone = true; - } - for (var t in tokens) { - if (tokens.hasOwnProperty(t)) { - t = tokens[t]; - if (funct[t.id] === "const") { - warning("E011", null, t.id); - } - if (funct["(global)"] && predefined[t.id] === false) { - warning("W079", t.token, t.id); - } - if (t.id) { - addlabel(t.id, { token: t.token, type: "const", unused: true }); - names.push(t.token); - } - } - } - if (prefix) { - break; - } - - this.first = this.first.concat(names); - - if (state.tokens.next.id !== "=") { - warning("E012", state.tokens.curr, state.tokens.curr.value); - } - - if (state.tokens.next.id === "=") { - nonadjacent(state.tokens.curr, state.tokens.next); - advance("="); - nonadjacent(state.tokens.curr, state.tokens.next); - if (state.tokens.next.id === "undefined") { - warning("W080", state.tokens.prev, state.tokens.prev.value); - } - if (peek(0).id === "=" && state.tokens.next.identifier) { - warning("W120", state.tokens.next, state.tokens.next.value); - } - value = expression(10); - if (lone) { - tokens[0].first = value; - } else { - destructuringExpressionMatch(names, value); - } - } - - if (state.tokens.next.id !== ",") { - break; - } - comma(); - } - return this; - }); - - conststatement.exps = true; - var varstatement = stmt("var", function (prefix) { - // JavaScript does not have block scope. It only has function scope. So, - // declaring a variable in a block can have unexpected consequences. - var tokens, lone, value; - - if (funct["(onevar)"] && state.option.onevar) { - warning("W081"); - } else if (!funct["(global)"]) { - funct["(onevar)"] = true; - } - - this.first = []; - for (;;) { - var names = []; - nonadjacent(state.tokens.curr, state.tokens.next); - if (_.contains(["{", "["], state.tokens.next.value)) { - tokens = destructuringExpression(); - lone = false; - } else { - tokens = [ { id: identifier(), token: state.tokens.curr } ]; - lone = true; - } - for (var t in tokens) { - if (tokens.hasOwnProperty(t)) { - t = tokens[t]; - if (state.option.inESNext() && funct[t.id] === "const") { - warning("E011", null, t.id); - } - if (funct["(global)"] && predefined[t.id] === false) { - warning("W079", t.token, t.id); - } - if (t.id) { - addlabel(t.id, { type: "unused", token: t.token }); - names.push(t.token); - } - } - } - if (prefix) { - break; - } - - this.first = this.first.concat(names); - - if (state.tokens.next.id === "=") { - nonadjacent(state.tokens.curr, state.tokens.next); - advance("="); - nonadjacent(state.tokens.curr, state.tokens.next); - if (state.tokens.next.id === "undefined") { - warning("W080", state.tokens.prev, state.tokens.prev.value); - } - if (peek(0).id === "=" && state.tokens.next.identifier) { - warning("W120", state.tokens.next, state.tokens.next.value); - } - value = expression(10); - if (lone) { - tokens[0].first = value; - } else { - destructuringExpressionMatch(names, value); - } - } - - if (state.tokens.next.id !== ",") { - break; - } - comma(); - } - return this; - }); - varstatement.exps = true; - - var letstatement = stmt("let", function (prefix) { - var tokens, lone, value, letblock; - - if (!state.option.inESNext()) { - warning("W104", state.tokens.curr, "let"); - } - - if (state.tokens.next.value === "(") { - if (!state.option.inMoz(true)) { - warning("W118", state.tokens.next, "let block"); - } - advance("("); - funct["(blockscope)"].stack(); - letblock = true; - } else if (funct["(nolet)"]) { - error("E048", state.tokens.curr); - } - - if (funct["(onevar)"] && state.option.onevar) { - warning("W081"); - } else if (!funct["(global)"]) { - funct["(onevar)"] = true; - } - - this.first = []; - for (;;) { - var names = []; - nonadjacent(state.tokens.curr, state.tokens.next); - if (_.contains(["{", "["], state.tokens.next.value)) { - tokens = destructuringExpression(); - lone = false; - } else { - tokens = [ { id: identifier(), token: state.tokens.curr.value } ]; - lone = true; - } - for (var t in tokens) { - if (tokens.hasOwnProperty(t)) { - t = tokens[t]; - if (state.option.inESNext() && funct[t.id] === "const") { - warning("E011", null, t.id); - } - if (funct["(global)"] && predefined[t.id] === false) { - warning("W079", t.token, t.id); - } - if (t.id && !funct["(nolet)"]) { - addlabel(t.id, { type: "unused", token: t.token, islet: true }); - names.push(t.token); - } - } - } - if (prefix) { - break; - } - - this.first = this.first.concat(names); - - if (state.tokens.next.id === "=") { - nonadjacent(state.tokens.curr, state.tokens.next); - advance("="); - nonadjacent(state.tokens.curr, state.tokens.next); - if (state.tokens.next.id === "undefined") { - warning("W080", state.tokens.prev, state.tokens.prev.value); - } - if (peek(0).id === "=" && state.tokens.next.identifier) { - warning("W120", state.tokens.next, state.tokens.next.value); - } - value = expression(10); - if (lone) { - tokens[0].first = value; - } else { - destructuringExpressionMatch(names, value); - } - } - - if (state.tokens.next.id !== ",") { - break; - } - comma(); - } - if (letblock) { - advance(")"); - block(true, true); - this.block = true; - funct["(blockscope)"].unstack(); - } - - return this; - }); - letstatement.exps = true; - - blockstmt("class", function () { - return classdef.call(this, true); - }); - - function classdef(stmt) { - /*jshint validthis:true */ - if (!state.option.inESNext()) { - warning("W104", state.tokens.curr, "class"); - } - if (stmt) { - // BindingIdentifier - this.name = identifier(); - addlabel(this.name, { type: "unused", token: state.tokens.curr }); - } else if (state.tokens.next.identifier && state.tokens.next.value !== "extends") { - // BindingIdentifier(opt) - this.name = identifier(); - } - classtail(this); - return this; - } - - function classtail(c) { - var strictness = state.directive["use strict"]; - - // ClassHeritage(opt) - if (state.tokens.next.value === "extends") { - advance("extends"); - c.heritage = expression(10); - } - - // A ClassBody is always strict code. - state.directive["use strict"] = true; - advance("{"); - // ClassBody(opt) - c.body = state.syntax["{"].nud(true); - state.directive["use strict"] = strictness; - } - - blockstmt("function", function () { - var generator = false; - if (state.tokens.next.value === "*") { - advance("*"); - if (state.option.inESNext(true)) { - generator = true; - } else { - warning("W119", state.tokens.curr, "function*"); - } - } - if (inblock) { - warning("W082", state.tokens.curr); - - } - var i = identifier(); - if (funct[i] === "const") { - warning("E011", null, i); - } - adjacent(state.tokens.curr, state.tokens.next); - addlabel(i, { type: "unction", token: state.tokens.curr }); - - doFunction(i, { statement: true }, generator); - if (state.tokens.next.id === "(" && state.tokens.next.line === state.tokens.curr.line) { - error("E039"); - } - return this; - }); - - prefix("function", function () { - var generator = false; - if (state.tokens.next.value === "*") { - if (!state.option.inESNext()) { - warning("W119", state.tokens.curr, "function*"); - } - advance("*"); - generator = true; - } - var i = optionalidentifier(); - if (i || state.option.gcl) { - adjacent(state.tokens.curr, state.tokens.next); - } else { - nonadjacent(state.tokens.curr, state.tokens.next); - } - doFunction(i, undefined, generator); - if (!state.option.loopfunc && funct["(loopage)"]) { - warning("W083"); - } - return this; - }); - - blockstmt("if", function () { - var t = state.tokens.next; - increaseComplexityCount(); - state.condition = true; - advance("("); - nonadjacent(this, t); - nospace(); - checkCondAssignment(expression(0)); - advance(")", t); - state.condition = false; - nospace(state.tokens.prev, state.tokens.curr); - block(true, true); - if (state.tokens.next.id === "else") { - nonadjacent(state.tokens.curr, state.tokens.next); - advance("else"); - if (state.tokens.next.id === "if" || state.tokens.next.id === "switch") { - statement(true); - } else { - block(true, true); - } - } - return this; - }); - - blockstmt("try", function () { - var b; - - function doCatch() { - var oldScope = scope; - var e; - - advance("catch"); - nonadjacent(state.tokens.curr, state.tokens.next); - advance("("); - - scope = Object.create(oldScope); - - e = state.tokens.next.value; - if (state.tokens.next.type !== "(identifier)") { - e = null; - warning("E030", state.tokens.next, e); - } - - advance(); - - funct = functor("(catch)", state.tokens.next, scope, { - "(context)" : funct, - "(breakage)" : funct["(breakage)"], - "(loopage)" : funct["(loopage)"], - "(statement)": false, - "(catch)" : true - }); - - if (e) { - addlabel(e, { type: "exception" }); - } - - if (state.tokens.next.value === "if") { - if (!state.option.inMoz(true)) { - warning("W118", state.tokens.curr, "catch filter"); - } - advance("if"); - expression(0); - } - - advance(")"); - - state.tokens.curr.funct = funct; - functions.push(funct); - - block(false); - - scope = oldScope; - - funct["(last)"] = state.tokens.curr.line; - funct["(lastcharacter)"] = state.tokens.curr.character; - funct = funct["(context)"]; - } - - block(true); - - while (state.tokens.next.id === "catch") { - increaseComplexityCount(); - if (b && (!state.option.inMoz(true))) { - warning("W118", state.tokens.next, "multiple catch blocks"); - } - doCatch(); - b = true; - } - - if (state.tokens.next.id === "finally") { - advance("finally"); - block(true); - return; - } - - if (!b) { - error("E021", state.tokens.next, "catch", state.tokens.next.value); - } - - return this; - }); - - blockstmt("while", function () { - var t = state.tokens.next; - funct["(breakage)"] += 1; - funct["(loopage)"] += 1; - increaseComplexityCount(); - advance("("); - nonadjacent(this, t); - nospace(); - checkCondAssignment(expression(0)); - advance(")", t); - nospace(state.tokens.prev, state.tokens.curr); - block(true, true); - funct["(breakage)"] -= 1; - funct["(loopage)"] -= 1; - return this; - }).labelled = true; - - blockstmt("with", function () { - var t = state.tokens.next; - if (state.directive["use strict"]) { - error("E010", state.tokens.curr); - } else if (!state.option.withstmt) { - warning("W085", state.tokens.curr); - } - - advance("("); - nonadjacent(this, t); - nospace(); - expression(0); - advance(")", t); - nospace(state.tokens.prev, state.tokens.curr); - block(true, true); - - return this; - }); - - blockstmt("switch", function () { - var t = state.tokens.next; - var g = false; - var noindent = false; - - funct["(breakage)"] += 1; - advance("("); - nonadjacent(this, t); - nospace(); - checkCondAssignment(expression(0)); - advance(")", t); - nospace(state.tokens.prev, state.tokens.curr); - nonadjacent(state.tokens.curr, state.tokens.next); - t = state.tokens.next; - advance("{"); - nonadjacent(state.tokens.curr, state.tokens.next); - - if (state.tokens.next.from === indent) - noindent = true; - - if (!noindent) - indent += state.option.indent; - - this.cases = []; - - for (;;) { - switch (state.tokens.next.id) { - case "case": - switch (funct["(verb)"]) { - case "yield": - case "break": - case "case": - case "continue": - case "return": - case "switch": - case "throw": - break; - default: - // You can tell JSHint that you don't use break intentionally by - // adding a comment /* falls through */ on a line just before - // the next `case`. - if (!reg.fallsThrough.test(state.lines[state.tokens.next.line - 2])) { - warning("W086", state.tokens.curr, "case"); - } - } - indentation(); - advance("case"); - this.cases.push(expression(20)); - increaseComplexityCount(); - g = true; - advance(":"); - funct["(verb)"] = "case"; - break; - case "default": - switch (funct["(verb)"]) { - case "yield": - case "break": - case "continue": - case "return": - case "throw": - break; - default: - // Do not display a warning if 'default' is the first statement or if - // there is a special /* falls through */ comment. - if (this.cases.length) { - if (!reg.fallsThrough.test(state.lines[state.tokens.next.line - 2])) { - warning("W086", state.tokens.curr, "default"); - } - } - } - indentation(); - advance("default"); - g = true; - advance(":"); - break; - case "}": - if (!noindent) - indent -= state.option.indent; - indentation(); - advance("}", t); - funct["(breakage)"] -= 1; - funct["(verb)"] = undefined; - return; - case "(end)": - error("E023", state.tokens.next, "}"); - return; - default: - indent += state.option.indent; - if (g) { - switch (state.tokens.curr.id) { - case ",": - error("E040"); - return; - case ":": - g = false; - statements(); - break; - default: - error("E025", state.tokens.curr); - return; - } - } else { - if (state.tokens.curr.id === ":") { - advance(":"); - error("E024", state.tokens.curr, ":"); - statements(); - } else { - error("E021", state.tokens.next, "case", state.tokens.next.value); - return; - } - } - indent -= state.option.indent; - } - } - }).labelled = true; - - stmt("debugger", function () { - if (!state.option.debug) { - warning("W087", this); - } - return this; - }).exps = true; - - (function () { - var x = stmt("do", function () { - funct["(breakage)"] += 1; - funct["(loopage)"] += 1; - increaseComplexityCount(); - - this.first = block(true, true); - advance("while"); - var t = state.tokens.next; - nonadjacent(state.tokens.curr, t); - advance("("); - nospace(); - checkCondAssignment(expression(0)); - advance(")", t); - nospace(state.tokens.prev, state.tokens.curr); - funct["(breakage)"] -= 1; - funct["(loopage)"] -= 1; - return this; - }); - x.labelled = true; - x.exps = true; - }()); - - blockstmt("for", function () { - var s, t = state.tokens.next; - var letscope = false; - var foreachtok = null; - - if (t.value === "each") { - foreachtok = t; - advance("each"); - if (!state.option.inMoz(true)) { - warning("W118", state.tokens.curr, "for each"); - } - } - - funct["(breakage)"] += 1; - funct["(loopage)"] += 1; - increaseComplexityCount(); - advance("("); - nonadjacent(this, t); - nospace(); - - // what kind of for(…) statement it is? for(…of…)? for(…in…)? for(…;…;…)? - var nextop; // contains the token of the "in" or "of" operator - var i = 0; - var inof = ["in", "of"]; - do { - nextop = peek(i); - ++i; - } while (!_.contains(inof, nextop.value) && nextop.value !== ";" && - nextop.type !== "(end)"); - - // if we're in a for (… in|of …) statement - if (_.contains(inof, nextop.value)) { - if (!state.option.inESNext() && nextop.value === "of") { - error("W104", nextop, "for of"); - } - if (state.tokens.next.id === "var") { - advance("var"); - state.syntax["var"].fud.call(state.syntax["var"].fud, true); - } else if (state.tokens.next.id === "let") { - advance("let"); - // create a new block scope - letscope = true; - funct["(blockscope)"].stack(); - state.syntax["let"].fud.call(state.syntax["let"].fud, true); - } else { - switch (funct[state.tokens.next.value]) { - case "unused": - funct[state.tokens.next.value] = "var"; - break; - case "var": - break; - default: - if (!funct["(blockscope)"].getlabel(state.tokens.next.value)) - warning("W088", state.tokens.next, state.tokens.next.value); - } - advance(); - } - advance(nextop.value); - expression(20); - advance(")", t); - s = block(true, true); - if (state.option.forin && s && (s.length > 1 || typeof s[0] !== "object" || - s[0].value !== "if")) { - warning("W089", this); - } - funct["(breakage)"] -= 1; - funct["(loopage)"] -= 1; - } else { - if (foreachtok) { - error("E045", foreachtok); - } - if (state.tokens.next.id !== ";") { - if (state.tokens.next.id === "var") { - advance("var"); - state.syntax["var"].fud.call(state.syntax["var"].fud); - } else if (state.tokens.next.id === "let") { - advance("let"); - // create a new block scope - letscope = true; - funct["(blockscope)"].stack(); - state.syntax["let"].fud.call(state.syntax["let"].fud); - } else { - for (;;) { - expression(0, "for"); - if (state.tokens.next.id !== ",") { - break; - } - comma(); - } - } - } - nolinebreak(state.tokens.curr); - advance(";"); - if (state.tokens.next.id !== ";") { - checkCondAssignment(expression(0)); - } - nolinebreak(state.tokens.curr); - advance(";"); - if (state.tokens.next.id === ";") { - error("E021", state.tokens.next, ")", ";"); - } - if (state.tokens.next.id !== ")") { - for (;;) { - expression(0, "for"); - if (state.tokens.next.id !== ",") { - break; - } - comma(); - } - } - advance(")", t); - nospace(state.tokens.prev, state.tokens.curr); - block(true, true); - funct["(breakage)"] -= 1; - funct["(loopage)"] -= 1; - - } - // unstack loop blockscope - if (letscope) { - funct["(blockscope)"].unstack(); - } - return this; - }).labelled = true; - - - stmt("break", function () { - var v = state.tokens.next.value; - - if (funct["(breakage)"] === 0) - warning("W052", state.tokens.next, this.value); - - if (!state.option.asi) - nolinebreak(this); - - if (state.tokens.next.id !== ";" && !state.tokens.next.reach) { - if (state.tokens.curr.line === state.tokens.next.line) { - if (funct[v] !== "label") { - warning("W090", state.tokens.next, v); - } else if (scope[v] !== funct) { - warning("W091", state.tokens.next, v); - } - this.first = state.tokens.next; - advance(); - } - } - reachable("break"); - return this; - }).exps = true; - - - stmt("continue", function () { - var v = state.tokens.next.value; - - if (funct["(breakage)"] === 0) - warning("W052", state.tokens.next, this.value); - - if (!state.option.asi) - nolinebreak(this); - - if (state.tokens.next.id !== ";" && !state.tokens.next.reach) { - if (state.tokens.curr.line === state.tokens.next.line) { - if (funct[v] !== "label") { - warning("W090", state.tokens.next, v); - } else if (scope[v] !== funct) { - warning("W091", state.tokens.next, v); - } - this.first = state.tokens.next; - advance(); - } - } else if (!funct["(loopage)"]) { - warning("W052", state.tokens.next, this.value); - } - reachable("continue"); - return this; - }).exps = true; - - - stmt("return", function () { - if (this.line === state.tokens.next.line) { - if (state.tokens.next.id === "(regexp)") - warning("W092"); - - if (state.tokens.next.id !== ";" && !state.tokens.next.reach) { - nonadjacent(state.tokens.curr, state.tokens.next); - this.first = expression(0); - - if (this.first && - this.first.type === "(punctuator)" && this.first.value === "=" && - !this.first.paren && !state.option.boss) { - warningAt("W093", this.first.line, this.first.character); - } - } - } else { - if (state.tokens.next.type === "(punctuator)" && - ["[", "{", "+", "-"].indexOf(state.tokens.next.value) > -1) { - nolinebreak(this); // always warn (Line breaking error) - } - } - reachable("return"); - return this; - }).exps = true; - - (function (x) { - x.exps = true; - x.lbp = 25; - }(prefix("yield", function () { - var prev = state.tokens.prev; - if (state.option.inESNext(true) && !funct["(generator)"]) { - error("E046", state.tokens.curr, "yield"); - } else if (!state.option.inESNext()) { - warning("W104", state.tokens.curr, "yield"); - } - funct["(generator)"] = "yielded"; - if (this.line === state.tokens.next.line || !state.option.inMoz(true)) { - if (state.tokens.next.id === "(regexp)") - warning("W092"); - - if (state.tokens.next.id !== ";" && !state.tokens.next.reach && state.tokens.next.nud) { - nobreaknonadjacent(state.tokens.curr, state.tokens.next); - this.first = expression(10); - - if (this.first.type === "(punctuator)" && this.first.value === "=" && - !this.first.paren && !state.option.boss) { - warningAt("W093", this.first.line, this.first.character); - } - } - - if (state.option.inMoz(true) && state.tokens.next.id !== ")" && - (prev.lbp > 30 || (!prev.assign && !isEndOfExpr()) || prev.id === "yield")) { - error("E050", this); - } - } else if (!state.option.asi) { - nolinebreak(this); // always warn (Line breaking error) - } - return this; - }))); - - - stmt("throw", function () { - nolinebreak(this); - nonadjacent(state.tokens.curr, state.tokens.next); - this.first = expression(20); - reachable("throw"); - return this; - }).exps = true; - - stmt("import", function () { - if (!state.option.inESNext()) { - warning("W119", state.tokens.curr, "import"); - } - - if (state.tokens.next.identifier) { - this.name = identifier(); - addlabel(this.name, { type: "unused", token: state.tokens.curr }); - } else { - advance("{"); - for (;;) { - var importName; - if (state.tokens.next.type === "default") { - importName = "default"; - advance("default"); - } else { - importName = identifier(); - } - if (state.tokens.next.value === "as") { - advance("as"); - importName = identifier(); - } - addlabel(importName, { type: "unused", token: state.tokens.curr }); - - if (state.tokens.next.value === ",") { - advance(","); - } else if (state.tokens.next.value === "}") { - advance("}"); - break; - } else { - error("E024", state.tokens.next, state.tokens.next.value); - break; - } - } + warning("W119", state.tokens.curr, "module"); } + advance("module"); + var name = identifier(); + addlabel(name, { type: "unused", token: state.tokens.curr }); advance("from"); advance("(string)"); - return this; - }).exps = true; + parseFinalSemicolon(); + return; + } + } - stmt("export", function () { - if (!state.option.inESNext()) { - warning("W119", state.tokens.curr, "export"); + if (t.identifier && !res && peek().id === ":") { + advance(); + advance(":"); + scope = Object.create(s); + addlabel(t.value, { type: "label" }); + + if (!state.tokens.next.labelled && state.tokens.next.value !== "{") { + warning("W028", state.tokens.next, t.value, state.tokens.next.value); + } + + state.tokens.next.label = t.value; + t = state.tokens.next; + } + + // Is it a lonely block? + + if (t.id === "{") { + // Is it a switch case block? + // + // switch (foo) { + // case bar: { <= here. + // ... + // } + // } + var iscase = (funct["(verb)"] === "case" && state.tokens.curr.value === ":"); + block(true, true, false, false, iscase); + return; + } + + // Parse the statement. + + r = expression(0, true); + + if (r && (!r.identifier || r.value !== "function") && (r.type !== "(punctuator)")) { + if (!state.directive["use strict"] && + state.option.globalstrict && + state.option.strict) { + warning("E007"); + } + } + + // Look for the final semicolon. + + if (!t.block) { + if (!state.option.expr && (!r || !r.exps)) { + warning("W030", state.tokens.curr); + } else if (state.option.nonew && r && r.left && r.id === "(" && r.left.id === "new") { + warning("W031", t); + } + parseFinalSemicolon(); + } + + + // Restore the indentation. + + indent = i; + scope = s; + return r; + } + + + function statements() { + var a = [], p; + + while (!state.tokens.next.reach && state.tokens.next.id !== "(end)") { + if (state.tokens.next.id === ";") { + p = peek(); + + if (!p || (p.id !== "(" && p.id !== "[")) { + warning("W032"); } - if (state.tokens.next.type === "default") { - advance("default"); - if (state.tokens.next.id === "function" || state.tokens.next.id === "class") { - this.block = true; - } - this.exportee = expression(10); + advance(";"); + } else { + a.push(statement()); + } + } + return a; + } - return this; - } - if (state.tokens.next.value === "{") { - advance("{"); - for (;;) { - identifier(); + /* + * read all directives + * recognizes a simple form of asi, but always + * warns, if it is used + */ + function directives() { + var i, p, pn; - if (state.tokens.next.value === ",") { - advance(","); - } else if (state.tokens.next.value === "}") { - advance("}"); - break; - } else { - error("E024", state.tokens.next, state.tokens.next.value); - break; - } - } - return this; - } - - if (state.tokens.next.id === "var") { - advance("var"); - state.syntax["var"].fud.call(state.syntax["var"].fud); - } else if (state.tokens.next.id === "let") { - advance("let"); - state.syntax["let"].fud.call(state.syntax["let"].fud); - } else if (state.tokens.next.id === "const") { - advance("const"); - state.syntax["const"].fud.call(state.syntax["const"].fud); - } else if (state.tokens.next.id === "function") { - this.block = true; - advance("function"); - state.syntax["function"].fud(); - } else if (state.tokens.next.id === "class") { - this.block = true; - advance("class"); - state.syntax["class"].fud(); - } else { - error("E024", state.tokens.next, state.tokens.next.value); - } - - return this; - }).exps = true; - - // Future Reserved Words - - FutureReservedWord("abstract"); - FutureReservedWord("boolean"); - FutureReservedWord("byte"); - FutureReservedWord("char"); - FutureReservedWord("class", { es5: true, nud: classdef }); - FutureReservedWord("double"); - FutureReservedWord("enum", { es5: true }); - FutureReservedWord("export", { es5: true }); - FutureReservedWord("extends", { es5: true }); - FutureReservedWord("final"); - FutureReservedWord("float"); - FutureReservedWord("goto"); - FutureReservedWord("implements", { es5: true, strictOnly: true }); - FutureReservedWord("import", { es5: true }); - FutureReservedWord("int"); - FutureReservedWord("interface", { es5: true, strictOnly: true }); - FutureReservedWord("long"); - FutureReservedWord("native"); - FutureReservedWord("package", { es5: true, strictOnly: true }); - FutureReservedWord("private", { es5: true, strictOnly: true }); - FutureReservedWord("protected", { es5: true, strictOnly: true }); - FutureReservedWord("public", { es5: true, strictOnly: true }); - FutureReservedWord("short"); - FutureReservedWord("static", { es5: true, strictOnly: true }); - FutureReservedWord("super", { es5: true }); - FutureReservedWord("synchronized"); - FutureReservedWord("throws"); - FutureReservedWord("transient"); - FutureReservedWord("volatile"); - - // this function is used to determine wether a squarebracket or a curlybracket - // expression is a comprehension array, destructuring assignment or a json value. - - var lookupBlockType = function () { - var pn, pn1; - var i = -1; - var bracketStack = 0; - var ret = {}; - if (_.contains(["[", "{"], state.tokens.curr.value)) - bracketStack += 1; + while (state.tokens.next.id === "(string)") { + p = peek(0); + if (p.id === "(endline)") { + i = 1; do { - pn = (i === -1) ? state.tokens.next : peek(i); - pn1 = peek(i + 1); - i = i + 1; - if (_.contains(["[", "{"], pn.value)) { - bracketStack += 1; - } else if (_.contains(["]", "}"], pn.value)) { - bracketStack -= 1; - } - if (pn.identifier && pn.value === "for" && bracketStack === 1) { - ret.isCompArray = true; - ret.notJson = true; - break; - } - if (_.contains(["}", "]"], pn.value) && pn1.value === "=" && bracketStack === 0) { - ret.isDestAssign = true; - ret.notJson = true; - break; - } - if (pn.value === ";") { - ret.isBlock = true; - ret.notJson = true; - } - } while (bracketStack > 0 && pn.id !== "(end)" && i < 15); - return ret; - }; - - // Check whether this function has been reached for a destructuring assign with undeclared values - function destructuringAssignOrJsonValue() { - // lookup for the assignment (esnext only) - // if it has semicolons, it is a block, so go parse it as a block - // or it's not a block, but there are assignments, check for undeclared variables - - var block = lookupBlockType(); - if (block.notJson) { - if (!state.option.inESNext() && block.isDestAssign) { - warning("W104", state.tokens.curr, "destructuring assignment"); - } - statements(); - // otherwise parse json value - } else { - state.option.laxbreak = true; - state.jsonMode = true; - jsonValue(); + pn = peek(i++); + } while (pn.id === "(endline)"); + if (pn.id === ";") { + p = pn; + } else if (pn.value === "[" || pn.value === ".") { + // string -> [ | . is a valid production + return; + } else if (!state.option.asi || pn.value === "(") { + // string -> ( is not a valid production + warning("W033", state.tokens.next); } + } else if (p.id === "." || p.id === "[") { + return; + } else if (p.id !== ";") { + warning("W033", p); + } + + advance(); + if (state.directive[state.tokens.curr.value]) { + warning("W034", state.tokens.curr, state.tokens.curr.value); + } + + if (state.tokens.curr.value === "use strict") { + if (!state.option["(explicitNewcap)"]) { + state.option.newcap = true; + } + state.option.undef = true; + } + + // there's no directive negation, so always set to true + state.directive[state.tokens.curr.value] = true; + + if (p.id === ";") { + advance(";"); + } + } + } + + + /* + * Parses a single block. A block is a sequence of statements wrapped in + * braces. + * + * ordinary - true for everything but function bodies and try blocks. + * stmt - true if block can be a single statement (e.g. in if/for/while). + * isfunc - true if block is a function body + * isfatarrow - true if its a body of a fat arrow function + * iscase - true if block is a switch case block + */ + function block(ordinary, stmt, isfunc, isfatarrow, iscase) { + var a, + b = inblock, + old_indent = indent, + m, + s = scope, + t, + line, + d; + + inblock = ordinary; + + if (!ordinary || !state.option.funcscope) + scope = Object.create(scope); + + t = state.tokens.next; + + var metrics = funct["(metrics)"]; + metrics.nestedBlockDepth += 1; + metrics.verifyMaxNestedBlockDepthPerFunction(); + + if (state.tokens.next.id === "{") { + advance("{"); + + // create a new block scope + funct["(blockscope)"].stack(); + + line = state.tokens.curr.line; + if (state.tokens.next.id !== "}") { + indent += state.option.indent; + while (!ordinary && state.tokens.next.from > indent) { + indent += state.option.indent; + } + + if (isfunc) { + m = {}; + for (d in state.directive) { + if (_.has(state.directive, d)) { + m[d] = state.directive[d]; + } + } + directives(); + + if (state.option.strict && funct["(context)"]["(global)"]) { + if (!m["use strict"] && !state.directive["use strict"]) { + warning("E007"); + } + } + } + + a = statements(); + + metrics.statementCount += a.length; + + if (isfunc) { + state.directive = m; + } + + indent -= state.option.indent; + } + + advance("}", t); + + funct["(blockscope)"].unstack(); + + indent = old_indent; + } else if (!ordinary) { + if (isfunc) { + m = {}; + if (stmt && !isfatarrow && !state.option.inMoz(true)) { + error("W118", state.tokens.curr, "function closure expressions"); + } + + if (!stmt) { + for (d in state.directive) { + if (_.has(state.directive, d)) { + m[d] = state.directive[d]; + } + } + } + expression(10); + + if (state.option.strict && funct["(context)"]["(global)"]) { + if (!m["use strict"] && !state.directive["use strict"]) { + warning("E007"); + } + } + } else { + error("E021", state.tokens.next, "{", state.tokens.next.value); + } + } else { + + // check to avoid let declaration not within a block + funct["(nolet)"] = true; + + if (!stmt || state.option.curly) { + warning("W116", state.tokens.next, "{", state.tokens.next.value); + } + + state.tokens.next.inBracelessBlock = true; + indent += state.option.indent; + // test indentation only if statement is in new line + a = [statement()]; + indent -= state.option.indent; + + delete funct["(nolet)"]; } - // array comprehension parsing function - // parses and defines the three states of the list comprehension in order - // to avoid defining global variables, but keeping them to the list comprehension scope - // only. The order of the states are as follows: - // * "use" which will be the returned iterative part of the list comprehension - // * "define" which will define the variables local to the list comprehension - // * "filter" which will help filter out values + // Don't clear and let it propagate out if it is "break", "return" or similar in switch case + switch (funct["(verb)"]) { + case "break": + case "continue": + case "return": + case "throw": + if (iscase) { + break; + } - var arrayComprehension = function () { - var CompArray = function () { - this.mode = "use"; - this.variables = []; - }; - var _carrays = []; - var _current; - function declare(v) { - var l = _current.variables.filter(function (elt) { - // if it has, change its undef state - if (elt.value === v) { - elt.undef = false; - return v; - } - }).length; - return l !== 0; + /* falls through */ + default: + funct["(verb)"] = null; + } + + if (!ordinary || !state.option.funcscope) scope = s; + inblock = b; + if (ordinary && state.option.noempty && (!a || a.length === 0)) { + warning("W035", state.tokens.prev); + } + metrics.nestedBlockDepth -= 1; + return a; + } + + + function countMember(m) { + if (membersOnly && typeof membersOnly[m] !== "boolean") { + warning("W036", state.tokens.curr, m); + } + if (typeof member[m] === "number") { + member[m] += 1; + } else { + member[m] = 1; + } + } + + + function note_implied(tkn) { + var name = tkn.value; + var desc = Object.getOwnPropertyDescriptor(implied, name); + + if (!desc) + implied[name] = [tkn.line]; + else + desc.value.push(tkn.line); + } + + + // Build the syntax table by declaring the syntactic elements of the language. + + type("(number)", function() { + return this; + }); + + type("(string)", function() { + return this; + }); + + state.syntax["(identifier)"] = { + type: "(identifier)", + lbp: 0, + identifier: true, + + nud: function() { + var v = this.value; + var s = scope[v]; + var f; + var block; + + // If this identifier is the lone parameter to a shorthand "fat arrow" + // function definition, i.e. + // + // x => x; + // + // ...it should not be considered as a variable in the current scope. It + // will be added to the scope of the new function when the next token is + // parsed, so it can be safely ignored for now. + if (state.tokens.next.id === "=>") { + return this; + } + + if (typeof s === "function") { + // Protection against accidental inheritance. + s = undefined; + } else if (!funct["(blockscope)"].current.has(v) && typeof s === "boolean") { + f = funct; + funct = functions[0]; + addlabel(v, { type: "var" }); + s = funct; + funct = f; + } + + block = funct["(blockscope)"].getlabel(v); + + // The name is in scope and defined in the current function. + if (funct === s || block) { + // Change 'unused' to 'var', and reject labels. + // the name is in a block scope. + switch (block ? block[v]["(type)"] : funct[v]) { + case "unused": + if (block) block[v]["(type)"] = "var"; + else funct[v] = "var"; + break; + case "unction": + if (block) block[v]["(type)"] = "function"; + else funct[v] = "function"; + this["function"] = true; + break; + case "const": + setprop(funct, v, { unused: false }); + break; + case "function": + this["function"] = true; + break; + case "label": + warning("W037", state.tokens.curr, v); + break; } - function use(v) { - var l = _current.variables.filter(function (elt) { - // and if it has been defined - if (elt.value === v && !elt.undef) { - if (elt.unused === true) { - elt.unused = false; - } - return v; - } - }).length; - // otherwise we warn about it - return (l === 0); - } - return {stack: function () { - _current = new CompArray(); - _carrays.push(_current); - }, - unstack: function () { - _current.variables.filter(function (v) { - if (v.unused) - warning("W098", v.token, v.value); - if (v.undef) - isundef(v.funct, "W117", v.token, v.value); - }); - _carrays.splice(-1, 1); - _current = _carrays[_carrays.length - 1]; - }, - setState: function (s) { - if (_.contains(["use", "define", "generate", "filter"], s)) - _current.mode = s; - }, - check: function (v) { - if (!_current) { - return; - } - // When we are in "use" state of the list comp, we enqueue that var - if (_current && _current.mode === "use") { - if (use(v)) { - _current.variables.push({ - funct: funct, - token: state.tokens.curr, - value: v, - undef: true, - unused: false - }); - } - return true; - // When we are in "define" state of the list comp, - } else if (_current && _current.mode === "define") { - // check if the variable has been used previously - if (!declare(v)) { - _current.variables.push({ - funct: funct, - token: state.tokens.curr, - value: v, - undef: false, - unused: true - }); - } - return true; - // When we are in the "generate" state of the list comp, - } else if (_current && _current.mode === "generate") { - isundef(funct, "W117", state.tokens.curr, v); - return true; - // When we are in "filter" state, - } else if (_current && _current.mode === "filter") { - // we check whether current variable has been declared - if (use(v)) { - // if not we warn about it - isundef(funct, "W117", state.tokens.curr, v); - } - return true; - } - return false; - } - }; - }; + } else { + // If the name is already defined in the current + // function, but not as outer, then there is a scope error. - - // Parse JSON - - function jsonValue() { - - function jsonObject() { - var o = {}, t = state.tokens.next; - advance("{"); - if (state.tokens.next.id !== "}") { - for (;;) { - if (state.tokens.next.id === "(end)") { - error("E026", state.tokens.next, t.line); - } else if (state.tokens.next.id === "}") { - warning("W094", state.tokens.curr); - break; - } else if (state.tokens.next.id === ",") { - error("E028", state.tokens.next); - } else if (state.tokens.next.id !== "(string)") { - warning("W095", state.tokens.next, state.tokens.next.value); - } - if (o[state.tokens.next.value] === true) { - warning("W075", state.tokens.next, state.tokens.next.value); - } else if ((state.tokens.next.value === "__proto__" && - !state.option.proto) || (state.tokens.next.value === "__iterator__" && - !state.option.iterator)) { - warning("W096", state.tokens.next, state.tokens.next.value); - } else { - o[state.tokens.next.value] = true; - } - advance(); - advance(":"); - jsonValue(); - if (state.tokens.next.id !== ",") { - break; - } - advance(","); - } - } - advance("}"); - } - - function jsonArray() { - var t = state.tokens.next; - advance("["); - if (state.tokens.next.id !== "]") { - for (;;) { - if (state.tokens.next.id === "(end)") { - error("E027", state.tokens.next, t.line); - } else if (state.tokens.next.id === "]") { - warning("W094", state.tokens.curr); - break; - } else if (state.tokens.next.id === ",") { - error("E028", state.tokens.next); - } - jsonValue(); - if (state.tokens.next.id !== ",") { - break; - } - advance(","); - } - } - advance("]"); - } - - switch (state.tokens.next.id) { - case "{": - jsonObject(); - break; - case "[": - jsonArray(); - break; - case "true": - case "false": - case "null": - case "(number)": - case "(string)": - advance(); - break; - case "-": - advance("-"); - if (state.tokens.curr.character !== state.tokens.next.from) { - warning("W011", state.tokens.curr); - } - adjacent(state.tokens.curr, state.tokens.next); - advance("(number)"); - break; + switch (funct[v]) { + case "closure": + case "function": + case "var": + case "unused": + warning("W038", state.tokens.curr, v); + break; + case "label": + warning("W037", state.tokens.curr, v); + break; + case "outer": + case "global": + break; default: - error("E003", state.tokens.next); + // If the name is defined in an outer function, make an outer entry, + // and if it was unused, make it var. + if (s === true) { + funct[v] = true; + } else if (s === null) { + warning("W039", state.tokens.curr, v); + note_implied(state.tokens.curr); + } else if (typeof s !== "object") { + // if we're in a list comprehension, variables are declared + // locally and used before being defined. So we check + // the presence of the given variable in the comp array + // before declaring it undefined. + + if (!funct["(comparray)"].check(v)) { + isundef(funct, "W117", state.tokens.curr, v); + } + + // Explicitly mark the variable as used within function scopes + if (!funct["(global)"]) { + funct[v] = true; + } + + note_implied(state.tokens.curr); + } else { + switch (s[v]) { + case "function": + case "unction": + this["function"] = true; + s[v] = "closure"; + funct[v] = s["(global)"] ? "global" : "outer"; + break; + case "var": + case "unused": + s[v] = "closure"; + funct[v] = s["(global)"] ? "global" : "outer"; + break; + case "const": + setprop(s, v, { unused: false }); + break; + case "closure": + funct[v] = s["(global)"] ? "global" : "outer"; + break; + case "label": + warning("W037", state.tokens.curr, v); + } + } } + } + return this; + }, + + led: function() { + error("E033", state.tokens.next, state.tokens.next.value); + } + }; + + state.syntax["(template)"] = { + type: "(template)", + lbp: 0, + identifier: false, + fud: doTemplateLiteral + }; + + type("(template middle)", function() { + return this; + }); + + type("(template tail)", function() { + return this; + }); + + type("(regexp)", function() { + return this; + }); + + // ECMAScript parser + + delim("(endline)"); + delim("(begin)"); + delim("(end)").reach = true; + delim("(error)").reach = true; + delim("}").reach = true; + delim(")"); + delim("]"); + delim("\"").reach = true; + delim("'").reach = true; + delim(";"); + delim(":").reach = true; + delim("#"); + + reserve("else"); + reserve("case").reach = true; + reserve("catch"); + reserve("default").reach = true; + reserve("finally"); + reservevar("arguments", function(x) { + if (state.directive["use strict"] && funct["(global)"]) { + warning("E008", x); + } + }); + reservevar("eval"); + reservevar("false"); + reservevar("Infinity"); + reservevar("null"); + reservevar("this", function(x) { + if (state.directive["use strict"] && !isMethod() && + !state.option.validthis && ((funct["(statement)"] && + funct["(name)"].charAt(0) > "Z") || funct["(global)"])) { + warning("W040", x); + } + }); + reservevar("true"); + reservevar("undefined"); + + assignop("=", "assign", 20); + assignop("+=", "assignadd", 20); + assignop("-=", "assignsub", 20); + assignop("*=", "assignmult", 20); + assignop("/=", "assigndiv", 20).nud = function() { + error("E014"); + }; + assignop("%=", "assignmod", 20); + + bitwiseassignop("&="); + bitwiseassignop("|="); + bitwiseassignop("^="); + bitwiseassignop("<<="); + bitwiseassignop(">>="); + bitwiseassignop(">>>="); + infix(",", function(left, that) { + var expr; + that.exprs = [left]; + if (!comma({ peek: true })) { + return that; + } + while (true) { + if (!(expr = expression(10))) { + break; + } + that.exprs.push(expr); + if (state.tokens.next.value !== "," || !comma()) { + break; + } + } + return that; + }, 10, true); + + infix("?", function(left, that) { + increaseComplexityCount(); + that.left = left; + that.right = expression(10); + advance(":"); + that["else"] = expression(10); + return that; + }, 30); + + var orPrecendence = 40; + infix("||", function(left, that) { + increaseComplexityCount(); + that.left = left; + that.right = expression(orPrecendence); + return that; + }, orPrecendence); + infix("&&", "and", 50); + bitwise("|", "bitor", 70); + bitwise("^", "bitxor", 80); + bitwise("&", "bitand", 90); + relation("==", function(left, right) { + var eqnull = state.option.eqnull && (left.value === "null" || right.value === "null"); + + switch (true) { + case !eqnull && state.option.eqeqeq: + this.from = this.character; + warning("W116", this, "===", "=="); + break; + case isPoorRelation(left): + warning("W041", this, "===", left.value); + break; + case isPoorRelation(right): + warning("W041", this, "===", right.value); + break; + case isTypoTypeof(right, left): + warning("W122", this, right.value); + break; + case isTypoTypeof(left, right): + warning("W122", this, left.value); + break; } - var blockScope = function () { - var _current = {}; - var _variables = [_current]; + return this; + }); + relation("===", function(left, right) { + if (isTypoTypeof(right, left)) { + warning("W122", this, right.value); + } else if (isTypoTypeof(left, right)) { + warning("W122", this, left.value); + } + return this; + }); + relation("!=", function(left, right) { + var eqnull = state.option.eqnull && + (left.value === "null" || right.value === "null"); - function _checkBlockLabels() { - for (var t in _current) { - if (_current[t]["(type)"] === "unused") { - if (state.option.unused) { - var tkn = _current[t]["(token)"]; - var line = tkn.line; - var chr = tkn.character; - warningAt("W098", line, chr, t); - } - } + if (!eqnull && state.option.eqeqeq) { + this.from = this.character; + warning("W116", this, "!==", "!="); + } else if (isPoorRelation(left)) { + warning("W041", this, "!==", left.value); + } else if (isPoorRelation(right)) { + warning("W041", this, "!==", right.value); + } else if (isTypoTypeof(right, left)) { + warning("W122", this, right.value); + } else if (isTypoTypeof(left, right)) { + warning("W122", this, left.value); + } + return this; + }); + relation("!==", function(left, right) { + if (isTypoTypeof(right, left)) { + warning("W122", this, right.value); + } else if (isTypoTypeof(left, right)) { + warning("W122", this, left.value); + } + return this; + }); + relation("<"); + relation(">"); + relation("<="); + relation(">="); + bitwise("<<", "shiftleft", 120); + bitwise(">>", "shiftright", 120); + bitwise(">>>", "shiftrightunsigned", 120); + infix("in", "in", 120); + infix("instanceof", "instanceof", 120); + infix("+", function(left, that) { + var right = expression(130); + if (left && right && left.id === "(string)" && right.id === "(string)") { + left.value += right.value; + left.character = right.character; + if (!state.option.scripturl && reg.javascriptURL.test(left.value)) { + warning("W050", left); + } + return left; + } + that.left = left; + that.right = right; + return that; + }, 130); + prefix("+", "num"); + prefix("+++", function() { + warning("W007"); + this.arity = "unary"; + this.right = expression(150); + return this; + }); + infix("+++", function(left) { + warning("W007"); + this.left = left; + this.right = expression(130); + return this; + }, 130); + infix("-", "sub", 130); + prefix("-", "neg"); + prefix("---", function() { + warning("W006"); + this.arity = "unary"; + this.right = expression(150); + return this; + }); + infix("---", function(left) { + warning("W006"); + this.left = left; + this.right = expression(130); + return this; + }, 130); + infix("*", "mult", 140); + infix("/", "div", 140); + infix("%", "mod", 140); + + suffix("++"); + prefix("++", "preinc"); + state.syntax["++"].exps = true; + + suffix("--"); + prefix("--", "predec"); + state.syntax["--"].exps = true; + prefix("delete", function() { + var p = expression(10); + if (!p || (p.id !== "." && p.id !== "[")) { + warning("W051"); + } + this.first = p; + + // The `delete` operator accepts unresolvable references when not in strict + // mode, so the operand may be undefined. + if (p.identifier && !state.directive["use strict"]) { + p.forgiveUndef = true; + } + return this; + }).exps = true; + + prefix("~", function() { + if (state.option.bitwise) { + warning("W052", this, "~"); + } + this.arity = "unary"; + expression(150); + return this; + }); + + prefix("...", function() { + if (!state.option.esnext) { + warning("W119", this, "spread/rest operator"); + } + if (!state.tokens.next.identifier) { + error("E030", state.tokens.next, state.tokens.next.value); + } + expression(150); + return this; + }); + + prefix("!", function() { + this.arity = "unary"; + this.right = expression(150); + + if (!this.right) { // '!' followed by nothing? Give up. + quit("E041", this.line || 0); + } + + if (bang[this.right.id] === true) { + warning("W018", this, "!"); + } + return this; + }); + + prefix("typeof", (function() { + var p = expression(150); + this.first = p; + + // The `typeof` operator accepts unresolvable references, so the operand + // may be undefined. + if (p.identifier) { + p.forgiveUndef = true; + } + return this; + })); + prefix("new", function() { + var c = expression(155), i; + if (c && c.id !== "function") { + if (c.identifier) { + c["new"] = true; + switch (c.value) { + case "Number": + case "String": + case "Boolean": + case "Math": + case "JSON": + warning("W053", state.tokens.prev, c.value); + break; + case "Symbol": + if (state.option.esnext) { + warning("W053", state.tokens.prev, c.value); + } + break; + case "Function": + if (!state.option.evil) { + warning("W054"); + } + break; + case "Date": + case "RegExp": + case "this": + break; + default: + if (c.id !== "function") { + i = c.value.substr(0, 1); + if (state.option.newcap && (i < "A" || i > "Z") && !_.has(global, c.value)) { + warning("W055", state.tokens.curr); } + } } + } else { + if (c.id !== "." && c.id !== "[" && c.id !== "(") { + warning("W056", state.tokens.curr); + } + } + } else { + if (!state.option.supernew) + warning("W057", this); + } + if (state.tokens.next.id !== "(" && !state.option.supernew) { + warning("W058", state.tokens.curr, state.tokens.curr.value); + } + this.first = c; + return this; + }); + state.syntax["new"].exps = true; - return { - stack: function () { - _current = {}; - _variables.push(_current); - }, + prefix("void").exps = true; - unstack: function () { - _checkBlockLabels(); - _variables.splice(_variables.length - 1, 1); - _current = _.last(_variables); - }, + infix(".", function(left, that) { + var m = identifier(false, true); - getlabel: function (l) { - for (var i = _variables.length - 1 ; i >= 0; --i) { - if (_.has(_variables[i], l) && !_variables[i][l]["(shadowed)"]) { - return _variables[i]; - } - } - }, + if (typeof m === "string") { + countMember(m); + } - shadow: function (name) { - for (var i = _variables.length - 1; i >= 0; i--) { - if (_.has(_variables[i], name)) { - _variables[i][name]["(shadowed)"] = true; - } - } - }, + that.left = left; + that.right = m; - unshadow: function (name) { - for (var i = _variables.length - 1; i >= 0; i--) { - if (_.has(_variables[i], name)) { - _variables[i][name]["(shadowed)"] = false; - } - } - }, + if (m && m === "hasOwnProperty" && state.tokens.next.value === "=") { + warning("W001"); + } - current: { - has: function (t) { - return _.has(_current, t); - }, + if (left && left.value === "arguments" && (m === "callee" || m === "caller")) { + if (state.option.noarg) + warning("W059", left, m); + else if (state.directive["use strict"]) + error("E008"); + } else if (!state.option.evil && left && left.value === "document" && + (m === "write" || m === "writeln")) { + warning("W060", left); + } - add: function (t, type, tok) { - _current[t] = { "(type)" : type, "(token)": tok, "(shadowed)": false }; - } + if (!state.option.evil && (m === "eval" || m === "execScript")) { + warning("W061"); + } + + return that; + }, 160, true); + + infix("(", function(left, that) { + if (state.option.immed && left && !left.immed && left.id === "function") { + warning("W062"); + } + + var n = 0; + var p = []; + + if (left) { + if (left.type === "(identifier)") { + if (left.value.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)) { + if ("Number String Boolean Date Object Error Symbol".indexOf(left.value) === -1) { + if (left.value === "Math") { + warning("W063", left); + } else if (state.option.newcap) { + warning("W064", left); } - }; + } + } + } + } + + if (state.tokens.next.id !== ")") { + for (;;) { + p[p.length] = expression(10); + n += 1; + if (state.tokens.next.id !== ",") { + break; + } + comma(); + } + } + + advance(")"); + + if (typeof left === "object") { + if (state.option.inES3() && left.value === "parseInt" && n === 1) { + warning("W065", state.tokens.curr); + } + if (!state.option.evil) { + if (left.value === "eval" || left.value === "Function" || + left.value === "execScript") { + warning("W061", left); + + if (p[0] && [0].id === "(string)") { + addInternalSrc(left, p[0].value); + } + } else if (p[0] && p[0].id === "(string)" && + (left.value === "setTimeout" || + left.value === "setInterval")) { + warning("W066", left); + addInternalSrc(left, p[0].value); + + // window.setTimeout/setInterval + } else if (p[0] && p[0].id === "(string)" && + left.value === "." && + left.left.value === "window" && + (left.right === "setTimeout" || + left.right === "setInterval")) { + warning("W066", left); + addInternalSrc(left, p[0].value); + } + } + if (!left.identifier && left.id !== "." && left.id !== "[" && + left.id !== "(" && left.id !== "&&" && left.id !== "||" && + left.id !== "?" && !(state.option.esnext && left["(name)"])) { + warning("W067", that); + } + } + + that.left = left; + return that; + }, 155, true).exps = true; + + prefix("(", function() { + var pn = state.tokens.next, pn1, i = -1; + var ret, triggerFnExpr, first, last; + var parens = 1; + var opening = state.tokens.curr; + var preceeding = state.tokens.prev; + var isNecessary = !state.option.singleGroups; + + do { + if (pn.value === "(") { + parens += 1; + } else if (pn.value === ")") { + parens -= 1; + } + + i += 1; + pn1 = pn; + pn = peek(i); + } while (!(parens === 0 && pn1.value === ")") && pn.value !== ";" && pn.type !== "(end)"); + + if (state.tokens.next.id === "function") { + triggerFnExpr = state.tokens.next.immed = true; + } + + // If the balanced grouping operator is followed by a "fat arrow", the + // current token marks the beginning of a "fat arrow" function and parsing + // should proceed accordingly. + if (pn.value === "=>") { + return doFunction(null, null, null, { parsedParen: true }); + } + + var exprs = []; + + if (state.tokens.next.id !== ")") { + for (;;) { + exprs.push(expression(10)); + + if (state.tokens.next.id !== ",") { + break; + } + comma(); + } + } + + advance(")", this); + if (state.option.immed && exprs[0] && exprs[0].id === "function") { + if (state.tokens.next.id !== "(" && + state.tokens.next.id !== "." && state.tokens.next.id !== "[") { + warning("W068", this); + } + } + + if (!exprs.length) { + return; + } + if (exprs.length > 1) { + ret = Object.create(state.syntax[","]); + ret.exprs = exprs; + + first = exprs[0]; + last = exprs[exprs.length - 1]; + + if (!isNecessary) { + isNecessary = preceeding.assign || preceeding.delim; + } + } else { + ret = first = last = exprs[0]; + + if (!isNecessary) { + isNecessary = + // Used to distinguish from an ExpressionStatement which may not + // begin with the `{` and `function` tokens + (opening.beginsStmt && (ret.id === "{" || triggerFnExpr || isFunctor(ret))) || + // Used as the return value of a single-statement arrow function + (ret.id === "{" && preceeding.id === "=>") || + // Used to prevent left-to-right application of adjacent addition + // operators (the order of which effect type) + (first.id === "+" && preceeding.id === "+"); + } + } + + if (ret) { + // The operator may be necessary to override the default binding power of + // neighboring operators (whenever there is an operator in use within the + // first expression *or* the current group contains multiple expressions) + if (!isNecessary && (first.left || ret.exprs)) { + isNecessary = + (!isBeginOfExpr(preceeding) && first.lbp < preceeding.lbp) || + (!isEndOfExpr() && last.lbp < state.tokens.next.lbp); + } + + if (!isNecessary) { + warning("W126"); + } + + ret.paren = true; + } + + return ret; + }); + + application("=>"); + + infix("[", function(left, that) { + var e = expression(10), s; + if (e && e.type === "(string)") { + if (!state.option.evil && (e.value === "eval" || e.value === "execScript")) { + warning("W061", that); + } + + countMember(e.value); + if (!state.option.sub && reg.identifier.test(e.value)) { + s = state.syntax[e.value]; + if (!s || !isReserved(s)) { + warning("W069", state.tokens.prev, e.value); + } + } + } + advance("]", that); + + if (e && e.value === "hasOwnProperty" && state.tokens.next.value === "=") { + warning("W001"); + } + + that.left = left; + that.right = e; + return that; + }, 160, true); + + function comprehensiveArrayExpression() { + var res = {}; + res.exps = true; + funct["(comparray)"].stack(); + + // Handle reversed for expressions, used in spidermonkey + var reversed = false; + if (state.tokens.next.value !== "for") { + reversed = true; + if (!state.option.inMoz(true)) { + warning("W116", state.tokens.next, "for", state.tokens.next.value); + } + funct["(comparray)"].setState("use"); + res.right = expression(10); + } + + advance("for"); + if (state.tokens.next.value === "each") { + advance("each"); + if (!state.option.inMoz(true)) { + warning("W118", state.tokens.curr, "for each"); + } + } + advance("("); + funct["(comparray)"].setState("define"); + res.left = expression(130); + if (_.contains(["in", "of"], state.tokens.next.value)) { + advance(); + } else { + error("E045", state.tokens.curr); + } + funct["(comparray)"].setState("generate"); + expression(10); + + advance(")"); + if (state.tokens.next.value === "if") { + advance("if"); + advance("("); + funct["(comparray)"].setState("filter"); + res.filter = expression(10); + advance(")"); + } + + if (!reversed) { + funct["(comparray)"].setState("use"); + res.right = expression(10); + } + + advance("]"); + funct["(comparray)"].unstack(); + return res; + } + + prefix("[", function() { + var blocktype = lookupBlockType(); + if (blocktype.isCompArray) { + if (!state.option.inESNext()) { + warning("W119", state.tokens.curr, "array comprehension"); + } + return comprehensiveArrayExpression(); + } else if (blocktype.isDestAssign && !state.option.inESNext()) { + warning("W104", state.tokens.curr, "destructuring assignment"); + } + var b = state.tokens.curr.line !== state.tokens.next.line; + this.first = []; + if (b) { + indent += state.option.indent; + if (state.tokens.next.from === indent + state.option.indent) { + indent += state.option.indent; + } + } + while (state.tokens.next.id !== "(end)") { + while (state.tokens.next.id === ",") { + if (!state.option.elision) { + if (!state.option.inES5()) { + // Maintain compat with old options --- ES5 mode without + // elision=true will warn once per comma + warning("W070"); + } else { + warning("W128"); + do { + advance(","); + } while (state.tokens.next.id === ","); + continue; + } + } + advance(","); + } + + if (state.tokens.next.id === "]") { + break; + } + + this.first.push(expression(10)); + if (state.tokens.next.id === ",") { + comma({ allowTrailing: true }); + if (state.tokens.next.id === "]" && !state.option.inES5(true)) { + warning("W070", state.tokens.curr); + break; + } + } else { + break; + } + } + if (b) { + indent -= state.option.indent; + } + advance("]", this); + return this; + }); + + + function isMethod() { + return funct["(statement)"] && funct["(statement)"].type === "class" || + funct["(context)"] && funct["(context)"]["(verb)"] === "class"; + } + + + function isPropertyName(token) { + return token.identifier || token.id === "(string)" || token.id === "(number)"; + } + + + function propertyName(preserveOrToken) { + var id; + var preserve = true; + if (typeof preserveOrToken === "object") { + id = preserveOrToken; + } else { + preserve = preserveOrToken; + id = optionalidentifier(false, true, preserve); + } + + if (!id) { + if (state.tokens.next.id === "(string)") { + id = state.tokens.next.value; + if (!preserve) { + advance(); + } + } else if (state.tokens.next.id === "(number)") { + id = state.tokens.next.value.toString(); + if (!preserve) { + advance(); + } + } + } else if (typeof id === "object") { + if (id.id === "(string)" || id.id === "(identifier)") id = id.value; + else if (id.id === "(number)") id = id.value.toString(); + } + + if (id === "hasOwnProperty") { + warning("W001"); + } + + return id; + } + + function functionparams(fatarrow) { + var next; + var params = []; + var ident; + var tokens = []; + var t; + var pastDefault = false; + var loneArg = fatarrow && fatarrow.loneArg; + + if (loneArg && loneArg.identifier === true) { + addlabel(loneArg.value, { type: "unused", token: loneArg }); + return [loneArg]; + } + + next = state.tokens.next; + + if (!fatarrow || !fatarrow.parsedParen) { + advance("("); + } + + if (state.tokens.next.id === ")") { + advance(")"); + return; + } + + for (;;) { + if (_.contains(["{", "["], state.tokens.next.id)) { + tokens = destructuringExpression(); + for (t in tokens) { + t = tokens[t]; + if (t.id) { + params.push(t.id); + addlabel(t.id, { type: "unused", token: t.token }); + } + } + } else if (state.tokens.next.value === "...") { + if (!state.option.esnext) { + warning("W119", state.tokens.next, "spread/rest operator"); + } + advance("..."); + ident = identifier(true); + params.push(ident); + addlabel(ident, { type: "unused", token: state.tokens.curr }); + } else { + ident = identifier(true); + params.push(ident); + addlabel(ident, { type: "unused", token: state.tokens.curr }); + } + + // it is a syntax error to have a regular argument after a default argument + if (pastDefault) { + if (state.tokens.next.id !== "=") { + error("E051", state.tokens.current); + } + } + if (state.tokens.next.id === "=") { + if (!state.option.inESNext()) { + warning("W119", state.tokens.next, "default parameters"); + } + advance("="); + pastDefault = true; + expression(10); + } + if (state.tokens.next.id === ",") { + comma(); + } else { + advance(")", next); + return params; + } + } + } + + function setprop(funct, name, values) { + if (!funct["(properties)"][name]) { + funct["(properties)"][name] = { unused: false }; + } + + _.extend(funct["(properties)"][name], values); + } + + function getprop(funct, name, prop) { + if (!funct["(properties)"][name]) + return null; + + return funct["(properties)"][name][prop] || null; + } + + function functor(name, token, scope, overwrites) { + var funct = { + "(name)" : name, + "(breakage)" : 0, + "(loopage)" : 0, + "(scope)" : scope, + "(tokens)" : {}, + "(properties)": {}, + + "(catch)" : false, + "(global)" : false, + + "(line)" : null, + "(character)" : null, + "(metrics)" : null, + "(statement)" : null, + "(context)" : null, + "(blockscope)": null, + "(comparray)" : null, + "(generator)" : null, + "(params)" : null }; - // The actual JSHINT function itself. - var itself = function (s, o, g) { - var i, k, x; - var optionKeys; - var newOptionObj = {}; - var newIgnoredObj = {}; + if (token) { + _.extend(funct, { + "(line)" : token.line, + "(character)": token.character, + "(metrics)" : createMetrics(token) + }); + } - o = _.clone(o); - state.reset(); + _.extend(funct, overwrites); - if (o && o.scope) { - JSHINT.scope = o.scope; + if (funct["(context)"]) { + funct["(blockscope)"] = funct["(context)"]["(blockscope)"]; + funct["(comparray)"] = funct["(context)"]["(comparray)"]; + } + + return funct; + } + + function isFunctor(token) { + return "(scope)" in token; + } + + function doTemplateLiteral() { + while (state.tokens.next.type !== "(template tail)" && state.tokens.next.id !== "(end)") { + advance(); + if (state.tokens.next.type === "(template tail)") { + break; + } else if (state.tokens.next.type !== "(template middle)" && + state.tokens.next.type !== "(end)") { + expression(10); // should probably have different rbp? + } + } + return { + id: "(template)", + type: "(template)" + }; + } + + /** + * @param {Object} [fatarrow] In the case that the function being parsed + * takes the "fat arrow" form, this object will + * contain details about the in-progress parsing + * operation. + * @param {Token} [fatarrow.loneArg] The argument to the function in cases + * where it was defined using the single- + * argument shorthand. + * @param {bool} [fatarrow.parsedParen] Whether the opening parenthesis has + * already been parsed. + */ + function doFunction(name, statement, generator, fatarrow) { + var f; + var oldOption = state.option; + var oldIgnored = state.ignored; + var oldScope = scope; + + state.option = Object.create(state.option); + state.ignored = Object.create(state.ignored); + scope = Object.create(scope); + + funct = functor(name || state.nameStack.infer(), state.tokens.next, scope, { + "(statement)": statement, + "(context)": funct, + "(generator)": generator ? true : null + }); + + f = funct; + state.tokens.curr.funct = funct; + + functions.push(funct); + + if (name) { + addlabel(name, { type: "function" }); + } + + funct["(params)"] = functionparams(fatarrow); + funct["(metrics)"].verifyMaxParametersPerFunction(funct["(params)"]); + + if (fatarrow) { + if (!state.option.esnext) { + warning("W119", state.tokens.curr, "arrow function syntax (=>)"); + } + + if (!fatarrow.loneArg) { + advance("=>"); + } + } + + block(false, true, true, !!fatarrow); + + if (!state.option.noyield && generator && + funct["(generator)"] !== "yielded") { + warning("W124", state.tokens.curr); + } + + funct["(metrics)"].verifyMaxStatementsPerFunction(); + funct["(metrics)"].verifyMaxComplexityPerFunction(); + funct["(unusedOption)"] = state.option.unused; + + scope = oldScope; + state.option = oldOption; + state.ignored = oldIgnored; + funct["(last)"] = state.tokens.curr.line; + funct["(lastcharacter)"] = state.tokens.curr.character; + + _.map(Object.keys(funct), function(key) { + if (key[0] === "(") return; + funct["(blockscope)"].unshadow(key); + }); + + funct = funct["(context)"]; + + return f; + } + + function createMetrics(functionStartToken) { + return { + statementCount: 0, + nestedBlockDepth: -1, + ComplexityCount: 1, + + verifyMaxStatementsPerFunction: function() { + if (state.option.maxstatements && + this.statementCount > state.option.maxstatements) { + warning("W071", functionStartToken, this.statementCount); + } + }, + + verifyMaxParametersPerFunction: function(params) { + params = params || []; + + if (state.option.maxparams && params.length > state.option.maxparams) { + warning("W072", functionStartToken, params.length); + } + }, + + verifyMaxNestedBlockDepthPerFunction: function() { + if (state.option.maxdepth && + this.nestedBlockDepth > 0 && + this.nestedBlockDepth === state.option.maxdepth + 1) { + warning("W073", null, this.nestedBlockDepth); + } + }, + + verifyMaxComplexityPerFunction: function() { + var max = state.option.maxcomplexity; + var cc = this.ComplexityCount; + if (max && cc > max) { + warning("W074", functionStartToken, cc); + } + } + }; + } + + function increaseComplexityCount() { + funct["(metrics)"].ComplexityCount += 1; + } + + // Parse assignments that were found instead of conditionals. + // For example: if (a = 1) { ... } + + function checkCondAssignment(expr) { + var id, paren; + if (expr) { + id = expr.id; + paren = expr.paren; + if (id === "," && (expr = expr.exprs[expr.exprs.length - 1])) { + id = expr.id; + paren = paren || expr.paren; + } + } + switch (id) { + case "=": + case "+=": + case "-=": + case "*=": + case "%=": + case "&=": + case "|=": + case "^=": + case "/=": + if (!paren && !state.option.boss) { + warning("W084"); + } + } + } + + /** + * @param {object} props Collection of property descriptors for a given + * object. + */ + function checkProperties(props) { + // Check for lonely setters if in the ES5 mode. + if (state.option.inES5()) { + for (var name in props) { + if (_.has(props, name) && props[name].setterToken && !props[name].getterToken) { + warning("W078", props[name].setterToken); + } + } + } + } + + (function(x) { + x.nud = function() { + var b, f, i, p, t, g, nextVal; + var props = {}; // All properties, including accessors + + b = state.tokens.curr.line !== state.tokens.next.line; + if (b) { + indent += state.option.indent; + if (state.tokens.next.from === indent + state.option.indent) { + indent += state.option.indent; + } + } + + for (;;) { + if (state.tokens.next.id === "}") { + break; + } + + nextVal = state.tokens.next.value; + if (peek().id !== ":" && (nextVal === "get" || nextVal === "set")) { + advance(nextVal); + + if (!state.option.inES5()) { + error("E034"); + } + + i = propertyName(); + + // ES6 allows for get() {...} and set() {...} method + // definition shorthand syntax, so we don't produce an error + // if the esnext option is enabled. + if (!i && !state.option.inESNext()) { + error("E035"); + } + + // We don't want to save this getter unless it's an actual getter + // and not an ES6 concise method + if (i) { + saveAccessor(nextVal, props, i, state.tokens.curr); + } + + t = state.tokens.next; + f = doFunction(); + p = f["(params)"]; + + // Don't warn about getter/setter pairs if this is an ES6 concise method + if (nextVal === "get" && i && p) { + warning("W076", t, p[0], i); + } else if (nextVal === "set" && i && (!p || p.length !== 1)) { + warning("W077", t, i); + } } else { - JSHINT.errors = []; - JSHINT.undefs = []; - JSHINT.internals = []; - JSHINT.blacklist = {}; - JSHINT.scope = "(main)"; - } - - predefined = Object.create(null); - combine(predefined, vars.ecmaIdentifiers); - combine(predefined, vars.reservedVars); - - combine(predefined, g || {}); - - declared = Object.create(null); - exported = Object.create(null); - - function each(obj, cb) { - if (!obj) - return; - - if (!Array.isArray(obj) && typeof obj === "object") - obj = Object.keys(obj); - - obj.forEach(cb); - } - - if (o) { - each(o.predef || null, function (item) { - var slice, prop; - - if (item[0] === "-") { - slice = item.slice(1); - JSHINT.blacklist[slice] = slice; - } else { - prop = Object.getOwnPropertyDescriptor(o.predef, item); - predefined[item] = prop ? prop.value : false; - } - }); - - each(o.exported || null, function (item) { - exported[item] = true; - }); - - delete o.predef; - delete o.exported; - - optionKeys = Object.keys(o); - for (x = 0; x < optionKeys.length; x++) { - if (/^-W\d{3}$/g.test(optionKeys[x])) { - newIgnoredObj[optionKeys[x].slice(1)] = true; - } else { - newOptionObj[optionKeys[x]] = o[optionKeys[x]]; - - if (optionKeys[x] === "newcap" && o[optionKeys[x]] === false) - newOptionObj["(explicitNewcap)"] = true; - - if (optionKeys[x] === "indent") - newOptionObj["(explicitIndent)"] = o[optionKeys[x]] === false ? false : true; - } + g = false; + if (state.tokens.next.value === "*" && state.tokens.next.type === "(punctuator)") { + if (!state.option.inESNext()) { + warning("W104", state.tokens.next, "generator functions"); } - } - - state.option = newOptionObj; - state.ignored = newIgnoredObj; - - state.option.indent = state.option.indent || 4; - state.option.maxerr = state.option.maxerr || 50; - - indent = 1; - global = Object.create(predefined); - scope = global; - - funct = functor("(global)", null, scope, { - "(global)" : true, - "(blockscope)": blockScope(), - "(comparray)" : arrayComprehension(), - "(metrics)" : createMetrics(state.tokens.next) - }); - - functions = [funct]; - urls = []; - stack = null; - member = {}; - membersOnly = null; - implied = {}; - inblock = false; - lookahead = []; - warnings = 0; - unuseds = []; - - if (!isString(s) && !Array.isArray(s)) { - errorAt("E004", 0); - return false; - } - - api = { - get isJSON() { - return state.jsonMode; - }, - - getOption: function (name) { - return state.option[name] || null; - }, - - getCache: function (name) { - return state.cache[name]; - }, - - setCache: function (name, value) { - state.cache[name] = value; - }, - - warn: function (code, data) { - warningAt.apply(null, [ code, data.line, data.char ].concat(data.data)); - }, - - on: function (names, listener) { - names.split(" ").forEach(function (name) { - emitter.on(name, listener); - }.bind(this)); + advance("*"); + g = true; + } + if (state.tokens.next.identifier && + (peekIgnoreEOL().id === "," || peekIgnoreEOL().id === "}")) { + if (!state.option.inESNext()) { + warning("W104", state.tokens.next, "object short notation"); } - }; + i = propertyName(true); + saveProperty(props, i, state.tokens.next); - emitter.removeAllListeners(); - (extraModules || []).forEach(function (func) { - func(api); - }); - - state.tokens.prev = state.tokens.curr = state.tokens.next = state.syntax["(begin)"]; - - lex = new Lexer(s); - - lex.on("warning", function (ev) { - warningAt.apply(null, [ ev.code, ev.line, ev.character].concat(ev.data)); - }); - - lex.on("error", function (ev) { - errorAt.apply(null, [ ev.code, ev.line, ev.character ].concat(ev.data)); - }); - - lex.on("fatal", function (ev) { - quit("E041", ev.line, ev.from); - }); - - lex.on("Identifier", function (ev) { - emitter.emit("Identifier", ev); - }); - - lex.on("String", function (ev) { - emitter.emit("String", ev); - }); - - lex.on("Number", function (ev) { - emitter.emit("Number", ev); - }); - - lex.start(); - - // Check options - for (var name in o) { - if (_.has(o, name)) { - checkOption(name, state.tokens.curr); - } - } - - assume(); - - // combine the passed globals after we've assumed all our options - combine(predefined, g || {}); - - //reset values - comma.first = true; - - try { - advance(); - switch (state.tokens.next.id) { - case "{": - case "[": - destructuringAssignOrJsonValue(); - break; - default: - directives(); - - if (state.directive["use strict"]) { - if (!state.option.globalstrict && !(state.option.node || state.option.phantom)) { - warning("W097", state.tokens.prev); - } - } else if (state.option.globalstrict && state.option.strict) { - warning("E007"); - } - - statements(); - } - advance((state.tokens.next && state.tokens.next.value !== ".") ? "(end)" : undefined); - funct["(blockscope)"].unstack(); - - var markDefined = function (name, context) { - do { - if (typeof context[name] === "string") { - // JSHINT marks unused variables as 'unused' and - // unused function declaration as 'unction'. This - // code changes such instances back 'var' and - // 'closure' so that the code in JSHINT.data() - // doesn't think they're unused. - - if (context[name] === "unused") - context[name] = "var"; - else if (context[name] === "unction") - context[name] = "closure"; - - return true; - } - - context = context["(context)"]; - } while (context); - - return false; - }; - - var clearImplied = function (name, line) { - if (!implied[name]) - return; - - var newImplied = []; - for (var i = 0; i < implied[name].length; i += 1) { - if (implied[name][i] !== line) - newImplied.push(implied[name][i]); - } - - if (newImplied.length === 0) - delete implied[name]; - else - implied[name] = newImplied; - }; - - var warnUnused = function (name, tkn, type, unused_opt) { - var line = tkn.line; - var chr = tkn.character; - - if (unused_opt === undefined) { - unused_opt = state.option.unused; - } - - if (unused_opt === true) { - unused_opt = "last-param"; - } - - var warnable_types = { - "vars": ["var"], - "last-param": ["var", "param"], - "strict": ["var", "param", "last-param"] - }; - - if (unused_opt) { - if (warnable_types[unused_opt] && warnable_types[unused_opt].indexOf(type) !== -1) { - warningAt("W098", line, chr, name); - } - } - - unuseds.push({ - name: name, - line: line, - character: chr - }); - }; - - var checkUnused = function (func, key) { - var type = func[key]; - var tkn = func["(tokens)"][key]; - - if (key.charAt(0) === "(") - return; - - if (type !== "unused" && type !== "unction" && type !== "const") - return; - - // Params are checked separately from other variables. - if (func["(params)"] && func["(params)"].indexOf(key) !== -1) - return; - - // Variable is in global scope and defined as exported. - if (func["(global)"] && _.has(exported, key)) - return; - - // Is this constant unused? - if (type === "const" && !getprop(func, key, "unused")) - return; - - warnUnused(key, tkn, "var"); - }; - - // Check queued 'x is not defined' instances to see if they're still undefined. - for (i = 0; i < JSHINT.undefs.length; i += 1) { - k = JSHINT.undefs[i].slice(0); - - if (markDefined(k[2].value, k[0])) { - clearImplied(k[2].value, k[2].line); - } else if (state.option.undef) { - warning.apply(warning, k.slice(1)); - } - } - - functions.forEach(function (func) { - if (func["(unusedOption)"] === false) { - return; - } - - for (var key in func) { - if (_.has(func, key)) { - checkUnused(func, key); - } - } - - if (!func["(params)"]) - return; - - var params = func["(params)"].slice(); - var param = params.pop(); - var type, unused_opt; - - while (param) { - type = func[param]; - unused_opt = func["(unusedOption)"] || state.option.unused; - unused_opt = unused_opt === true ? "last-param" : unused_opt; - - // 'undefined' is a special case for (function (window, undefined) { ... })(); - // patterns. - - if (param === "undefined") - return; - - if (type === "unused" || type === "unction") { - warnUnused(param, func["(tokens)"][param], "param", func["(unusedOption)"]); - } else if (unused_opt === "last-param") { - return; - } - - param = params.pop(); - } - }); - - for (var key in declared) { - if (_.has(declared, key) && !_.has(global, key) && !_.has(exported, key)) { - warnUnused(key, declared[key], "var"); - } - } - - } catch (err) { - if (err && err.name === "JSHintError") { - var nt = state.tokens.next || {}; - JSHINT.errors.push({ - scope : "(main)", - raw : err.raw, - code : err.code, - reason : err.message, - line : err.line || nt.line, - character : err.character || nt.from - }, null); + expression(10); + } else { + if (state.tokens.next.id === "[") { + i = computedPropertyName(); + state.nameStack.set(i); } else { - throw err; + state.nameStack.set(state.tokens.next); + i = propertyName(); + saveProperty(props, i, state.tokens.next); + + if (typeof i !== "string") { + break; + } } + + if (state.tokens.next.value === "(") { + if (!state.option.inESNext()) { + warning("W104", state.tokens.curr, "concise methods"); + } + doFunction(null, undefined, g); + } else { + advance(":"); + expression(10); + } + } } - // Loop over the listed "internals", and check them as well. + countMember(i); - if (JSHINT.scope === "(main)") { - o = o || {}; + if (state.tokens.next.id === ",") { + comma({ allowTrailing: true, property: true }); + if (state.tokens.next.id === ",") { + warning("W070", state.tokens.curr); + } else if (state.tokens.next.id === "}" && !state.option.inES5(true)) { + warning("W070", state.tokens.curr); + } + } else { + break; + } + } + if (b) { + indent -= state.option.indent; + } + advance("}", this); - for (i = 0; i < JSHINT.internals.length; i += 1) { - k = JSHINT.internals[i]; - o.scope = k.elem; - itself(k.value, o, g); - } + checkProperties(props); + + return this; + }; + x.fud = function() { + error("E036", state.tokens.curr); + }; + }(delim("{"))); + + function destructuringExpression() { + var id, ids; + var identifiers = []; + if (!state.option.inESNext()) { + warning("W104", state.tokens.curr, "destructuring expression"); + } + var nextInnerDE = function() { + var ident; + if (_.contains(["[", "{"], state.tokens.next.value)) { + ids = destructuringExpression(); + for (var id in ids) { + id = ids[id]; + identifiers.push({ id: id.id, token: id.token }); + } + } else if (state.tokens.next.value === ",") { + identifiers.push({ id: null, token: state.tokens.curr }); + } else if (state.tokens.next.value === "(") { + advance("("); + nextInnerDE(); + advance(")"); + } else { + ident = identifier(); + if (ident) + identifiers.push({ id: ident, token: state.tokens.curr }); + } + }; + if (state.tokens.next.value === "[") { + advance("["); + nextInnerDE(); + while (state.tokens.next.value !== "]") { + advance(","); + nextInnerDE(); + } + advance("]"); + } else if (state.tokens.next.value === "{") { + advance("{"); + id = identifier(); + if (state.tokens.next.value === ":") { + advance(":"); + nextInnerDE(); + } else { + identifiers.push({ id: id, token: state.tokens.curr }); + } + while (state.tokens.next.value !== "}") { + advance(","); + id = identifier(); + if (state.tokens.next.value === ":") { + advance(":"); + nextInnerDE(); + } else { + identifiers.push({ id: id, token: state.tokens.curr }); + } + } + advance("}"); + } + return identifiers; + } + + function destructuringExpressionMatch(tokens, value) { + var first = value.first; + + if (!first) + return; + + _.zip(tokens, Array.isArray(first) ? first : [ first ]).forEach(function(val) { + var token = val[0]; + var value = val[1]; + + if (token && value) + token.first = value; + else if (token && token.first && !value) + warning("W080", token.first, token.first.value); + }); + } + + var conststatement = stmt("const", function(prefix) { + var tokens; + var value; + var lone; // State variable to know if it is a lone identifier, or a destructuring statement. + + if (!state.option.inESNext()) + warning("W104", state.tokens.curr, "const"); + + this.first = []; + for (;;) { + var names = []; + if (_.contains(["{", "["], state.tokens.next.value)) { + tokens = destructuringExpression(); + lone = false; + } else { + tokens = [ { id: identifier(), token: state.tokens.curr } ]; + lone = true; + } + for (var t in tokens) { + if (tokens.hasOwnProperty(t)) { + t = tokens[t]; + if (funct[t.id] === "const") { + warning("E011", null, t.id); + } + if (funct["(global)"] && predefined[t.id] === false) { + warning("W079", t.token, t.id); + } + if (t.id) { + addlabel(t.id, { token: t.token, type: "const", unused: true }); + names.push(t.token); + } + } + } + if (prefix) { + break; + } + + this.first = this.first.concat(names); + + if (state.tokens.next.id !== "=") { + warning("E012", state.tokens.curr, state.tokens.curr.value); + } + + if (state.tokens.next.id === "=") { + advance("="); + if (state.tokens.next.id === "undefined") { + warning("W080", state.tokens.prev, state.tokens.prev.value); + } + if (peek(0).id === "=" && state.tokens.next.identifier) { + warning("W120", state.tokens.next, state.tokens.next.value); + } + value = expression(10); + if (lone) { + tokens[0].first = value; + } else { + destructuringExpressionMatch(names, value); + } + } + + if (state.tokens.next.id !== ",") { + break; + } + comma(); + } + return this; + }); + + conststatement.exps = true; + var varstatement = stmt("var", function(prefix) { + // JavaScript does not have block scope. It only has function scope. So, + // declaring a variable in a block can have unexpected consequences. + var tokens, lone, value; + + this.first = []; + for (;;) { + var names = []; + if (_.contains(["{", "["], state.tokens.next.value)) { + tokens = destructuringExpression(); + lone = false; + } else { + tokens = [ { id: identifier(), token: state.tokens.curr } ]; + lone = true; + } + for (var t in tokens) { + if (tokens.hasOwnProperty(t)) { + t = tokens[t]; + if (state.option.inESNext() && funct[t.id] === "const") { + warning("E011", null, t.id); + } + if (funct["(global)"] && predefined[t.id] === false) { + warning("W079", t.token, t.id); + } + if (t.id) { + addlabel(t.id, { type: "unused", token: t.token }); + names.push(t.token); + } + } + } + if (prefix) { + break; + } + + this.first = this.first.concat(names); + + if (state.tokens.next.id === "=") { + state.nameStack.set(state.tokens.curr); + advance("="); + if (state.tokens.next.id === "undefined") { + warning("W080", state.tokens.prev, state.tokens.prev.value); + } + if (peek(0).id === "=" && state.tokens.next.identifier) { + if (!funct["(params)"] || funct["(params)"].indexOf(state.tokens.next.value) === -1) { + warning("W120", state.tokens.next, state.tokens.next.value); + } + } + value = expression(10); + if (lone) { + tokens[0].first = value; + } else { + destructuringExpressionMatch(names, value); + } + } + + if (state.tokens.next.id !== ",") { + break; + } + comma(); + } + return this; + }); + varstatement.exps = true; + + var letstatement = stmt("let", function(prefix) { + var tokens, lone, value, letblock; + + if (!state.option.inESNext()) { + warning("W104", state.tokens.curr, "let"); + } + + if (state.tokens.next.value === "(") { + if (!state.option.inMoz(true)) { + warning("W118", state.tokens.next, "let block"); + } + advance("("); + funct["(blockscope)"].stack(); + letblock = true; + } else if (funct["(nolet)"]) { + error("E048", state.tokens.curr); + } + + this.first = []; + for (;;) { + var names = []; + if (_.contains(["{", "["], state.tokens.next.value)) { + tokens = destructuringExpression(); + lone = false; + } else { + tokens = [ { id: identifier(), token: state.tokens.curr.value } ]; + lone = true; + } + for (var t in tokens) { + if (tokens.hasOwnProperty(t)) { + t = tokens[t]; + if (state.option.inESNext() && funct[t.id] === "const") { + warning("E011", null, t.id); + } + if (funct["(global)"] && predefined[t.id] === false) { + warning("W079", t.token, t.id); + } + if (t.id && !funct["(nolet)"]) { + addlabel(t.id, { type: "unused", token: t.token, islet: true }); + names.push(t.token); + } + } + } + if (prefix) { + break; + } + + this.first = this.first.concat(names); + + if (state.tokens.next.id === "=") { + advance("="); + if (state.tokens.next.id === "undefined") { + warning("W080", state.tokens.prev, state.tokens.prev.value); + } + if (peek(0).id === "=" && state.tokens.next.identifier) { + warning("W120", state.tokens.next, state.tokens.next.value); + } + value = expression(10); + if (lone) { + tokens[0].first = value; + } else { + destructuringExpressionMatch(names, value); + } + } + + if (state.tokens.next.id !== ",") { + break; + } + comma(); + } + if (letblock) { + advance(")"); + block(true, true); + this.block = true; + funct["(blockscope)"].unstack(); + } + + return this; + }); + letstatement.exps = true; + + blockstmt("class", function() { + return classdef.call(this, true); + }); + + function classdef(stmt) { + /*jshint validthis:true */ + if (!state.option.inESNext()) { + warning("W104", state.tokens.curr, "class"); + } + if (stmt) { + // BindingIdentifier + this.name = identifier(); + addlabel(this.name, { type: "unused", token: state.tokens.curr }); + } else if (state.tokens.next.identifier && state.tokens.next.value !== "extends") { + // BindingIdentifier(opt) + this.name = identifier(); + } else { + this.name = state.nameStack.infer(); + } + classtail(this); + return this; + } + + function classtail(c) { + var strictness = state.directive["use strict"]; + + // ClassHeritage(opt) + if (state.tokens.next.value === "extends") { + advance("extends"); + c.heritage = expression(10); + } + + // A ClassBody is always strict code. + state.directive["use strict"] = true; + advance("{"); + // ClassBody(opt) + c.body = classbody(c); + advance("}"); + state.directive["use strict"] = strictness; + } + + function classbody(c) { + var name; + var isStatic; + var isGenerator; + var getset; + var props = {}; + var staticProps = {}; + var computed; + for (var i = 0; state.tokens.next.id !== "}"; ++i) { + name = state.tokens.next; + isStatic = false; + isGenerator = false; + getset = null; + if (name.id === "*") { + isGenerator = true; + advance("*"); + name = state.tokens.next; + } + if (name.id === "[") { + name = computedPropertyName(); + } else if (isPropertyName(name)) { + // Non-Computed PropertyName + advance(); + computed = false; + if (name.identifier && name.value === "static") { + if (checkPunctuators(state.tokens.next, ["*"])) { + isGenerator = true; + advance("*"); + } + if (isPropertyName(state.tokens.next) || state.tokens.next.id === "[") { + computed = state.tokens.next.id === "["; + isStatic = true; + name = state.tokens.next; + if (state.tokens.next.id === "[") { + name = computedPropertyName(); + } else advance(); + } } - return JSHINT.errors.length === 0; + if (name.identifier && (name.value === "get" || name.value === "set")) { + if (isPropertyName(state.tokens.next) || state.tokens.next.id === "[") { + computed = state.tokens.next.id === "["; + getset = name; + name = state.tokens.next; + if (state.tokens.next.id === "[") { + name = computedPropertyName(); + } else advance(); + } + } + } else { + warning("W052", state.tokens.next, state.tokens.next.value || state.tokens.next.type); + advance(); + continue; + } + + if (!checkPunctuators(state.tokens.next, ["("])) { + // error --- class properties must be methods + error("E054", state.tokens.next, state.tokens.next.value); + while (state.tokens.next.id !== "}" && + !checkPunctuators(state.tokens.next, ["("])) { + advance(); + } + if (state.tokens.next.value !== "(") { + doFunction(undefined, c, false, null); + } + } + + if (!computed) { + // We don't know how to determine if we have duplicate computed property names :( + if (getset) { + saveAccessor( + getset.value, isStatic ? staticProps : props, name.value, name, true, isStatic); + } else { + if (name.value === "constructor") { + state.nameStack.set(c); + } else { + state.nameStack.set(name); + } + saveProperty(isStatic ? staticProps : props, name.value, name, true, isStatic); + } + } + + if (getset && name.value === "constructor") { + var propDesc = getset.value === "get" ? "class getter method" : "class setter method"; + error("E049", name, propDesc, "constructor"); + } else if (name.value === "prototype") { + error("E049", name, "class method", "prototype"); + } + + propertyName(name); + + doFunction(null, c, isGenerator, null); + } + + checkProperties(props); + } + + blockstmt("function", function() { + var generator = false; + if (state.tokens.next.value === "*") { + advance("*"); + if (state.option.inESNext(true)) { + generator = true; + } else { + warning("W119", state.tokens.curr, "function*"); + } + } + if (inblock) { + warning("W082", state.tokens.curr); + + } + var i = optionalidentifier(); + + if (i === undefined) { + warning("W025"); + } + + if (funct[i] === "const") { + warning("E011", null, i); + } + addlabel(i, { type: "unction", token: state.tokens.curr }); + + doFunction(i, { statement: true }, generator); + if (state.tokens.next.id === "(" && state.tokens.next.line === state.tokens.curr.line) { + error("E039"); + } + return this; + }); + + prefix("function", function() { + var generator = false; + + if (state.tokens.next.value === "*") { + if (!state.option.inESNext()) { + warning("W119", state.tokens.curr, "function*"); + } + advance("*"); + generator = true; + } + + var i = optionalidentifier(); + var fn = doFunction(i, undefined, generator); + + function isVariable(name) { return name[0] !== "("; } + function isLocal(name) { return fn[name] === "var"; } + + if (!state.option.loopfunc && funct["(loopage)"]) { + // If the function we just parsed accesses any non-local variables + // trigger a warning. Otherwise, the function is safe even within + // a loop. + if (_.some(fn, function(val, name) { return isVariable(name) && !isLocal(name); })) { + warning("W083"); + } + } + return this; + }); + + blockstmt("if", function() { + var t = state.tokens.next; + increaseComplexityCount(); + state.condition = true; + advance("("); + var expr = expression(0); + checkCondAssignment(expr); + + // When the if is within a for-in loop, check if the condition + // starts with a negation operator + var forinifcheck = null; + if (state.option.forin && state.forinifcheckneeded) { + state.forinifcheckneeded = false; // We only need to analyze the first if inside the loop + forinifcheck = state.forinifchecks[state.forinifchecks.length - 1]; + if (expr.type === "(punctuator)" && expr.value === "!") { + forinifcheck.type = "(negative)"; + } else { + forinifcheck.type = "(positive)"; + } + } + + advance(")", t); + state.condition = false; + var s = block(true, true); + + // When the if is within a for-in loop and the condition has a negative form, + // check if the body contains nothing but a continue statement + if (forinifcheck && forinifcheck.type === "(negative)") { + if (s && s.length === 1 && s[0].type === "(identifier)" && s[0].value === "continue") { + forinifcheck.type = "(negative-with-continue)"; + } + } + + if (state.tokens.next.id === "else") { + advance("else"); + if (state.tokens.next.id === "if" || state.tokens.next.id === "switch") { + statement(); + } else { + block(true, true); + } + } + return this; + }); + + blockstmt("try", function() { + var b; + + function doCatch() { + var oldScope = scope; + var e; + + advance("catch"); + advance("("); + + scope = Object.create(oldScope); + + e = state.tokens.next.value; + if (state.tokens.next.type !== "(identifier)") { + e = null; + warning("E030", state.tokens.next, e); + } + + advance(); + + funct = functor("(catch)", state.tokens.next, scope, { + "(context)" : funct, + "(breakage)" : funct["(breakage)"], + "(loopage)" : funct["(loopage)"], + "(statement)": false, + "(catch)" : true + }); + + if (e) { + addlabel(e, { type: "exception" }); + } + + if (state.tokens.next.value === "if") { + if (!state.option.inMoz(true)) { + warning("W118", state.tokens.curr, "catch filter"); + } + advance("if"); + expression(0); + } + + advance(")"); + + state.tokens.curr.funct = funct; + functions.push(funct); + + block(false); + + scope = oldScope; + + funct["(last)"] = state.tokens.curr.line; + funct["(lastcharacter)"] = state.tokens.curr.character; + funct = funct["(context)"]; + } + + block(true); + + while (state.tokens.next.id === "catch") { + increaseComplexityCount(); + if (b && (!state.option.inMoz(true))) { + warning("W118", state.tokens.next, "multiple catch blocks"); + } + doCatch(); + b = true; + } + + if (state.tokens.next.id === "finally") { + advance("finally"); + block(true); + return; + } + + if (!b) { + error("E021", state.tokens.next, "catch", state.tokens.next.value); + } + + return this; + }); + + blockstmt("while", function() { + var t = state.tokens.next; + funct["(breakage)"] += 1; + funct["(loopage)"] += 1; + increaseComplexityCount(); + advance("("); + checkCondAssignment(expression(0)); + advance(")", t); + block(true, true); + funct["(breakage)"] -= 1; + funct["(loopage)"] -= 1; + return this; + }).labelled = true; + + blockstmt("with", function() { + var t = state.tokens.next; + if (state.directive["use strict"]) { + error("E010", state.tokens.curr); + } else if (!state.option.withstmt) { + warning("W085", state.tokens.curr); + } + + advance("("); + expression(0); + advance(")", t); + block(true, true); + + return this; + }); + + blockstmt("switch", function() { + var t = state.tokens.next; + var g = false; + var noindent = false; + + funct["(breakage)"] += 1; + advance("("); + checkCondAssignment(expression(0)); + advance(")", t); + t = state.tokens.next; + advance("{"); + + if (state.tokens.next.from === indent) + noindent = true; + + if (!noindent) + indent += state.option.indent; + + this.cases = []; + + for (;;) { + switch (state.tokens.next.id) { + case "case": + switch (funct["(verb)"]) { + case "yield": + case "break": + case "case": + case "continue": + case "return": + case "switch": + case "throw": + break; + default: + // You can tell JSHint that you don't use break intentionally by + // adding a comment /* falls through */ on a line just before + // the next `case`. + if (!reg.fallsThrough.test(state.lines[state.tokens.next.line - 2])) { + warning("W086", state.tokens.curr, "case"); + } + } + + advance("case"); + this.cases.push(expression(0)); + increaseComplexityCount(); + g = true; + advance(":"); + funct["(verb)"] = "case"; + break; + case "default": + switch (funct["(verb)"]) { + case "yield": + case "break": + case "continue": + case "return": + case "throw": + break; + default: + // Do not display a warning if 'default' is the first statement or if + // there is a special /* falls through */ comment. + if (this.cases.length) { + if (!reg.fallsThrough.test(state.lines[state.tokens.next.line - 2])) { + warning("W086", state.tokens.curr, "default"); + } + } + } + + advance("default"); + g = true; + advance(":"); + break; + case "}": + if (!noindent) + indent -= state.option.indent; + + advance("}", t); + funct["(breakage)"] -= 1; + funct["(verb)"] = undefined; + return; + case "(end)": + error("E023", state.tokens.next, "}"); + return; + default: + indent += state.option.indent; + if (g) { + switch (state.tokens.curr.id) { + case ",": + error("E040"); + return; + case ":": + g = false; + statements(); + break; + default: + error("E025", state.tokens.curr); + return; + } + } else { + if (state.tokens.curr.id === ":") { + advance(":"); + error("E024", state.tokens.curr, ":"); + statements(); + } else { + error("E021", state.tokens.next, "case", state.tokens.next.value); + return; + } + } + indent -= state.option.indent; + } + } + }).labelled = true; + + stmt("debugger", function() { + if (!state.option.debug) { + warning("W087", this); + } + return this; + }).exps = true; + + (function() { + var x = stmt("do", function() { + funct["(breakage)"] += 1; + funct["(loopage)"] += 1; + increaseComplexityCount(); + + this.first = block(true, true); + advance("while"); + var t = state.tokens.next; + advance("("); + checkCondAssignment(expression(0)); + advance(")", t); + funct["(breakage)"] -= 1; + funct["(loopage)"] -= 1; + return this; + }); + x.labelled = true; + x.exps = true; + }()); + + blockstmt("for", function() { + var s, t = state.tokens.next; + var letscope = false; + var foreachtok = null; + + if (t.value === "each") { + foreachtok = t; + advance("each"); + if (!state.option.inMoz(true)) { + warning("W118", state.tokens.curr, "for each"); + } + } + + funct["(breakage)"] += 1; + funct["(loopage)"] += 1; + increaseComplexityCount(); + advance("("); + + // what kind of for(…) statement it is? for(…of…)? for(…in…)? for(…;…;…)? + var nextop; // contains the token of the "in" or "of" operator + var i = 0; + var inof = ["in", "of"]; + do { + nextop = peek(i); + ++i; + } while (!_.contains(inof, nextop.value) && nextop.value !== ";" && nextop.type !== "(end)"); + + // if we're in a for (… in|of …) statement + if (_.contains(inof, nextop.value)) { + if (!state.option.inESNext() && nextop.value === "of") { + error("W104", nextop, "for of"); + } + + if (state.tokens.next.id === "var") { + advance("var"); + state.syntax["var"].fud.call(state.syntax["var"].fud, true); + } else if (state.tokens.next.id === "let") { + advance("let"); + // create a new block scope + letscope = true; + funct["(blockscope)"].stack(); + state.syntax["let"].fud.call(state.syntax["let"].fud, true); + } else if (!state.tokens.next.identifier) { + error("E030", state.tokens.next, state.tokens.next.type); + advance(); + } else { + switch (funct[state.tokens.next.value]) { + case "unused": + funct[state.tokens.next.value] = "var"; + break; + case "var": + break; + default: + var ident = state.tokens.next.value; + if (!funct["(blockscope)"].getlabel(ident) && + !(scope[ident] || {})[ident]) + warning("W088", state.tokens.next, state.tokens.next.value); + } + advance(); + } + advance(nextop.value); + expression(20); + advance(")", t); + + if (nextop.value === "in" && state.option.forin) { + state.forinifcheckneeded = true; + + if (state.forinifchecks === undefined) { + state.forinifchecks = []; + } + + // Push a new for-in-if check onto the stack. The type will be modified + // when the loop's body is parsed and a suitable if statement exists. + state.forinifchecks.push({ + type: "(none)" + }); + } + + s = block(true, true); + + if (nextop.value === "in" && state.option.forin) { + if (state.forinifchecks && state.forinifchecks.length > 0) { + var check = state.forinifchecks.pop(); + + if (// No if statement or not the first statement in loop body + s && s.length > 0 && (typeof s[0] !== "object" || s[0].value !== "if") || + // Positive if statement is not the only one in loop body + check.type === "(positive)" && s.length > 1 || + // Negative if statement but no continue + check.type === "(negative)") { + warning("W089", this); + } + } + + // Reset the flag in case no if statement was contained in the loop body + state.forinifcheckneeded = false; + } + + funct["(breakage)"] -= 1; + funct["(loopage)"] -= 1; + } else { + if (foreachtok) { + error("E045", foreachtok); + } + if (state.tokens.next.id !== ";") { + if (state.tokens.next.id === "var") { + advance("var"); + state.syntax["var"].fud.call(state.syntax["var"].fud); + } else if (state.tokens.next.id === "let") { + advance("let"); + // create a new block scope + letscope = true; + funct["(blockscope)"].stack(); + state.syntax["let"].fud.call(state.syntax["let"].fud); + } else { + for (;;) { + expression(0, "for"); + if (state.tokens.next.id !== ",") { + break; + } + comma(); + } + } + } + nolinebreak(state.tokens.curr); + advance(";"); + if (state.tokens.next.id !== ";") { + checkCondAssignment(expression(0)); + } + nolinebreak(state.tokens.curr); + advance(";"); + if (state.tokens.next.id === ";") { + error("E021", state.tokens.next, ")", ";"); + } + if (state.tokens.next.id !== ")") { + for (;;) { + expression(0, "for"); + if (state.tokens.next.id !== ",") { + break; + } + comma(); + } + } + advance(")", t); + block(true, true); + funct["(breakage)"] -= 1; + funct["(loopage)"] -= 1; + + } + // unstack loop blockscope + if (letscope) { + funct["(blockscope)"].unstack(); + } + return this; + }).labelled = true; + + + stmt("break", function() { + var v = state.tokens.next.value; + + if (funct["(breakage)"] === 0) + warning("W052", state.tokens.next, this.value); + + if (!state.option.asi) + nolinebreak(this); + + if (state.tokens.next.id !== ";" && !state.tokens.next.reach) { + if (state.tokens.curr.line === state.tokens.next.line) { + if (funct[v] !== "label") { + warning("W090", state.tokens.next, v); + } else if (scope[v] !== funct) { + warning("W091", state.tokens.next, v); + } + this.first = state.tokens.next; + advance(); + } + } + + reachable(this); + + return this; + }).exps = true; + + + stmt("continue", function() { + var v = state.tokens.next.value; + + if (funct["(breakage)"] === 0) + warning("W052", state.tokens.next, this.value); + + if (!state.option.asi) + nolinebreak(this); + + if (state.tokens.next.id !== ";" && !state.tokens.next.reach) { + if (state.tokens.curr.line === state.tokens.next.line) { + if (funct[v] !== "label") { + warning("W090", state.tokens.next, v); + } else if (scope[v] !== funct) { + warning("W091", state.tokens.next, v); + } + this.first = state.tokens.next; + advance(); + } + } else if (!funct["(loopage)"]) { + warning("W052", state.tokens.next, this.value); + } + + reachable(this); + + return this; + }).exps = true; + + + stmt("return", function() { + if (this.line === state.tokens.next.line) { + if (state.tokens.next.id !== ";" && !state.tokens.next.reach) { + this.first = expression(0); + + if (this.first && + this.first.type === "(punctuator)" && this.first.value === "=" && + !this.first.paren && !state.option.boss) { + warningAt("W093", this.first.line, this.first.character); + } + } + } else { + if (state.tokens.next.type === "(punctuator)" && + ["[", "{", "+", "-"].indexOf(state.tokens.next.value) > -1) { + nolinebreak(this); // always warn (Line breaking error) + } + } + + reachable(this); + + return this; + }).exps = true; + + (function(x) { + x.exps = true; + x.lbp = 25; + }(prefix("yield", function() { + var prev = state.tokens.prev; + if (state.option.inESNext(true) && !funct["(generator)"]) { + // If it's a yield within a catch clause inside a generator then that's ok + if (!("(catch)" === funct["(name)"] && funct["(context)"]["(generator)"])) { + error("E046", state.tokens.curr, "yield"); + } + } else if (!state.option.inESNext()) { + warning("W104", state.tokens.curr, "yield"); + } + funct["(generator)"] = "yielded"; + var delegatingYield = false; + + if (state.tokens.next.value === "*") { + delegatingYield = true; + advance("*"); + } + + if (this.line === state.tokens.next.line || !state.option.inMoz(true)) { + if (delegatingYield || + (state.tokens.next.id !== ";" && !state.tokens.next.reach && state.tokens.next.nud)) { + + nobreaknonadjacent(state.tokens.curr, state.tokens.next); + this.first = expression(10); + + if (this.first.type === "(punctuator)" && this.first.value === "=" && + !this.first.paren && !state.option.boss) { + warningAt("W093", this.first.line, this.first.character); + } + } + + if (state.option.inMoz(true) && state.tokens.next.id !== ")" && + (prev.lbp > 30 || (!prev.assign && !isEndOfExpr()) || prev.id === "yield")) { + error("E050", this); + } + } else if (!state.option.asi) { + nolinebreak(this); // always warn (Line breaking error) + } + return this; + }))); + + + stmt("throw", function() { + nolinebreak(this); + this.first = expression(20); + + reachable(this); + + return this; + }).exps = true; + + stmt("import", function() { + if (!state.option.inESNext()) { + warning("W119", state.tokens.curr, "import"); + } + + if (state.tokens.next.type === "(string)") { + // ModuleSpecifier :: StringLiteral + advance("(string)"); + return this; + } + + if (state.tokens.next.identifier) { + // ImportClause :: ImportedDefaultBinding + this.name = identifier(); + addlabel(this.name, { type: "unused", token: state.tokens.curr }); + } else if (state.tokens.next.id === "*") { + // ImportClause :: NameSpaceImport + advance("*"); + advance("as"); + if (state.tokens.next.identifier) { + this.name = identifier(); + addlabel(this.name, { type: "unused", token: state.tokens.curr }); + } + } else { + advance("{"); + for (;;) { + if (state.tokens.next.value === "}") { + advance("}"); + break; + } + var importName; + if (state.tokens.next.type === "default") { + importName = "default"; + advance("default"); + } else { + importName = identifier(); + } + if (state.tokens.next.value === "as") { + advance("as"); + importName = identifier(); + } + addlabel(importName, { type: "unused", token: state.tokens.curr }); + + if (state.tokens.next.value === ",") { + advance(","); + } else if (state.tokens.next.value === "}") { + advance("}"); + break; + } else { + error("E024", state.tokens.next, state.tokens.next.value); + break; + } + } + } + + // FromClause + advance("from"); + advance("(string)"); + return this; + }).exps = true; + + stmt("export", function() { + var ok = true; + if (!state.option.inESNext()) { + warning("W119", state.tokens.curr, "export"); + ok = false; + } + + if (!funct["(global)"] || !funct["(blockscope)"].atTop()) { + error("E053", state.tokens.curr); + ok = false; + } + + if (state.tokens.next.value === "*") { + advance("*"); + advance("from"); + advance("(string)"); + return this; + } + + if (state.tokens.next.type === "default") { + state.nameStack.set(state.tokens.next); + advance("default"); + if (state.tokens.next.id === "function" || state.tokens.next.id === "class") { + this.block = true; + } + this.exportee = expression(10); + + return this; + } + + if (state.tokens.next.value === "{") { + advance("{"); + for (;;) { + var id; + exported[id = identifier(false, false, ok)] = ok; + if (ok) { + funct["(blockscope)"].setExported(id); + } + + if (state.tokens.next.value === ",") { + advance(","); + } else if (state.tokens.next.value === "}") { + advance("}"); + break; + } else { + error("E024", state.tokens.next, state.tokens.next.value); + break; + } + } + return this; + } + + if (state.tokens.next.id === "var") { + advance("var"); + exported[state.tokens.next.value] = ok; + state.tokens.next.exported = true; + state.syntax["var"].fud.call(state.syntax["var"].fud); + } else if (state.tokens.next.id === "let") { + advance("let"); + exported[state.tokens.next.value] = ok; + state.tokens.next.exported = true; + state.syntax["let"].fud.call(state.syntax["let"].fud); + } else if (state.tokens.next.id === "const") { + advance("const"); + exported[state.tokens.next.value] = ok; + state.tokens.next.exported = true; + state.syntax["const"].fud.call(state.syntax["const"].fud); + } else if (state.tokens.next.id === "function") { + this.block = true; + advance("function"); + exported[state.tokens.next.value] = ok; + state.tokens.next.exported = true; + state.syntax["function"].fud(); + } else if (state.tokens.next.id === "class") { + this.block = true; + advance("class"); + exported[state.tokens.next.value] = ok; + state.tokens.next.exported = true; + state.syntax["class"].fud(); + } else { + error("E024", state.tokens.next, state.tokens.next.value); + } + + return this; + }).exps = true; + + // Future Reserved Words + + FutureReservedWord("abstract"); + FutureReservedWord("boolean"); + FutureReservedWord("byte"); + FutureReservedWord("char"); + FutureReservedWord("class", { es5: true, nud: classdef }); + FutureReservedWord("double"); + FutureReservedWord("enum", { es5: true }); + FutureReservedWord("export", { es5: true }); + FutureReservedWord("extends", { es5: true }); + FutureReservedWord("final"); + FutureReservedWord("float"); + FutureReservedWord("goto"); + FutureReservedWord("implements", { es5: true, strictOnly: true }); + FutureReservedWord("import", { es5: true }); + FutureReservedWord("int"); + FutureReservedWord("interface", { es5: true, strictOnly: true }); + FutureReservedWord("long"); + FutureReservedWord("native"); + FutureReservedWord("package", { es5: true, strictOnly: true }); + FutureReservedWord("private", { es5: true, strictOnly: true }); + FutureReservedWord("protected", { es5: true, strictOnly: true }); + FutureReservedWord("public", { es5: true, strictOnly: true }); + FutureReservedWord("short"); + FutureReservedWord("static", { es5: true, strictOnly: true }); + FutureReservedWord("super", { es5: true }); + FutureReservedWord("synchronized"); + FutureReservedWord("transient"); + FutureReservedWord("volatile"); + + // this function is used to determine whether a squarebracket or a curlybracket + // expression is a comprehension array, destructuring assignment or a json value. + + var lookupBlockType = function() { + var pn, pn1; + var i = -1; + var bracketStack = 0; + var ret = {}; + if (checkPunctuators(state.tokens.curr, ["[", "{"])) + bracketStack += 1; + do { + pn = (i === -1) ? state.tokens.next : peek(i); + pn1 = peek(i + 1); + i = i + 1; + if (checkPunctuators(pn, ["[", "{"])) { + bracketStack += 1; + } else if (checkPunctuators(pn, ["]", "}"])) { + bracketStack -= 1; + } + if (pn.identifier && pn.value === "for" && bracketStack === 1) { + ret.isCompArray = true; + ret.notJson = true; + break; + } + if (checkPunctuators(pn, ["}", "]"]) && bracketStack === 0) { + if (pn1.value === "=") { + ret.isDestAssign = true; + ret.notJson = true; + break; + } else if (pn1.value === ".") { + ret.notJson = true; + break; + } + } + if (pn.value === ";") { + ret.isBlock = true; + ret.notJson = true; + } + } while (bracketStack > 0 && pn.id !== "(end)" && i < 15); + return ret; + }; + + function saveProperty(props, name, tkn, isClass, isStatic) { + var msg = ["key", "class method", "static class method"]; + msg = msg[(isClass || false) + (isStatic || false)]; + if (tkn.identifier) { + name = tkn.value; + } + + if (props[name] && _.has(props, name)) { + warning("W075", state.tokens.next, msg, name); + } else { + props[name] = {}; + } + + props[name].basic = true; + props[name].basictkn = tkn; + } + + /** + * @param {string} accessorType - Either "get" or "set" + * @param {object} props - a collection of all properties of the object to + * which the current accessor is being assigned + * @param {object} tkn - the identifier token representing the accessor name + * @param {boolean} isClass - whether the accessor is part of an ES6 Class + * definition + * @param {boolean} isStatic - whether the accessor is a static method + */ + function saveAccessor(accessorType, props, name, tkn, isClass, isStatic) { + var flagName = accessorType === "get" ? "getterToken" : "setterToken"; + var msg = ""; + + if (isClass) { + if (isStatic) { + msg += "static "; + } + msg += accessorType + "ter method"; + } else { + msg = "key"; + } + + state.tokens.curr.accessorType = accessorType; + state.nameStack.set(tkn); + + if (props[name] && _.has(props, name)) { + if (props[name].basic || props[name][flagName]) { + warning("W075", state.tokens.next, msg, name); + } + } else { + props[name] = {}; + } + + props[name][flagName] = tkn; + } + + function computedPropertyName() { + advance("["); + if (!state.option.esnext) { + warning("W119", state.tokens.curr, "computed property names"); + } + var value = expression(10); + advance("]"); + return value; + } + + // Test whether a given token is a punctuator matching one of the specified values + function checkPunctuators(token, values) { + return token.type === "(punctuator)" && _.contains(values, token.value); + } + + // Check whether this function has been reached for a destructuring assign with undeclared values + function destructuringAssignOrJsonValue() { + // lookup for the assignment (esnext only) + // if it has semicolons, it is a block, so go parse it as a block + // or it's not a block, but there are assignments, check for undeclared variables + + var block = lookupBlockType(); + if (block.notJson) { + if (!state.option.inESNext() && block.isDestAssign) { + warning("W104", state.tokens.curr, "destructuring assignment"); + } + statements(); + // otherwise parse json value + } else { + state.option.laxbreak = true; + state.jsonMode = true; + jsonValue(); + } + } + + // array comprehension parsing function + // parses and defines the three states of the list comprehension in order + // to avoid defining global variables, but keeping them to the list comprehension scope + // only. The order of the states are as follows: + // * "use" which will be the returned iterative part of the list comprehension + // * "define" which will define the variables local to the list comprehension + // * "filter" which will help filter out values + + var arrayComprehension = function() { + var CompArray = function() { + this.mode = "use"; + this.variables = []; + }; + var _carrays = []; + var _current; + function declare(v) { + var l = _current.variables.filter(function(elt) { + // if it has, change its undef state + if (elt.value === v) { + elt.undef = false; + return v; + } + }).length; + return l !== 0; + } + function use(v) { + var l = _current.variables.filter(function(elt) { + // and if it has been defined + if (elt.value === v && !elt.undef) { + if (elt.unused === true) { + elt.unused = false; + } + return v; + } + }).length; + // otherwise we warn about it + return (l === 0); + } + return { stack: function() { + _current = new CompArray(); + _carrays.push(_current); + }, + unstack: function() { + _current.variables.filter(function(v) { + if (v.unused) + warning("W098", v.token, v.raw_text || v.value); + if (v.undef) + isundef(v.funct, "W117", v.token, v.value); + }); + _carrays.splice(-1, 1); + _current = _carrays[_carrays.length - 1]; + }, + setState: function(s) { + if (_.contains(["use", "define", "generate", "filter"], s)) + _current.mode = s; + }, + check: function(v) { + if (!_current) { + return; + } + // When we are in "use" state of the list comp, we enqueue that var + if (_current && _current.mode === "use") { + if (use(v)) { + _current.variables.push({ + funct: funct, + token: state.tokens.curr, + value: v, + undef: true, + unused: false + }); + } + return true; + // When we are in "define" state of the list comp, + } else if (_current && _current.mode === "define") { + // check if the variable has been used previously + if (!declare(v)) { + _current.variables.push({ + funct: funct, + token: state.tokens.curr, + value: v, + undef: false, + unused: true + }); + } + return true; + // When we are in the "generate" state of the list comp, + } else if (_current && _current.mode === "generate") { + isundef(funct, "W117", state.tokens.curr, v); + return true; + // When we are in "filter" state, + } else if (_current && _current.mode === "filter") { + // we check whether current variable has been declared + if (use(v)) { + // if not we warn about it + isundef(funct, "W117", state.tokens.curr, v); + } + return true; + } + return false; + } + }; + }; + + + // Parse JSON + + function jsonValue() { + function jsonObject() { + var o = {}, t = state.tokens.next; + advance("{"); + if (state.tokens.next.id !== "}") { + for (;;) { + if (state.tokens.next.id === "(end)") { + error("E026", state.tokens.next, t.line); + } else if (state.tokens.next.id === "}") { + warning("W094", state.tokens.curr); + break; + } else if (state.tokens.next.id === ",") { + error("E028", state.tokens.next); + } else if (state.tokens.next.id !== "(string)") { + warning("W095", state.tokens.next, state.tokens.next.value); + } + if (o[state.tokens.next.value] === true) { + warning("W075", state.tokens.next, state.tokens.next.value); + } else if ((state.tokens.next.value === "__proto__" && + !state.option.proto) || (state.tokens.next.value === "__iterator__" && + !state.option.iterator)) { + warning("W096", state.tokens.next, state.tokens.next.value); + } else { + o[state.tokens.next.value] = true; + } + advance(); + advance(":"); + jsonValue(); + if (state.tokens.next.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + } + + function jsonArray() { + var t = state.tokens.next; + advance("["); + if (state.tokens.next.id !== "]") { + for (;;) { + if (state.tokens.next.id === "(end)") { + error("E027", state.tokens.next, t.line); + } else if (state.tokens.next.id === "]") { + warning("W094", state.tokens.curr); + break; + } else if (state.tokens.next.id === ",") { + error("E028", state.tokens.next); + } + jsonValue(); + if (state.tokens.next.id !== ",") { + break; + } + advance(","); + } + } + advance("]"); + } + + switch (state.tokens.next.id) { + case "{": + jsonObject(); + break; + case "[": + jsonArray(); + break; + case "true": + case "false": + case "null": + case "(number)": + case "(string)": + advance(); + break; + case "-": + advance("-"); + advance("(number)"); + break; + default: + error("E003", state.tokens.next); + } + } + + var blockScope = function() { + var _current = {}; + var _variables = [_current]; + + function _checkBlockLabels() { + for (var t in _current) { + if (_current[t]["(type)"] === "unused") { + if (state.option.unused) { + var tkn = _current[t]["(token)"]; + // Don't report exported labels as unused + if (tkn.exported) { + continue; + } + var line = tkn.line; + var chr = tkn.character; + warningAt("W098", line, chr, t); + } + } + } + } + + return { + stack: function() { + _current = {}; + _variables.push(_current); + }, + + unstack: function() { + _checkBlockLabels(); + _variables.splice(_variables.length - 1, 1); + _current = _.last(_variables); + }, + + getlabel: function(l) { + for (var i = _variables.length - 1 ; i >= 0; --i) { + if (_.has(_variables[i], l) && !_variables[i][l]["(shadowed)"]) { + return _variables[i]; + } + } + }, + + shadow: function(name) { + for (var i = _variables.length - 1; i >= 0; i--) { + if (_.has(_variables[i], name)) { + _variables[i][name]["(shadowed)"] = true; + } + } + }, + + unshadow: function(name) { + for (var i = _variables.length - 1; i >= 0; i--) { + if (_.has(_variables[i], name)) { + _variables[i][name]["(shadowed)"] = false; + } + } + }, + + atTop: function() { + return _variables.length === 1; + }, + + setExported: function(id) { + if (funct["(blockscope)"].atTop()) { + var item = _current[id]; + if (item && item["(token)"]) { + item["(token)"].exported = true; + } + } + }, + + current: { + has: function(t) { + return _.has(_current, t); + }, + + add: function(t, type, tok) { + _current[t] = { "(type)" : type, "(token)": tok, "(shadowed)": false }; + } + } + }; + }; + + var escapeRegex = function(str) { + return str.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&"); + }; + + // The actual JSHINT function itself. + var itself = function(s, o, g) { + var i, k, x, reIgnoreStr, reIgnore; + var optionKeys; + var newOptionObj = {}; + var newIgnoredObj = {}; + + o = _.clone(o); + state.reset(); + + if (o && o.scope) { + JSHINT.scope = o.scope; + } else { + JSHINT.errors = []; + JSHINT.undefs = []; + JSHINT.internals = []; + JSHINT.blacklist = {}; + JSHINT.scope = "(main)"; + } + + predefined = Object.create(null); + combine(predefined, vars.ecmaIdentifiers); + combine(predefined, vars.reservedVars); + + combine(predefined, g || {}); + + declared = Object.create(null); + exported = Object.create(null); + + function each(obj, cb) { + if (!obj) + return; + + if (!Array.isArray(obj) && typeof obj === "object") + obj = Object.keys(obj); + + obj.forEach(cb); + } + + if (o) { + each(o.predef || null, function(item) { + var slice, prop; + + if (item[0] === "-") { + slice = item.slice(1); + JSHINT.blacklist[slice] = slice; + // remove from predefined if there + delete predefined[slice]; + } else { + prop = Object.getOwnPropertyDescriptor(o.predef, item); + predefined[item] = prop ? prop.value : false; + } + }); + + each(o.exported || null, function(item) { + exported[item] = true; + }); + + delete o.predef; + delete o.exported; + + optionKeys = Object.keys(o); + for (x = 0; x < optionKeys.length; x++) { + if (/^-W\d{3}$/g.test(optionKeys[x])) { + newIgnoredObj[optionKeys[x].slice(1)] = true; + } else { + newOptionObj[optionKeys[x]] = o[optionKeys[x]]; + + if (optionKeys[x] === "newcap" && o[optionKeys[x]] === false) + newOptionObj["(explicitNewcap)"] = true; + } + } + } + + state.option = newOptionObj; + state.ignored = newIgnoredObj; + + state.option.indent = state.option.indent || 4; + state.option.maxerr = state.option.maxerr || 50; + + indent = 1; + global = Object.create(predefined); + scope = global; + + funct = functor("(global)", null, scope, { + "(global)" : true, + "(blockscope)": blockScope(), + "(comparray)" : arrayComprehension(), + "(metrics)" : createMetrics(state.tokens.next) + }); + + functions = [funct]; + urls = []; + stack = null; + member = {}; + membersOnly = null; + implied = {}; + inblock = false; + lookahead = []; + unuseds = []; + + if (!isString(s) && !Array.isArray(s)) { + errorAt("E004", 0); + return false; + } + + api = { + get isJSON() { + return state.jsonMode; + }, + + getOption: function(name) { + return state.option[name] || null; + }, + + getCache: function(name) { + return state.cache[name]; + }, + + setCache: function(name, value) { + state.cache[name] = value; + }, + + warn: function(code, data) { + warningAt.apply(null, [ code, data.line, data.char ].concat(data.data)); + }, + + on: function(names, listener) { + names.split(" ").forEach(function(name) { + emitter.on(name, listener); + }.bind(this)); + } }; - // Modules. - itself.addModule = function (func) { - extraModules.push(func); - }; + emitter.removeAllListeners(); + (extraModules || []).forEach(function(func) { + func(api); + }); - itself.addModule(style.register); + state.tokens.prev = state.tokens.curr = state.tokens.next = state.syntax["(begin)"]; - // Data summary. - itself.data = function () { - var data = { - functions: [], - options: state.option + if (o && o.ignoreDelimiters) { + + if (!Array.isArray(o.ignoreDelimiters)) { + o.ignoreDelimiters = [o.ignoreDelimiters]; + } + + o.ignoreDelimiters.forEach(function(delimiterPair) { + if (!delimiterPair.start || !delimiterPair.end) + return; + + reIgnoreStr = escapeRegex(delimiterPair.start) + + "[\\s\\S]*?" + + escapeRegex(delimiterPair.end); + + reIgnore = new RegExp(reIgnoreStr, "ig"); + + s = s.replace(reIgnore, function(match) { + return match.replace(/./g, " "); + }); + }); + } + + lex = new Lexer(s); + + lex.on("warning", function(ev) { + warningAt.apply(null, [ ev.code, ev.line, ev.character].concat(ev.data)); + }); + + lex.on("error", function(ev) { + errorAt.apply(null, [ ev.code, ev.line, ev.character ].concat(ev.data)); + }); + + lex.on("fatal", function(ev) { + quit("E041", ev.line, ev.from); + }); + + lex.on("Identifier", function(ev) { + emitter.emit("Identifier", ev); + }); + + lex.on("String", function(ev) { + emitter.emit("String", ev); + }); + + lex.on("Number", function(ev) { + emitter.emit("Number", ev); + }); + + lex.start(); + + // Check options + for (var name in o) { + if (_.has(o, name)) { + checkOption(name, state.tokens.curr); + } + } + + assume(); + + // combine the passed globals after we've assumed all our options + combine(predefined, g || {}); + + //reset values + comma.first = true; + + try { + advance(); + switch (state.tokens.next.id) { + case "{": + case "[": + destructuringAssignOrJsonValue(); + break; + default: + directives(); + + if (state.directive["use strict"]) { + if (!state.option.globalstrict) { + if (!(state.option.node || state.option.phantom || state.option.browserify)) { + warning("W097", state.tokens.prev); + } + } + } + + statements(); + } + advance((state.tokens.next && state.tokens.next.value !== ".") ? "(end)" : undefined); + funct["(blockscope)"].unstack(); + + var markDefined = function(name, context) { + do { + if (typeof context[name] === "string") { + // JSHINT marks unused variables as 'unused' and + // unused function declaration as 'unction'. This + // code changes such instances back 'var' and + // 'closure' so that the code in JSHINT.data() + // doesn't think they're unused. + + if (context[name] === "unused") + context[name] = "var"; + else if (context[name] === "unction") + context[name] = "closure"; + + return true; + } + + context = context["(context)"]; + } while (context); + + return false; + }; + + var clearImplied = function(name, line) { + if (!implied[name]) + return; + + var newImplied = []; + for (var i = 0; i < implied[name].length; i += 1) { + if (implied[name][i] !== line) + newImplied.push(implied[name][i]); + } + + if (newImplied.length === 0) + delete implied[name]; + else + implied[name] = newImplied; + }; + + var warnUnused = function(name, tkn, type, unused_opt) { + var line = tkn.line; + var chr = tkn.from; + var raw_name = tkn.raw_text || name; + + if (unused_opt === undefined) { + unused_opt = state.option.unused; + } + + if (unused_opt === true) { + unused_opt = "last-param"; + } + + var warnable_types = { + "vars": ["var"], + "last-param": ["var", "param"], + "strict": ["var", "param", "last-param"] }; - var implieds = []; - var members = []; - var fu, f, i, j, n, globals; - - if (itself.errors.length) { - data.errors = itself.errors; - } - - if (state.jsonMode) { - data.json = true; - } - - for (n in implied) { - if (_.has(implied, n)) { - implieds.push({ - name: n, - line: implied[n] - }); + if (unused_opt) { + if (warnable_types[unused_opt] && warnable_types[unused_opt].indexOf(type) !== -1) { + if (!tkn.exported) { + warningAt("W098", line, chr, raw_name); } + } } - if (implieds.length > 0) { - data.implieds = implieds; + unuseds.push({ + name: name, + line: line, + character: chr + }); + }; + + var checkUnused = function(func, key) { + var type = func[key]; + var tkn = func["(tokens)"][key]; + + if (key.charAt(0) === "(") + return; + + if (type !== "unused" && type !== "unction" && type !== "const") + return; + + // Params are checked separately from other variables. + if (func["(params)"] && func["(params)"].indexOf(key) !== -1) + return; + + // Variable is in global scope and defined as exported. + if (func["(global)"] && _.has(exported, key)) + return; + + // Is this constant unused? + if (type === "const" && !getprop(func, key, "unused")) + return; + + warnUnused(key, tkn, "var"); + }; + + // Check queued 'x is not defined' instances to see if they're still undefined. + for (i = 0; i < JSHINT.undefs.length; i += 1) { + k = JSHINT.undefs[i].slice(0); + + if (markDefined(k[2].value, k[0]) || k[2].forgiveUndef) { + clearImplied(k[2].value, k[2].line); + } else if (state.option.undef) { + warning.apply(warning, k.slice(1)); + } + } + + functions.forEach(function(func) { + if (func["(unusedOption)"] === false) { + return; } - if (urls.length > 0) { - data.urls = urls; + for (var key in func) { + if (_.has(func, key)) { + checkUnused(func, key); + } } - globals = Object.keys(scope); - if (globals.length > 0) { - data.globals = globals; + if (!func["(params)"]) + return; + + var params = func["(params)"].slice(); + var param = params.pop(); + var type, unused_opt; + + while (param) { + type = func[param]; + unused_opt = func["(unusedOption)"] || state.option.unused; + unused_opt = unused_opt === true ? "last-param" : unused_opt; + + // 'undefined' is a special case for (function(window, undefined) { ... })(); + // patterns. + + if (param === "undefined") + return; + + if (type === "unused" || type === "unction") { + warnUnused(param, func["(tokens)"][param], "param", func["(unusedOption)"]); + } else if (unused_opt === "last-param") { + return; + } + + param = params.pop(); } + }); - for (i = 1; i < functions.length; i += 1) { - f = functions[i]; - fu = {}; - - for (j = 0; j < functionicity.length; j += 1) { - fu[functionicity[j]] = []; - } - - for (j = 0; j < functionicity.length; j += 1) { - if (fu[functionicity[j]].length === 0) { - delete fu[functionicity[j]]; - } - } - - fu.name = f["(name)"]; - fu.param = f["(params)"]; - fu.line = f["(line)"]; - fu.character = f["(character)"]; - fu.last = f["(last)"]; - fu.lastcharacter = f["(lastcharacter)"]; - - fu.metrics = { - complexity: f["(metrics)"].ComplexityCount, - parameters: (f["(params)"] || []).length, - statements: f["(metrics)"].statementCount - }; - - data.functions.push(fu); + for (var key in declared) { + if (_.has(declared, key) && !_.has(global, key) && !_.has(exported, key)) { + warnUnused(key, declared[key], "var"); } + } - if (unuseds.length > 0) { - data.unused = unuseds; - } + } catch (err) { + if (err && err.name === "JSHintError") { + var nt = state.tokens.next || {}; + JSHINT.errors.push({ + scope : "(main)", + raw : err.raw, + code : err.code, + reason : err.message, + line : err.line || nt.line, + character : err.character || nt.from + }, null); + } else { + throw err; + } + } - members = []; - for (n in member) { - if (typeof member[n] === "number") { - data.member = member; - break; - } - } + // Loop over the listed "internals", and check them as well. - return data; + if (JSHINT.scope === "(main)") { + o = o || {}; + + for (i = 0; i < JSHINT.internals.length; i += 1) { + k = JSHINT.internals[i]; + o.scope = k.elem; + itself(k.value, o, g); + } + } + + return JSHINT.errors.length === 0; + }; + + // Modules. + itself.addModule = function(func) { + extraModules.push(func); + }; + + itself.addModule(style.register); + + // Data summary. + itself.data = function() { + var data = { + functions: [], + options: state.option }; - itself.jshint = itself; + var implieds = []; + var members = []; + var fu, f, i, j, n, globals; - return itself; + if (itself.errors.length) { + data.errors = itself.errors; + } + + if (state.jsonMode) { + data.json = true; + } + + for (n in implied) { + if (_.has(implied, n)) { + implieds.push({ + name: n, + line: implied[n] + }); + } + } + + if (implieds.length > 0) { + data.implieds = implieds; + } + + if (urls.length > 0) { + data.urls = urls; + } + + globals = Object.keys(scope); + if (globals.length > 0) { + data.globals = globals; + } + + for (i = 1; i < functions.length; i += 1) { + f = functions[i]; + fu = {}; + + for (j = 0; j < functionicity.length; j += 1) { + fu[functionicity[j]] = []; + } + + for (j = 0; j < functionicity.length; j += 1) { + if (fu[functionicity[j]].length === 0) { + delete fu[functionicity[j]]; + } + } + + fu.name = f["(name)"]; + fu.param = f["(params)"]; + fu.line = f["(line)"]; + fu.character = f["(character)"]; + fu.last = f["(last)"]; + fu.lastcharacter = f["(lastcharacter)"]; + + fu.metrics = { + complexity: f["(metrics)"].ComplexityCount, + parameters: (f["(params)"] || []).length, + statements: f["(metrics)"].statementCount + }; + + data.functions.push(fu); + } + + if (unuseds.length > 0) { + data.unused = unuseds; + } + + members = []; + for (n in member) { + if (typeof member[n] === "number") { + data.member = member; + break; + } + } + + return data; + }; + + itself.jshint = itself; + + return itself; }()); // Make JSHINT a Node module, if possible. if (typeof exports === "object" && exports) { - exports.JSHINT = JSHINT; + exports.JSHINT = JSHINT; } -},{"./lex.js":14,"./messages.js":15,"./reg.js":16,"./state.js":17,"./style.js":18,"./vars.js":19,"console-browserify":10,"events":5,"underscore":11}],"jshint":[function(require,module,exports){ -module.exports=require('FcDwKk'); -},{}],14:[function(require,module,exports){ -/* - * Lexical analysis and token construction. - */ +},{"./lex.js":13,"./messages.js":14,"./options.js":16,"./reg.js":17,"./state.js":18,"./style.js":19,"./vars.js":20,"console-browserify":10,"events":5,"underscore":12}]},{},[]); -"use strict"; - -var _ = require("underscore"); -var events = require("events"); -var reg = require("./reg.js"); -var state = require("./state.js").state; - -var unicodeData = require("../data/ascii-identifier-data.js"); -var asciiIdentifierStartTable = unicodeData.asciiIdentifierStartTable; -var asciiIdentifierPartTable = unicodeData.asciiIdentifierPartTable; -var nonAsciiIdentifierStartTable = require("../data/non-ascii-identifier-start.js"); -var nonAsciiIdentifierPartTable = require("../data/non-ascii-identifier-part-only.js"); - -// Some of these token types are from JavaScript Parser API -// while others are specific to JSHint parser. -// JS Parser API: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API - -var Token = { - Identifier: 1, - Punctuator: 2, - NumericLiteral: 3, - StringLiteral: 4, - Comment: 5, - Keyword: 6, - NullLiteral: 7, - BooleanLiteral: 8, - RegExp: 9 -}; - -// Object that handles postponed lexing verifications that checks the parsed -// environment state. - -function asyncTrigger() { - var _checks = []; - - return { - push: function (fn) { - _checks.push(fn); - }, - - check: function () { - for (var check = 0; check < _checks.length; ++check) { - _checks[check](); - } - - _checks.splice(0, _checks.length); - } - }; -} - -/* - * Lexer for JSHint. - * - * This object does a char-by-char scan of the provided source code - * and produces a sequence of tokens. - * - * var lex = new Lexer("var i = 0;"); - * lex.start(); - * lex.token(); // returns the next token - * - * You have to use the token() method to move the lexer forward - * but you don't have to use its return value to get tokens. In addition - * to token() method returning the next token, the Lexer object also - * emits events. - * - * lex.on("Identifier", function (data) { - * if (data.name.indexOf("_") >= 0) { - * // Produce a warning. - * } - * }); - * - * Note that the token() method returns tokens in a JSLint-compatible - * format while the event emitter uses a slightly modified version of - * Mozilla's JavaScript Parser API. Eventually, we will move away from - * JSLint format. - */ -function Lexer(source) { - var lines = source; - - if (typeof lines === "string") { - lines = lines - .replace(/\r\n/g, "\n") - .replace(/\r/g, "\n") - .split("\n"); - } - - // If the first line is a shebang (#!), make it a blank and move on. - // Shebangs are used by Node scripts. - - if (lines[0] && lines[0].substr(0, 2) === "#!") { - if (lines[0].indexOf("node") !== -1) { - state.option.node = true; - } - lines[0] = ""; - } - - this.emitter = new events.EventEmitter(); - this.source = source; - this.setLines(lines); - this.prereg = true; - - this.line = 0; - this.char = 1; - this.from = 1; - this.input = ""; - - for (var i = 0; i < state.option.indent; i += 1) { - state.tab += " "; - } -} - -Lexer.prototype = { - _lines: [], - - getLines: function () { - this._lines = state.lines; - return this._lines; - }, - - setLines: function (val) { - this._lines = val; - state.lines = this._lines; - }, - - /* - * Return the next i character without actually moving the - * char pointer. - */ - peek: function (i) { - return this.input.charAt(i || 0); - }, - - /* - * Move the char pointer forward i times. - */ - skip: function (i) { - i = i || 1; - this.char += i; - this.input = this.input.slice(i); - }, - - /* - * Subscribe to a token event. The API for this method is similar - * Underscore.js i.e. you can subscribe to multiple events with - * one call: - * - * lex.on("Identifier Number", function (data) { - * // ... - * }); - */ - on: function (names, listener) { - names.split(" ").forEach(function (name) { - this.emitter.on(name, listener); - }.bind(this)); - }, - - /* - * Trigger a token event. All arguments will be passed to each - * listener. - */ - trigger: function () { - this.emitter.emit.apply(this.emitter, Array.prototype.slice.call(arguments)); - }, - - /* - * Postpone a token event. the checking condition is set as - * last parameter, and the trigger function is called in a - * stored callback. To be later called using the check() function - * by the parser. This avoids parser's peek() to give the lexer - * a false context. - */ - triggerAsync: function (type, args, checks, fn) { - checks.push(function () { - if (fn()) { - this.trigger(type, args); - } - }.bind(this)); - }, - - /* - * Extract a punctuator out of the next sequence of characters - * or return 'null' if its not possible. - * - * This method's implementation was heavily influenced by the - * scanPunctuator function in the Esprima parser's source code. - */ - scanPunctuator: function () { - var ch1 = this.peek(); - var ch2, ch3, ch4; - - switch (ch1) { - // Most common single-character punctuators - case ".": - if ((/^[0-9]$/).test(this.peek(1))) { - return null; - } - if (this.peek(1) === "." && this.peek(2) === ".") { - return { - type: Token.Punctuator, - value: "..." - }; - } - /* falls through */ - case "(": - case ")": - case ";": - case ",": - case "{": - case "}": - case "[": - case "]": - case ":": - case "~": - case "?": - return { - type: Token.Punctuator, - value: ch1 - }; - - // A pound sign (for Node shebangs) - case "#": - return { - type: Token.Punctuator, - value: ch1 - }; - - // We're at the end of input - case "": - return null; - } - - // Peek more characters - - ch2 = this.peek(1); - ch3 = this.peek(2); - ch4 = this.peek(3); - - // 4-character punctuator: >>>= - - if (ch1 === ">" && ch2 === ">" && ch3 === ">" && ch4 === "=") { - return { - type: Token.Punctuator, - value: ">>>=" - }; - } - - // 3-character punctuators: === !== >>> <<= >>= - - if (ch1 === "=" && ch2 === "=" && ch3 === "=") { - return { - type: Token.Punctuator, - value: "===" - }; - } - - if (ch1 === "!" && ch2 === "=" && ch3 === "=") { - return { - type: Token.Punctuator, - value: "!==" - }; - } - - if (ch1 === ">" && ch2 === ">" && ch3 === ">") { - return { - type: Token.Punctuator, - value: ">>>" - }; - } - - if (ch1 === "<" && ch2 === "<" && ch3 === "=") { - return { - type: Token.Punctuator, - value: "<<=" - }; - } - - if (ch1 === ">" && ch2 === ">" && ch3 === "=") { - return { - type: Token.Punctuator, - value: ">>=" - }; - } - - // Fat arrow punctuator - if (ch1 === "=" && ch2 === ">") { - return { - type: Token.Punctuator, - value: ch1 + ch2 - }; - } - - // 2-character punctuators: <= >= == != ++ -- << >> && || - // += -= *= %= &= |= ^= (but not /=, see below) - if (ch1 === ch2 && ("+-<>&|".indexOf(ch1) >= 0)) { - return { - type: Token.Punctuator, - value: ch1 + ch2 - }; - } - - if ("<>=!+-*%&|^".indexOf(ch1) >= 0) { - if (ch2 === "=") { - return { - type: Token.Punctuator, - value: ch1 + ch2 - }; - } - - return { - type: Token.Punctuator, - value: ch1 - }; - } - - // Special case: /=. We need to make sure that this is an - // operator and not a regular expression. - - if (ch1 === "/") { - if (ch2 === "=" && /\/=(?!(\S*\/[gim]?))/.test(this.input)) { - // /= is not a part of a regular expression, return it as a - // punctuator. - return { - type: Token.Punctuator, - value: "/=" - }; - } - - return { - type: Token.Punctuator, - value: "/" - }; - } - - return null; - }, - - /* - * Extract a comment out of the next sequence of characters and/or - * lines or return 'null' if its not possible. Since comments can - * span across multiple lines this method has to move the char - * pointer. - * - * In addition to normal JavaScript comments (// and /*) this method - * also recognizes JSHint- and JSLint-specific comments such as - * /*jshint, /*jslint, /*globals and so on. - */ - scanComments: function () { - var ch1 = this.peek(); - var ch2 = this.peek(1); - var rest = this.input.substr(2); - var startLine = this.line; - var startChar = this.char; - - // Create a comment token object and make sure it - // has all the data JSHint needs to work with special - // comments. - - function commentToken(label, body, opt) { - var special = ["jshint", "jslint", "members", "member", "globals", "global", "exported"]; - var isSpecial = false; - var value = label + body; - var commentType = "plain"; - opt = opt || {}; - - if (opt.isMultiline) { - value += "*/"; - } - - special.forEach(function (str) { - if (isSpecial) { - return; - } - - // Don't recognize any special comments other than jshint for single-line - // comments. This introduced many problems with legit comments. - if (label === "//" && str !== "jshint") { - return; - } - - if (body.substr(0, str.length) === str) { - isSpecial = true; - label = label + str; - body = body.substr(str.length); - } - - if (!isSpecial && body.charAt(0) === " " && body.substr(1, str.length) === str) { - isSpecial = true; - label = label + " " + str; - body = body.substr(str.length + 1); - } - - if (!isSpecial) { - return; - } - - switch (str) { - case "member": - commentType = "members"; - break; - case "global": - commentType = "globals"; - break; - default: - commentType = str; - } - }); - - return { - type: Token.Comment, - commentType: commentType, - value: value, - body: body, - isSpecial: isSpecial, - isMultiline: opt.isMultiline || false, - isMalformed: opt.isMalformed || false - }; - } - - // End of unbegun comment. Raise an error and skip that input. - if (ch1 === "*" && ch2 === "/") { - this.trigger("error", { - code: "E018", - line: startLine, - character: startChar - }); - - this.skip(2); - return null; - } - - // Comments must start either with // or /* - if (ch1 !== "/" || (ch2 !== "*" && ch2 !== "/")) { - return null; - } - - // One-line comment - if (ch2 === "/") { - this.skip(this.input.length); // Skip to the EOL. - return commentToken("//", rest); - } - - var body = ""; - - /* Multi-line comment */ - if (ch2 === "*") { - this.skip(2); - - while (this.peek() !== "*" || this.peek(1) !== "/") { - if (this.peek() === "") { // End of Line - body += "\n"; - - // If we hit EOF and our comment is still unclosed, - // trigger an error and end the comment implicitly. - if (!this.nextLine()) { - this.trigger("error", { - code: "E017", - line: startLine, - character: startChar - }); - - return commentToken("/*", body, { - isMultiline: true, - isMalformed: true - }); - } - } else { - body += this.peek(); - this.skip(); - } - } - - this.skip(2); - return commentToken("/*", body, { isMultiline: true }); - } - }, - - /* - * Extract a keyword out of the next sequence of characters or - * return 'null' if its not possible. - */ - scanKeyword: function () { - var result = /^[a-zA-Z_$][a-zA-Z0-9_$]*/.exec(this.input); - var keywords = [ - "if", "in", "do", "var", "for", "new", - "try", "let", "this", "else", "case", - "void", "with", "enum", "while", "break", - "catch", "throw", "const", "yield", "class", - "super", "return", "typeof", "delete", - "switch", "export", "import", "default", - "finally", "extends", "function", "continue", - "debugger", "instanceof" - ]; - - if (result && keywords.indexOf(result[0]) >= 0) { - return { - type: Token.Keyword, - value: result[0] - }; - } - - return null; - }, - - /* - * Extract a JavaScript identifier out of the next sequence of - * characters or return 'null' if its not possible. In addition, - * to Identifier this method can also produce BooleanLiteral - * (true/false) and NullLiteral (null). - */ - scanIdentifier: function () { - var id = ""; - var index = 0; - var type, char; - - function isNonAsciiIdentifierStart(code) { - return nonAsciiIdentifierStartTable.indexOf(code) > -1; - } - - function isNonAsciiIdentifierPart(code) { - return isNonAsciiIdentifierStart(code) || nonAsciiIdentifierPartTable.indexOf(code) > -1; - } - - function isHexDigit(str) { - return (/^[0-9a-fA-F]$/).test(str); - } - - var readUnicodeEscapeSequence = function () { - /*jshint validthis:true */ - index += 1; - - if (this.peek(index) !== "u") { - return null; - } - - var ch1 = this.peek(index + 1); - var ch2 = this.peek(index + 2); - var ch3 = this.peek(index + 3); - var ch4 = this.peek(index + 4); - var code; - - if (isHexDigit(ch1) && isHexDigit(ch2) && isHexDigit(ch3) && isHexDigit(ch4)) { - code = parseInt(ch1 + ch2 + ch3 + ch4, 16); - - if (asciiIdentifierPartTable[code] || isNonAsciiIdentifierPart(code)) { - index += 5; - return "\\u" + ch1 + ch2 + ch3 + ch4; - } - - return null; - } - - return null; - }.bind(this); - - var getIdentifierStart = function () { - /*jshint validthis:true */ - var chr = this.peek(index); - var code = chr.charCodeAt(0); - - if (code === 92) { - return readUnicodeEscapeSequence(); - } - - if (code < 128) { - if (asciiIdentifierStartTable[code]) { - index += 1; - return chr; - } - - return null; - } - - if (isNonAsciiIdentifierStart(code)) { - index += 1; - return chr; - } - - return null; - }.bind(this); - - var getIdentifierPart = function () { - /*jshint validthis:true */ - var chr = this.peek(index); - var code = chr.charCodeAt(0); - - if (code === 92) { - return readUnicodeEscapeSequence(); - } - - if (code < 128) { - if (asciiIdentifierPartTable[code]) { - index += 1; - return chr; - } - - return null; - } - - if (isNonAsciiIdentifierPart(code)) { - index += 1; - return chr; - } - - return null; - }.bind(this); - - char = getIdentifierStart(); - if (char === null) { - return null; - } - - id = char; - for (;;) { - char = getIdentifierPart(); - - if (char === null) { - break; - } - - id += char; - } - - switch (id) { - case "true": - case "false": - type = Token.BooleanLiteral; - break; - case "null": - type = Token.NullLiteral; - break; - default: - type = Token.Identifier; - } - - return { - type: type, - value: id - }; - }, - - /* - * Extract a numeric literal out of the next sequence of - * characters or return 'null' if its not possible. This method - * supports all numeric literals described in section 7.8.3 - * of the EcmaScript 5 specification. - * - * This method's implementation was heavily influenced by the - * scanNumericLiteral function in the Esprima parser's source code. - */ - scanNumericLiteral: function () { - var index = 0; - var value = ""; - var length = this.input.length; - var char = this.peek(index); - var bad; - - function isDecimalDigit(str) { - return (/^[0-9]$/).test(str); - } - - function isOctalDigit(str) { - return (/^[0-7]$/).test(str); - } - - function isHexDigit(str) { - return (/^[0-9a-fA-F]$/).test(str); - } - - function isIdentifierStart(ch) { - return (ch === "$") || (ch === "_") || (ch === "\\") || - (ch >= "a" && ch <= "z") || (ch >= "A" && ch <= "Z"); - } - - // Numbers must start either with a decimal digit or a point. - - if (char !== "." && !isDecimalDigit(char)) { - return null; - } - - if (char !== ".") { - value = this.peek(index); - index += 1; - char = this.peek(index); - - if (value === "0") { - // Base-16 numbers. - if (char === "x" || char === "X") { - index += 1; - value += char; - - while (index < length) { - char = this.peek(index); - if (!isHexDigit(char)) { - break; - } - value += char; - index += 1; - } - - if (value.length <= 2) { // 0x - return { - type: Token.NumericLiteral, - value: value, - isMalformed: true - }; - } - - if (index < length) { - char = this.peek(index); - if (isIdentifierStart(char)) { - return null; - } - } - - return { - type: Token.NumericLiteral, - value: value, - base: 16, - isMalformed: false - }; - } - - // Base-8 numbers. - if (isOctalDigit(char)) { - index += 1; - value += char; - bad = false; - - while (index < length) { - char = this.peek(index); - - // Numbers like '019' (note the 9) are not valid octals - // but we still parse them and mark as malformed. - - if (isDecimalDigit(char)) { - bad = true; - } else if (!isOctalDigit(char)) { - break; - } - value += char; - index += 1; - } - - if (index < length) { - char = this.peek(index); - if (isIdentifierStart(char)) { - return null; - } - } - - return { - type: Token.NumericLiteral, - value: value, - base: 8, - isMalformed: false - }; - } - - // Decimal numbers that start with '0' such as '09' are illegal - // but we still parse them and return as malformed. - - if (isDecimalDigit(char)) { - index += 1; - value += char; - } - } - - while (index < length) { - char = this.peek(index); - if (!isDecimalDigit(char)) { - break; - } - value += char; - index += 1; - } - } - - // Decimal digits. - - if (char === ".") { - value += char; - index += 1; - - while (index < length) { - char = this.peek(index); - if (!isDecimalDigit(char)) { - break; - } - value += char; - index += 1; - } - } - - // Exponent part. - - if (char === "e" || char === "E") { - value += char; - index += 1; - char = this.peek(index); - - if (char === "+" || char === "-") { - value += this.peek(index); - index += 1; - } - - char = this.peek(index); - if (isDecimalDigit(char)) { - value += char; - index += 1; - - while (index < length) { - char = this.peek(index); - if (!isDecimalDigit(char)) { - break; - } - value += char; - index += 1; - } - } else { - return null; - } - } - - if (index < length) { - char = this.peek(index); - if (isIdentifierStart(char)) { - return null; - } - } - - return { - type: Token.NumericLiteral, - value: value, - base: 10, - isMalformed: !isFinite(value) - }; - }, - - /* - * Extract a string out of the next sequence of characters and/or - * lines or return 'null' if its not possible. Since strings can - * span across multiple lines this method has to move the char - * pointer. - * - * This method recognizes pseudo-multiline JavaScript strings: - * - * var str = "hello\ - * world"; - */ - scanStringLiteral: function (checks) { - /*jshint loopfunc:true */ - var quote = this.peek(); - - // String must start with a quote. - if (quote !== "\"" && quote !== "'") { - return null; - } - - // In JSON strings must always use double quotes. - this.triggerAsync("warning", { - code: "W108", - line: this.line, - character: this.char // +1? - }, checks, function () { return state.jsonMode && quote !== "\""; }); - - var value = ""; - var startLine = this.line; - var startChar = this.char; - var allowNewLine = false; - - this.skip(); - - while (this.peek() !== quote) { - while (this.peek() === "") { // End Of Line - - // If an EOL is not preceded by a backslash, show a warning - // and proceed like it was a legit multi-line string where - // author simply forgot to escape the newline symbol. - // - // Another approach is to implicitly close a string on EOL - // but it generates too many false positives. - - if (!allowNewLine) { - this.trigger("warning", { - code: "W112", - line: this.line, - character: this.char - }); - } else { - allowNewLine = false; - - // Otherwise show a warning if multistr option was not set. - // For JSON, show warning no matter what. - - this.triggerAsync("warning", { - code: "W043", - line: this.line, - character: this.char - }, checks, function () { return !state.option.multistr; }); - - this.triggerAsync("warning", { - code: "W042", - line: this.line, - character: this.char - }, checks, function () { return state.jsonMode && state.option.multistr; }); - } - - // If we get an EOF inside of an unclosed string, show an - // error and implicitly close it at the EOF point. - - if (!this.nextLine()) { - this.trigger("error", { - code: "E029", - line: startLine, - character: startChar - }); - - return { - type: Token.StringLiteral, - value: value, - isUnclosed: true, - quote: quote - }; - } - } - - allowNewLine = false; - var char = this.peek(); - var jump = 1; // A length of a jump, after we're done - // parsing this character. - - if (char < " ") { - // Warn about a control character in a string. - this.trigger("warning", { - code: "W113", - line: this.line, - character: this.char, - data: [ "" ] - }); - } - - // Special treatment for some escaped characters. - - if (char === "\\") { - this.skip(); - char = this.peek(); - - switch (char) { - case "'": - this.triggerAsync("warning", { - code: "W114", - line: this.line, - character: this.char, - data: [ "\\'" ] - }, checks, function () {return state.jsonMode; }); - break; - case "b": - char = "\\b"; - break; - case "f": - char = "\\f"; - break; - case "n": - char = "\\n"; - break; - case "r": - char = "\\r"; - break; - case "t": - char = "\\t"; - break; - case "0": - char = "\\0"; - - // Octal literals fail in strict mode. - // Check if the number is between 00 and 07. - var n = parseInt(this.peek(1), 10); - this.triggerAsync("warning", { - code: "W115", - line: this.line, - character: this.char - }, checks, - function () { return n >= 0 && n <= 7 && state.directive["use strict"]; }); - break; - case "u": - char = String.fromCharCode(parseInt(this.input.substr(1, 4), 16)); - jump = 5; - break; - case "v": - this.triggerAsync("warning", { - code: "W114", - line: this.line, - character: this.char, - data: [ "\\v" ] - }, checks, function () { return state.jsonMode; }); - - char = "\v"; - break; - case "x": - var x = parseInt(this.input.substr(1, 2), 16); - - this.triggerAsync("warning", { - code: "W114", - line: this.line, - character: this.char, - data: [ "\\x-" ] - }, checks, function () { return state.jsonMode; }); - - char = String.fromCharCode(x); - jump = 3; - break; - case "\\": - char = "\\\\"; - break; - case "\"": - char = "\\\""; - break; - case "/": - break; - case "": - allowNewLine = true; - char = ""; - break; - case "!": - if (value.slice(value.length - 2) === "<") { - break; - } - - /*falls through */ - default: - // Weird escaping. - this.trigger("warning", { - code: "W044", - line: this.line, - character: this.char - }); - } - } - - value += char; - this.skip(jump); - } - - this.skip(); - return { - type: Token.StringLiteral, - value: value, - isUnclosed: false, - quote: quote - }; - }, - - /* - * Extract a regular expression out of the next sequence of - * characters and/or lines or return 'null' if its not possible. - * - * This method is platform dependent: it accepts almost any - * regular expression values but then tries to compile and run - * them using system's RegExp object. This means that there are - * rare edge cases where one JavaScript engine complains about - * your regular expression while others don't. - */ - scanRegExp: function () { - var index = 0; - var length = this.input.length; - var char = this.peek(); - var value = char; - var body = ""; - var flags = []; - var malformed = false; - var isCharSet = false; - var terminated; - - var scanUnexpectedChars = function () { - // Unexpected control character - if (char < " ") { - malformed = true; - this.trigger("warning", { - code: "W048", - line: this.line, - character: this.char - }); - } - - // Unexpected escaped character - if (char === "<") { - malformed = true; - this.trigger("warning", { - code: "W049", - line: this.line, - character: this.char, - data: [ char ] - }); - } - }.bind(this); - - // Regular expressions must start with '/' - if (!this.prereg || char !== "/") { - return null; - } - - index += 1; - terminated = false; - - // Try to get everything in between slashes. A couple of - // cases aside (see scanUnexpectedChars) we don't really - // care whether the resulting expression is valid or not. - // We will check that later using the RegExp object. - - while (index < length) { - char = this.peek(index); - value += char; - body += char; - - if (isCharSet) { - if (char === "]") { - if (this.peek(index - 1) !== "\\" || this.peek(index - 2) === "\\") { - isCharSet = false; - } - } - - if (char === "\\") { - index += 1; - char = this.peek(index); - body += char; - value += char; - - scanUnexpectedChars(); - } - - index += 1; - continue; - } - - if (char === "\\") { - index += 1; - char = this.peek(index); - body += char; - value += char; - - scanUnexpectedChars(); - - if (char === "/") { - index += 1; - continue; - } - - if (char === "[") { - index += 1; - continue; - } - } - - if (char === "[") { - isCharSet = true; - index += 1; - continue; - } - - if (char === "/") { - body = body.substr(0, body.length - 1); - terminated = true; - index += 1; - break; - } - - index += 1; - } - - // A regular expression that was never closed is an - // error from which we cannot recover. - - if (!terminated) { - this.trigger("error", { - code: "E015", - line: this.line, - character: this.from - }); - - return void this.trigger("fatal", { - line: this.line, - from: this.from - }); - } - - // Parse flags (if any). - - while (index < length) { - char = this.peek(index); - if (!/[gim]/.test(char)) { - break; - } - flags.push(char); - value += char; - index += 1; - } - - // Check regular expression for correctness. - - try { - new RegExp(body, flags.join("")); - } catch (err) { - malformed = true; - this.trigger("error", { - code: "E016", - line: this.line, - character: this.char, - data: [ err.message ] // Platform dependent! - }); - } - - return { - type: Token.RegExp, - value: value, - flags: flags, - isMalformed: malformed - }; - }, - - /* - * Scan for any occurence of mixed tabs and spaces. If smarttabs option - * is on, ignore tabs followed by spaces. - * - * Tabs followed by one space followed by a block comment are allowed. - */ - scanMixedSpacesAndTabs: function () { - var at, match; - - if (state.option.smarttabs) { - // Negative look-behind for "//" - match = this.input.match(/(\/\/|^\s?\*)? \t/); - at = match && !match[1] ? 0 : -1; - } else { - at = this.input.search(/ \t|\t [^\*]/); - } - - return at; - }, - - /* - * Scan for characters that get silently deleted by one or more browsers. - */ - scanUnsafeChars: function () { - return this.input.search(reg.unsafeChars); - }, - - /* - * Produce the next raw token or return 'null' if no tokens can be matched. - * This method skips over all space characters. - */ - next: function (checks) { - this.from = this.char; - - // Move to the next non-space character. - var start; - if (/\s/.test(this.peek())) { - start = this.char; - - while (/\s/.test(this.peek())) { - this.from += 1; - this.skip(); - } - - if (this.peek() === "") { // EOL - if (!/^\s*$/.test(this.getLines()[this.line - 1]) && state.option.trailing) { - this.trigger("warning", { code: "W102", line: this.line, character: start }); - } - } - } - - // Methods that work with multi-line structures and move the - // character pointer. - - var match = this.scanComments() || - this.scanStringLiteral(checks); - - if (match) { - return match; - } - - // Methods that don't move the character pointer. - - match = - this.scanRegExp() || - this.scanPunctuator() || - this.scanKeyword() || - this.scanIdentifier() || - this.scanNumericLiteral(); - - if (match) { - this.skip(match.value.length); - return match; - } - - // No token could be matched, give up. - - return null; - }, - - /* - * Switch to the next line and reset all char pointers. Once - * switched, this method also checks for mixed spaces and tabs - * and other minor warnings. - */ - nextLine: function () { - var char; - - if (this.line >= this.getLines().length) { - return false; - } - - this.input = this.getLines()[this.line]; - this.line += 1; - this.char = 1; - this.from = 1; - - var startsWith = function (prefix) { - return this.indexOf(prefix) === 0; - }; - var endsWith = function (suffix) { - return this.indexOf(suffix, this.length - suffix.length) !== -1; - }; - var inputTrimmed = this.input.trim(); - - // If we are ignoring linter errors, replace the input with empty string - // if it doesn't already at least start or end a multi-line comment - if (state.ignoreLinterErrors === true) { - if (! (startsWith.call(inputTrimmed, "/*") || endsWith.call(inputTrimmed, "*/"))) { - this.input = ""; - } - } - - char = this.scanMixedSpacesAndTabs(); - if (char >= 0) { - this.trigger("warning", { code: "W099", line: this.line, character: char + 1 }); - } - - this.input = this.input.replace(/\t/g, state.tab); - char = this.scanUnsafeChars(); - - if (char >= 0) { - this.trigger("warning", { code: "W100", line: this.line, character: char }); - } - - // If there is a limit on line length, warn when lines get too - // long. - - if (state.option.maxlen && state.option.maxlen < this.input.length) { - var inComment = state.tokens.curr.comment || - startsWith.call(inputTrimmed, "//") || - startsWith.call(inputTrimmed, "/*"); - - var shouldTriggerError = !inComment || !reg.maxlenException.test(inputTrimmed); - - if (shouldTriggerError) { - this.trigger("warning", { code: "W101", line: this.line, character: this.input.length }); - } - } - - return true; - }, - - /* - * This is simply a synonym for nextLine() method with a friendlier - * public name. - */ - start: function () { - this.nextLine(); - }, - - /* - * Produce the next token. This function is called by advance() to get - * the next token. It retuns a token in a JSLint-compatible format. - */ - token: function () { - /*jshint loopfunc:true */ - var checks = asyncTrigger(); - var token; - - - function isReserved(token, isProperty) { - if (!token.reserved) { - return false; - } - var meta = token.meta; - - if (meta && meta.isFutureReservedWord && state.option.inES5()) { - // ES3 FutureReservedWord in an ES5 environment. - if (!meta.es5) { - return false; - } - - // Some ES5 FutureReservedWord identifiers are active only - // within a strict mode environment. - if (meta.strictOnly) { - if (!state.option.strict && !state.directive["use strict"]) { - return false; - } - } - - if (isProperty) { - return false; - } - } - - return true; - } - - // Produce a token object. - var create = function (type, value, isProperty) { - /*jshint validthis:true */ - var obj; - - if (type !== "(endline)" && type !== "(end)") { - this.prereg = false; - } - - if (type === "(punctuator)") { - switch (value) { - case ".": - case ")": - case "~": - case "#": - case "]": - this.prereg = false; - break; - default: - this.prereg = true; - } - - obj = Object.create(state.syntax[value] || state.syntax["(error)"]); - } - - if (type === "(identifier)") { - if (value === "return" || value === "case" || value === "typeof") { - this.prereg = true; - } - - if (_.has(state.syntax, value)) { - obj = Object.create(state.syntax[value] || state.syntax["(error)"]); - - // If this can't be a reserved keyword, reset the object. - if (!isReserved(obj, isProperty && type === "(identifier)")) { - obj = null; - } - } - } - - if (!obj) { - obj = Object.create(state.syntax[type]); - } - - obj.identifier = (type === "(identifier)"); - obj.type = obj.type || type; - obj.value = value; - obj.line = this.line; - obj.character = this.char; - obj.from = this.from; - - if (isProperty && obj.identifier) { - obj.isProperty = isProperty; - } - - obj.check = checks.check; - - return obj; - }.bind(this); - - for (;;) { - if (!this.input.length) { - return create(this.nextLine() ? "(endline)" : "(end)", ""); - } - - token = this.next(checks); - - if (!token) { - if (this.input.length) { - // Unexpected character. - this.trigger("error", { - code: "E024", - line: this.line, - character: this.char, - data: [ this.peek() ] - }); - - this.input = ""; - } - - continue; - } - - switch (token.type) { - case Token.StringLiteral: - this.triggerAsync("String", { - line: this.line, - char: this.char, - from: this.from, - value: token.value, - quote: token.quote - }, checks, function () { return true; }); - - return create("(string)", token.value); - case Token.Identifier: - this.trigger("Identifier", { - line: this.line, - char: this.char, - from: this.form, - name: token.value, - isProperty: state.tokens.curr.id === "." - }); - - /* falls through */ - case Token.Keyword: - case Token.NullLiteral: - case Token.BooleanLiteral: - return create("(identifier)", token.value, state.tokens.curr.id === "."); - - case Token.NumericLiteral: - if (token.isMalformed) { - this.trigger("warning", { - code: "W045", - line: this.line, - character: this.char, - data: [ token.value ] - }); - } - - this.triggerAsync("warning", { - code: "W114", - line: this.line, - character: this.char, - data: [ "0x-" ] - }, checks, function () { return token.base === 16 && state.jsonMode; }); - - this.triggerAsync("warning", { - code: "W115", - line: this.line, - character: this.char - }, checks, function () { - return state.directive["use strict"] && token.base === 8; - }); - - this.trigger("Number", { - line: this.line, - char: this.char, - from: this.from, - value: token.value, - base: token.base, - isMalformed: token.malformed - }); - - return create("(number)", token.value); - - case Token.RegExp: - return create("(regexp)", token.value); - - case Token.Comment: - state.tokens.curr.comment = true; - - if (token.isSpecial) { - return { - id: '(comment)', - value: token.value, - body: token.body, - type: token.commentType, - isSpecial: token.isSpecial, - line: this.line, - character: this.char, - from: this.from - }; - } - - break; - - case "": - break; - - default: - return create("(punctuator)", token.value); - } - } - } -}; - -exports.Lexer = Lexer; - -},{"../data/ascii-identifier-data.js":1,"../data/non-ascii-identifier-part-only.js":2,"../data/non-ascii-identifier-start.js":3,"./reg.js":16,"./state.js":17,"events":5,"underscore":11}],15:[function(require,module,exports){ -"use strict"; - -var _ = require("underscore"); - -var errors = { - // JSHint options - E001: "Bad option: '{a}'.", - E002: "Bad option value.", - - // JSHint input - E003: "Expected a JSON value.", - E004: "Input is neither a string nor an array of strings.", - E005: "Input is empty.", - E006: "Unexpected early end of program.", - - // Strict mode - E007: "Missing \"use strict\" statement.", - E008: "Strict violation.", - E009: "Option 'validthis' can't be used in a global scope.", - E010: "'with' is not allowed in strict mode.", - - // Constants - E011: "const '{a}' has already been declared.", - E012: "const '{a}' is initialized to 'undefined'.", - E013: "Attempting to override '{a}' which is a constant.", - - // Regular expressions - E014: "A regular expression literal can be confused with '/='.", - E015: "Unclosed regular expression.", - E016: "Invalid regular expression.", - - // Tokens - E017: "Unclosed comment.", - E018: "Unbegun comment.", - E019: "Unmatched '{a}'.", - E020: "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'.", - E021: "Expected '{a}' and instead saw '{b}'.", - E022: "Line breaking error '{a}'.", - E023: "Missing '{a}'.", - E024: "Unexpected '{a}'.", - E025: "Missing ':' on a case clause.", - E026: "Missing '}' to match '{' from line {a}.", - E027: "Missing ']' to match '[' from line {a}.", - E028: "Illegal comma.", - E029: "Unclosed string.", - - // Everything else - E030: "Expected an identifier and instead saw '{a}'.", - E031: "Bad assignment.", // FIXME: Rephrase - E032: "Expected a small integer or 'false' and instead saw '{a}'.", - E033: "Expected an operator and instead saw '{a}'.", - E034: "get/set are ES5 features.", - E035: "Missing property name.", - E036: "Expected to see a statement and instead saw a block.", - E037: null, - E038: null, - E039: "Function declarations are not invocable. Wrap the whole function invocation in parens.", - E040: "Each value should have its own case label.", - E041: "Unrecoverable syntax error.", - E042: "Stopping.", - E043: "Too many errors.", - E044: null, - E045: "Invalid for each loop.", - E046: "A yield statement shall be within a generator function (with syntax: `function*`)", - E047: "A generator function shall contain a yield statement.", - E048: "Let declaration not directly within block.", - E049: "A {a} cannot be named '{b}'.", - E050: "Mozilla requires the yield expression to be parenthesized here.", - E051: "Regular parameters cannot come after default parameters." -}; - -var warnings = { - W001: "'hasOwnProperty' is a really bad name.", - W002: "Value of '{a}' may be overwritten in IE 8 and earlier.", - W003: "'{a}' was used before it was defined.", - W004: "'{a}' is already defined.", - W005: "A dot following a number can be confused with a decimal point.", - W006: "Confusing minuses.", - W007: "Confusing pluses.", - W008: "A leading decimal point can be confused with a dot: '{a}'.", - W009: "The array literal notation [] is preferrable.", - W010: "The object literal notation {} is preferrable.", - W011: "Unexpected space after '{a}'.", - W012: "Unexpected space before '{a}'.", - W013: "Missing space after '{a}'.", - W014: "Bad line breaking before '{a}'.", - W015: "Expected '{a}' to have an indentation at {b} instead at {c}.", - W016: "Unexpected use of '{a}'.", - W017: "Bad operand.", - W018: "Confusing use of '{a}'.", - W019: "Use the isNaN function to compare with NaN.", - W020: "Read only.", - W021: "'{a}' is a function.", - W022: "Do not assign to the exception parameter.", - W023: "Expected an identifier in an assignment and instead saw a function invocation.", - W024: "Expected an identifier and instead saw '{a}' (a reserved word).", - W025: "Missing name in function declaration.", - W026: "Inner functions should be listed at the top of the outer function.", - W027: "Unreachable '{a}' after '{b}'.", - W028: "Label '{a}' on {b} statement.", - W030: "Expected an assignment or function call and instead saw an expression.", - W031: "Do not use 'new' for side effects.", - W032: "Unnecessary semicolon.", - W033: "Missing semicolon.", - W034: "Unnecessary directive \"{a}\".", - W035: "Empty block.", - W036: "Unexpected /*member '{a}'.", - W037: "'{a}' is a statement label.", - W038: "'{a}' used out of scope.", - W039: "'{a}' is not allowed.", - W040: "Possible strict violation.", - W041: "Use '{a}' to compare with '{b}'.", - W042: "Avoid EOL escaping.", - W043: "Bad escaping of EOL. Use option multistr if needed.", - W044: "Bad or unnecessary escaping.", - W045: "Bad number '{a}'.", - W046: "Don't use extra leading zeros '{a}'.", - W047: "A trailing decimal point can be confused with a dot: '{a}'.", - W048: "Unexpected control character in regular expression.", - W049: "Unexpected escaped character '{a}' in regular expression.", - W050: "JavaScript URL.", - W051: "Variables should not be deleted.", - W052: "Unexpected '{a}'.", - W053: "Do not use {a} as a constructor.", - W054: "The Function constructor is a form of eval.", - W055: "A constructor name should start with an uppercase letter.", - W056: "Bad constructor.", - W057: "Weird construction. Is 'new' necessary?", - W058: "Missing '()' invoking a constructor.", - W059: "Avoid arguments.{a}.", - W060: "document.write can be a form of eval.", - W061: "eval can be harmful.", - W062: "Wrap an immediate function invocation in parens " + - "to assist the reader in understanding that the expression " + - "is the result of a function, and not the function itself.", - W063: "Math is not a function.", - W064: "Missing 'new' prefix when invoking a constructor.", - W065: "Missing radix parameter.", - W066: "Implied eval. Consider passing a function instead of a string.", - W067: "Bad invocation.", - W068: "Wrapping non-IIFE function literals in parens is unnecessary.", - W069: "['{a}'] is better written in dot notation.", - W070: "Extra comma. (it breaks older versions of IE)", - W071: "This function has too many statements. ({a})", - W072: "This function has too many parameters. ({a})", - W073: "Blocks are nested too deeply. ({a})", - W074: "This function's cyclomatic complexity is too high. ({a})", - W075: "Duplicate key '{a}'.", - W076: "Unexpected parameter '{a}' in get {b} function.", - W077: "Expected a single parameter in set {a} function.", - W078: "Setter is defined without getter.", - W079: "Redefinition of '{a}'.", - W080: "It's not necessary to initialize '{a}' to 'undefined'.", - W081: "Too many var statements.", - W082: "Function declarations should not be placed in blocks. " + - "Use a function expression or move the statement to the top of " + - "the outer function.", - W083: "Don't make functions within a loop.", - W084: "Expected a conditional expression and instead saw an assignment.", - W085: "Don't use 'with'.", - W086: "Expected a 'break' statement before '{a}'.", - W087: "Forgotten 'debugger' statement?", - W088: "Creating global 'for' variable. Should be 'for (var {a} ...'.", - W089: "The body of a for in should be wrapped in an if statement to filter " + - "unwanted properties from the prototype.", - W090: "'{a}' is not a statement label.", - W091: "'{a}' is out of scope.", - W092: "Wrap the /regexp/ literal in parens to disambiguate the slash operator.", - W093: "Did you mean to return a conditional instead of an assignment?", - W094: "Unexpected comma.", - W095: "Expected a string and instead saw {a}.", - W096: "The '{a}' key may produce unexpected results.", - W097: "Use the function form of \"use strict\".", - W098: "'{a}' is defined but never used.", - W099: "Mixed spaces and tabs.", - W100: "This character may get silently deleted by one or more browsers.", - W101: "Line is too long.", - W102: "Trailing whitespace.", - W103: "The '{a}' property is deprecated.", - W104: "'{a}' is only available in JavaScript 1.7.", - W105: "Unexpected {a} in '{b}'.", - W106: "Identifier '{a}' is not in camel case.", - W107: "Script URL.", - W108: "Strings must use doublequote.", - W109: "Strings must use singlequote.", - W110: "Mixed double and single quotes.", - W112: "Unclosed string.", - W113: "Control character in string: {a}.", - W114: "Avoid {a}.", - W115: "Octal literals are not allowed in strict mode.", - W116: "Expected '{a}' and instead saw '{b}'.", - W117: "'{a}' is not defined.", - W118: "'{a}' is only available in Mozilla JavaScript extensions (use moz option).", - W119: "'{a}' is only available in ES6 (use esnext option).", - W120: "You might be leaking a variable ({a}) here.", - W121: "Extending prototype of native object: '{a}'.", - W122: "Invalid typeof value '{a}'", - W123: "'{a}' is already defined in outer scope." -}; - -var info = { - I001: "Comma warnings can be turned off with 'laxcomma'.", - I002: "Reserved words as properties can be used under the 'es5' option.", - I003: "ES5 option is now set per default" -}; - -exports.errors = {}; -exports.warnings = {}; -exports.info = {}; - -_.each(errors, function (desc, code) { - exports.errors[code] = { code: code, desc: desc }; -}); - -_.each(warnings, function (desc, code) { - exports.warnings[code] = { code: code, desc: desc }; -}); - -_.each(info, function (desc, code) { - exports.info[code] = { code: code, desc: desc }; -}); - -},{"underscore":11}],16:[function(require,module,exports){ -/* - * Regular expressions. Some of these are stupidly long. - */ - -/*jshint maxlen:1000 */ - -"use string"; - -// Unsafe comment or string (ax) -exports.unsafeString = - /@cc|<\/?|script|\]\s*\]|<\s*!|</i; - -// Unsafe characters that are silently deleted by one or more browsers (cx) -exports.unsafeChars = - /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; - -// Characters in strings that need escaping (nx and nxg) -exports.needEsc = - /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; - -exports.needEscGlobal = - /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; - -// Star slash (lx) -exports.starSlash = /\*\//; - -// Identifier (ix) -exports.identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; - -// JavaScript URL (jx) -exports.javascriptURL = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i; - -// Catches /* falls through */ comments (ft) -exports.fallsThrough = /^\s*\/\*\s*falls?\sthrough\s*\*\/\s*$/; - -// very conservative rule (eg: only one space between the start of the comment and the first character) -// to relax the maxlen option -exports.maxlenException = /^(?:(?:\/\/|\/\*|\*) ?)?[^ ]+$/; - -},{}],17:[function(require,module,exports){ -"use strict"; - -var state = { - syntax: {}, - - reset: function () { - this.tokens = { - prev: null, - next: null, - curr: null - }; - - this.option = {}; - this.ignored = {}; - this.directive = {}; - this.jsonMode = false; - this.jsonWarnings = []; - this.lines = []; - this.tab = ""; - this.cache = {}; // Node.JS doesn't have Map. Sniff. - this.ignoreLinterErrors = false; // Blank out non-multi-line-commented - // lines when ignoring linter errors - } -}; - -exports.state = state; - -},{}],18:[function(require,module,exports){ -"use strict"; - -exports.register = function (linter) { - // Check for properties named __proto__. This special property was - // deprecated and then re-introduced for ES6. - - linter.on("Identifier", function style_scanProto(data) { - if (linter.getOption("proto")) { - return; - } - - if (data.name === "__proto__") { - linter.warn("W103", { - line: data.line, - char: data.char, - data: [ data.name ] - }); - } - }); - - // Check for properties named __iterator__. This is a special property - // available only in browsers with JavaScript 1.7 implementation. - - linter.on("Identifier", function style_scanIterator(data) { - if (linter.getOption("iterator")) { - return; - } - - if (data.name === "__iterator__") { - linter.warn("W104", { - line: data.line, - char: data.char, - data: [ data.name ] - }); - } - }); - - // Check for dangling underscores. - - linter.on("Identifier", function style_scanDangling(data) { - if (!linter.getOption("nomen")) { - return; - } - - // Underscore.js - if (data.name === "_") { - return; - } - - // In Node, __dirname and __filename should be ignored. - if (linter.getOption("node")) { - if (/^(__dirname|__filename)$/.test(data.name) && !data.isProperty) { - return; - } - } - - if (/^(_+.*|.*_+)$/.test(data.name)) { - linter.warn("W105", { - line: data.line, - char: data.from, - data: [ "dangling '_'", data.name ] - }); - } - }); - - // Check that all identifiers are using camelCase notation. - // Exceptions: names like MY_VAR and _myVar. - - linter.on("Identifier", function style_scanCamelCase(data) { - if (!linter.getOption("camelcase")) { - return; - } - - if (data.name.replace(/^_+|_+$/g, "").indexOf("_") > -1 && !data.name.match(/^[A-Z0-9_]*$/)) { - linter.warn("W106", { - line: data.line, - char: data.from, - data: [ data.name ] - }); - } - }); - - // Enforce consistency in style of quoting. - - linter.on("String", function style_scanQuotes(data) { - var quotmark = linter.getOption("quotmark"); - var code; - - if (!quotmark) { - return; - } - - // If quotmark is set to 'single' warn about all double-quotes. - - if (quotmark === "single" && data.quote !== "'") { - code = "W109"; - } - - // If quotmark is set to 'double' warn about all single-quotes. - - if (quotmark === "double" && data.quote !== "\"") { - code = "W108"; - } - - // If quotmark is set to true, remember the first quotation style - // and then warn about all others. - - if (quotmark === true) { - if (!linter.getCache("quotmark")) { - linter.setCache("quotmark", data.quote); - } - - if (linter.getCache("quotmark") !== data.quote) { - code = "W110"; - } - } - - if (code) { - linter.warn(code, { - line: data.line, - char: data.char, - }); - } - }); - - linter.on("Number", function style_scanNumbers(data) { - if (data.value.charAt(0) === ".") { - // Warn about a leading decimal point. - linter.warn("W008", { - line: data.line, - char: data.char, - data: [ data.value ] - }); - } - - if (data.value.substr(data.value.length - 1) === ".") { - // Warn about a trailing decimal point. - linter.warn("W047", { - line: data.line, - char: data.char, - data: [ data.value ] - }); - } - - if (/^00+/.test(data.value)) { - // Multiple leading zeroes. - linter.warn("W046", { - line: data.line, - char: data.char, - data: [ data.value ] - }); - } - }); - - // Warn about script URLs. - - linter.on("String", function style_scanJavaScriptURLs(data) { - var re = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i; - - if (linter.getOption("scripturl")) { - return; - } - - if (re.test(data.value)) { - linter.warn("W107", { - line: data.line, - char: data.char - }); - } - }); -}; -},{}],19:[function(require,module,exports){ -// jshint -W001 - -"use strict"; - -// Identifiers provided by the ECMAScript standard. - -exports.reservedVars = { - arguments : false, - NaN : false -}; - -exports.ecmaIdentifiers = { - Array : false, - Boolean : false, - Date : false, - decodeURI : false, - decodeURIComponent : false, - encodeURI : false, - encodeURIComponent : false, - Error : false, - "eval" : false, - EvalError : false, - Function : false, - hasOwnProperty : false, - isFinite : false, - isNaN : false, - JSON : false, - Math : false, - Map : false, - Number : false, - Object : false, - parseInt : false, - parseFloat : false, - RangeError : false, - ReferenceError : false, - RegExp : false, - Set : false, - String : false, - SyntaxError : false, - TypeError : false, - URIError : false, - WeakMap : false -}; - -// Global variables commonly provided by a web browser environment. - -exports.browser = { - Audio : false, - Blob : false, - addEventListener : false, - applicationCache : false, - atob : false, - blur : false, - btoa : false, - CanvasGradient : false, - CanvasPattern : false, - CanvasRenderingContext2D: false, - clearInterval : false, - clearTimeout : false, - close : false, - closed : false, - CustomEvent : false, - DOMParser : false, - defaultStatus : false, - document : false, - Element : false, - ElementTimeControl : false, - event : false, - FileReader : false, - FormData : false, - focus : false, - frames : false, - getComputedStyle : false, - HTMLElement : false, - HTMLAnchorElement : false, - HTMLBaseElement : false, - HTMLBlockquoteElement: false, - HTMLBodyElement : false, - HTMLBRElement : false, - HTMLButtonElement : false, - HTMLCanvasElement : false, - HTMLDirectoryElement : false, - HTMLDivElement : false, - HTMLDListElement : false, - HTMLFieldSetElement : false, - HTMLFontElement : false, - HTMLFormElement : false, - HTMLFrameElement : false, - HTMLFrameSetElement : false, - HTMLHeadElement : false, - HTMLHeadingElement : false, - HTMLHRElement : false, - HTMLHtmlElement : false, - HTMLIFrameElement : false, - HTMLImageElement : false, - HTMLInputElement : false, - HTMLIsIndexElement : false, - HTMLLabelElement : false, - HTMLLayerElement : false, - HTMLLegendElement : false, - HTMLLIElement : false, - HTMLLinkElement : false, - HTMLMapElement : false, - HTMLMenuElement : false, - HTMLMetaElement : false, - HTMLModElement : false, - HTMLObjectElement : false, - HTMLOListElement : false, - HTMLOptGroupElement : false, - HTMLOptionElement : false, - HTMLParagraphElement : false, - HTMLParamElement : false, - HTMLPreElement : false, - HTMLQuoteElement : false, - HTMLScriptElement : false, - HTMLSelectElement : false, - HTMLStyleElement : false, - HTMLTableCaptionElement: false, - HTMLTableCellElement : false, - HTMLTableColElement : false, - HTMLTableElement : false, - HTMLTableRowElement : false, - HTMLTableSectionElement: false, - HTMLTextAreaElement : false, - HTMLTitleElement : false, - HTMLUListElement : false, - HTMLVideoElement : false, - history : false, - Image : false, - length : false, - localStorage : false, - location : false, - MessageChannel : false, - MessageEvent : false, - MessagePort : false, - MouseEvent : false, - moveBy : false, - moveTo : false, - MutationObserver : false, - name : false, - Node : false, - NodeFilter : false, - navigator : false, - onbeforeunload : true, - onblur : true, - onerror : true, - onfocus : true, - onload : true, - onresize : true, - onunload : true, - open : false, - openDatabase : false, - opener : false, - Option : false, - parent : false, - print : false, - removeEventListener : false, - resizeBy : false, - resizeTo : false, - screen : false, - scroll : false, - scrollBy : false, - scrollTo : false, - sessionStorage : false, - setInterval : false, - setTimeout : false, - SharedWorker : false, - status : false, - SVGAElement : false, - SVGAltGlyphDefElement: false, - SVGAltGlyphElement : false, - SVGAltGlyphItemElement: false, - SVGAngle : false, - SVGAnimateColorElement: false, - SVGAnimateElement : false, - SVGAnimateMotionElement: false, - SVGAnimateTransformElement: false, - SVGAnimatedAngle : false, - SVGAnimatedBoolean : false, - SVGAnimatedEnumeration: false, - SVGAnimatedInteger : false, - SVGAnimatedLength : false, - SVGAnimatedLengthList: false, - SVGAnimatedNumber : false, - SVGAnimatedNumberList: false, - SVGAnimatedPathData : false, - SVGAnimatedPoints : false, - SVGAnimatedPreserveAspectRatio: false, - SVGAnimatedRect : false, - SVGAnimatedString : false, - SVGAnimatedTransformList: false, - SVGAnimationElement : false, - SVGCSSRule : false, - SVGCircleElement : false, - SVGClipPathElement : false, - SVGColor : false, - SVGColorProfileElement: false, - SVGColorProfileRule : false, - SVGComponentTransferFunctionElement: false, - SVGCursorElement : false, - SVGDefsElement : false, - SVGDescElement : false, - SVGDocument : false, - SVGElement : false, - SVGElementInstance : false, - SVGElementInstanceList: false, - SVGEllipseElement : false, - SVGExternalResourcesRequired: false, - SVGFEBlendElement : false, - SVGFEColorMatrixElement: false, - SVGFEComponentTransferElement: false, - SVGFECompositeElement: false, - SVGFEConvolveMatrixElement: false, - SVGFEDiffuseLightingElement: false, - SVGFEDisplacementMapElement: false, - SVGFEDistantLightElement: false, - SVGFEFloodElement : false, - SVGFEFuncAElement : false, - SVGFEFuncBElement : false, - SVGFEFuncGElement : false, - SVGFEFuncRElement : false, - SVGFEGaussianBlurElement: false, - SVGFEImageElement : false, - SVGFEMergeElement : false, - SVGFEMergeNodeElement: false, - SVGFEMorphologyElement: false, - SVGFEOffsetElement : false, - SVGFEPointLightElement: false, - SVGFESpecularLightingElement: false, - SVGFESpotLightElement: false, - SVGFETileElement : false, - SVGFETurbulenceElement: false, - SVGFilterElement : false, - SVGFilterPrimitiveStandardAttributes: false, - SVGFitToViewBox : false, - SVGFontElement : false, - SVGFontFaceElement : false, - SVGFontFaceFormatElement: false, - SVGFontFaceNameElement: false, - SVGFontFaceSrcElement: false, - SVGFontFaceUriElement: false, - SVGForeignObjectElement: false, - SVGGElement : false, - SVGGlyphElement : false, - SVGGlyphRefElement : false, - SVGGradientElement : false, - SVGHKernElement : false, - SVGICCColor : false, - SVGImageElement : false, - SVGLangSpace : false, - SVGLength : false, - SVGLengthList : false, - SVGLineElement : false, - SVGLinearGradientElement: false, - SVGLocatable : false, - SVGMPathElement : false, - SVGMarkerElement : false, - SVGMaskElement : false, - SVGMatrix : false, - SVGMetadataElement : false, - SVGMissingGlyphElement: false, - SVGNumber : false, - SVGNumberList : false, - SVGPaint : false, - SVGPathElement : false, - SVGPathSeg : false, - SVGPathSegArcAbs : false, - SVGPathSegArcRel : false, - SVGPathSegClosePath : false, - SVGPathSegCurvetoCubicAbs: false, - SVGPathSegCurvetoCubicRel: false, - SVGPathSegCurvetoCubicSmoothAbs: false, - SVGPathSegCurvetoCubicSmoothRel: false, - SVGPathSegCurvetoQuadraticAbs: false, - SVGPathSegCurvetoQuadraticRel: false, - SVGPathSegCurvetoQuadraticSmoothAbs: false, - SVGPathSegCurvetoQuadraticSmoothRel: false, - SVGPathSegLinetoAbs : false, - SVGPathSegLinetoHorizontalAbs: false, - SVGPathSegLinetoHorizontalRel: false, - SVGPathSegLinetoRel : false, - SVGPathSegLinetoVerticalAbs: false, - SVGPathSegLinetoVerticalRel: false, - SVGPathSegList : false, - SVGPathSegMovetoAbs : false, - SVGPathSegMovetoRel : false, - SVGPatternElement : false, - SVGPoint : false, - SVGPointList : false, - SVGPolygonElement : false, - SVGPolylineElement : false, - SVGPreserveAspectRatio: false, - SVGRadialGradientElement: false, - SVGRect : false, - SVGRectElement : false, - SVGRenderingIntent : false, - SVGSVGElement : false, - SVGScriptElement : false, - SVGSetElement : false, - SVGStopElement : false, - SVGStringList : false, - SVGStylable : false, - SVGStyleElement : false, - SVGSwitchElement : false, - SVGSymbolElement : false, - SVGTRefElement : false, - SVGTSpanElement : false, - SVGTests : false, - SVGTextContentElement: false, - SVGTextElement : false, - SVGTextPathElement : false, - SVGTextPositioningElement: false, - SVGTitleElement : false, - SVGTransform : false, - SVGTransformList : false, - SVGTransformable : false, - SVGURIReference : false, - SVGUnitTypes : false, - SVGUseElement : false, - SVGVKernElement : false, - SVGViewElement : false, - SVGViewSpec : false, - SVGZoomAndPan : false, - TimeEvent : false, - top : false, - URL : false, - WebSocket : false, - window : false, - Worker : false, - XMLHttpRequest : false, - XMLSerializer : false, - XPathEvaluator : false, - XPathException : false, - XPathExpression : false, - XPathNamespace : false, - XPathNSResolver : false, - XPathResult : false -}; - -exports.devel = { - alert : false, - confirm: false, - console: false, - Debug : false, - opera : false, - prompt : false -}; - -exports.worker = { - importScripts: true, - postMessage : true, - self : true -}; - -// Widely adopted global names that are not part of ECMAScript standard -exports.nonstandard = { - escape : false, - unescape: false -}; - -// Globals provided by popular JavaScript environments. - -exports.couch = { - "require" : false, - respond : false, - getRow : false, - emit : false, - send : false, - start : false, - sum : false, - log : false, - exports : false, - module : false, - provides : false -}; - -exports.node = { - __filename : false, - __dirname : false, - GLOBAL : false, - global : false, - module : false, - require : false, - - // These globals are writeable because Node allows the following - // usage pattern: var Buffer = require("buffer").Buffer; - - Buffer : true, - console : true, - exports : true, - process : true, - setTimeout : true, - clearTimeout : true, - setInterval : true, - clearInterval : true, - setImmediate : true, // v0.9.1+ - clearImmediate: true // v0.9.1+ -}; - -exports.phantom = { - phantom : true, - require : true, - WebPage : true, - console : true, // in examples, but undocumented - exports : true // v1.7+ -}; - -exports.rhino = { - defineClass : false, - deserialize : false, - gc : false, - help : false, - importPackage: false, - "java" : false, - load : false, - loadClass : false, - print : false, - quit : false, - readFile : false, - readUrl : false, - runCommand : false, - seal : false, - serialize : false, - spawn : false, - sync : false, - toint32 : false, - version : false -}; - -exports.shelljs = { - target : false, - echo : false, - exit : false, - cd : false, - pwd : false, - ls : false, - find : false, - cp : false, - rm : false, - mv : false, - mkdir : false, - test : false, - cat : false, - sed : false, - grep : false, - which : false, - dirs : false, - pushd : false, - popd : false, - env : false, - exec : false, - chmod : false, - config : false, - error : false, - tempdir : false -}; - -exports.typed = { - ArrayBuffer : false, - ArrayBufferView : false, - DataView : false, - Float32Array : false, - Float64Array : false, - Int16Array : false, - Int32Array : false, - Int8Array : false, - Uint16Array : false, - Uint32Array : false, - Uint8Array : false, - Uint8ClampedArray : false -}; - -exports.wsh = { - ActiveXObject : true, - Enumerator : true, - GetObject : true, - ScriptEngine : true, - ScriptEngineBuildVersion : true, - ScriptEngineMajorVersion : true, - ScriptEngineMinorVersion : true, - VBArray : true, - WSH : true, - WScript : true, - XDomainRequest : true -}; - -// Globals provided by popular JavaScript libraries. - -exports.dojo = { - dojo : false, - dijit : false, - dojox : false, - define : false, - "require": false -}; - -exports.jquery = { - "$" : false, - jQuery : false -}; - -exports.mootools = { - "$" : false, - "$$" : false, - Asset : false, - Browser : false, - Chain : false, - Class : false, - Color : false, - Cookie : false, - Core : false, - Document : false, - DomReady : false, - DOMEvent : false, - DOMReady : false, - Drag : false, - Element : false, - Elements : false, - Event : false, - Events : false, - Fx : false, - Group : false, - Hash : false, - HtmlTable : false, - Iframe : false, - IframeShim : false, - InputValidator: false, - instanceOf : false, - Keyboard : false, - Locale : false, - Mask : false, - MooTools : false, - Native : false, - Options : false, - OverText : false, - Request : false, - Scroller : false, - Slick : false, - Slider : false, - Sortables : false, - Spinner : false, - Swiff : false, - Tips : false, - Type : false, - typeOf : false, - URI : false, - Window : false -}; - -exports.prototypejs = { - "$" : false, - "$$" : false, - "$A" : false, - "$F" : false, - "$H" : false, - "$R" : false, - "$break" : false, - "$continue" : false, - "$w" : false, - Abstract : false, - Ajax : false, - Class : false, - Enumerable : false, - Element : false, - Event : false, - Field : false, - Form : false, - Hash : false, - Insertion : false, - ObjectRange : false, - PeriodicalExecuter: false, - Position : false, - Prototype : false, - Selector : false, - Template : false, - Toggle : false, - Try : false, - Autocompleter : false, - Builder : false, - Control : false, - Draggable : false, - Draggables : false, - Droppables : false, - Effect : false, - Sortable : false, - SortableObserver : false, - Sound : false, - Scriptaculous : false -}; - -exports.yui = { - YUI : false, - Y : false, - YUI_config: false -}; - - -},{}]},{},["FcDwKk"]) JSHINT = require('jshint').JSHINT; if (typeof exports === 'object' && exports) exports.JSHINT = JSHINT; }()); \ No newline at end of file diff --git a/vendor/assets/javascripts/list-view.js b/vendor/assets/javascripts/list-view.js new file mode 100644 index 0000000000..c841d108db --- /dev/null +++ b/vendor/assets/javascripts/list-view.js @@ -0,0 +1,1851 @@ +// ========================================================================== +// Project: Ember ListView +// Copyright: ©2012-2013 Erik Bryn, Yapp Inc., and contributors. +// License: Licensed under MIT license +// Version: 0.0.5 +// ========================================================================== + +(function(global){ +var define, requireModule, require, requirejs; + +(function() { + + var _isArray; + if (!Array.isArray) { + _isArray = function (x) { + return Object.prototype.toString.call(x) === "[object Array]"; + }; + } else { + _isArray = Array.isArray; + } + + var registry = {}, seen = {}, state = {}; + var FAILED = false; + + define = function(name, deps, callback) { + + if (!_isArray(deps)) { + callback = deps; + deps = []; + } + + registry[name] = { + deps: deps, + callback: callback + }; + }; + + function reify(deps, name, seen) { + var length = deps.length; + var reified = new Array(length); + var dep; + var exports; + + for (var i = 0, l = length; i < l; i++) { + dep = deps[i]; + if (dep === 'exports') { + exports = reified[i] = seen; + } else { + reified[i] = require(resolve(dep, name)); + } + } + + return { + deps: reified, + exports: exports + }; + } + + requirejs = require = requireModule = function(name) { + if (state[name] !== FAILED && + seen.hasOwnProperty(name)) { + return seen[name]; + } + + if (!registry[name]) { + throw new Error('Could not find module ' + name); + } + + var mod = registry[name]; + var reified; + var module; + var loaded = false; + + seen[name] = { }; // placeholder for run-time cycles + + try { + reified = reify(mod.deps, name, seen[name]); + module = mod.callback.apply(this, reified.deps); + loaded = true; + } finally { + if (!loaded) { + state[name] = FAILED; + } + } + + return reified.exports ? seen[name] : (seen[name] = module); + }; + + function resolve(child, name) { + if (child.charAt(0) !== '.') { return child; } + + var parts = child.split('/'); + var nameParts = name.split('/'); + var parentBase; + + if (nameParts.length === 1) { + parentBase = nameParts; + } else { + parentBase = nameParts.slice(0, -1); + } + + for (var i = 0, l = parts.length; i < l; i++) { + var part = parts[i]; + + if (part === '..') { parentBase.pop(); } + else if (part === '.') { continue; } + else { parentBase.push(part); } + } + + return parentBase.join('/'); + } + + requirejs.entries = requirejs._eak_seen = registry; + requirejs.clear = function(){ + requirejs.entries = requirejs._eak_seen = registry = {}; + seen = state = {}; + }; +})(); + +define("list-view/helper", + ["./list_view","./virtual_list_view","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var EmberListView = __dependency1__["default"]; + var EmberVirtualListView = __dependency2__["default"]; + + function createHelper (view, options) { + var hash = options.hash; + var types = options.hashTypes; + + hash.content = hash.items; + delete hash.items; + + types.content = types.items; + delete types.items; + + if (!hash.content) { + hash.content = 'this'; + types.content = 'ID'; + } + + for (var prop in hash) { + if (/-/.test(prop)) { + var camelized = Ember.String.camelize(prop); + hash[camelized] = hash[prop]; + types[camelized] = types[prop]; + delete hash[prop]; + delete types[prop]; + } + } + + /*jshint validthis:true */ + return Ember.Handlebars.helpers.collection.call(this, view, options); + } + + function EmberList (options) { + return createHelper.call(this, EmberListView, options); + } + + __exports__.EmberList = EmberList;__exports__["default"] = EmberList; + + function EmberVirtualList (options) { + return createHelper.call(this, EmberVirtualListView, options); + } + + __exports__.EmberVirtualList = EmberVirtualList; + }); +define("list-view/list_item_view", + ["list-view/list_item_view_mixin","exports"], + function(__dependency1__, __exports__) { + "use strict"; + /*jshint validthis:true */ + + var ListItemViewMixin = __dependency1__["default"]; + + var get = Ember.get, set = Ember.set; + + /** + The `Ember.ListItemView` view class renders a + [div](https://developer.mozilla.org/en/HTML/Element/div) HTML element + with `ember-list-item-view` class. It allows you to specify a custom item + handlebars template for `Ember.ListView`. + + Example: + + ```handlebars + + ``` + + ```javascript + App.ListView = Ember.ListView.extend({ + height: 500, + rowHeight: 20, + itemViewClass: Ember.ListItemView.extend({templateName: "row_item"}) + }); + ``` + + @extends Ember.View + @class ListItemView + @namespace Ember + */ + __exports__["default"] = Ember.View.extend(ListItemViewMixin, { + updateContext: function(newContext) { + var context = get(this, 'context'); + + Ember.instrument('view.updateContext.render', this, function() { + if (context !== newContext) { + set(this, 'context', newContext); + if (newContext && newContext.isController) { + set(this, 'controller', newContext); + } + } + }, this); + }, + + rerender: function () { + if (this.isDestroying || this.isDestroyed) { + return; + } + + return this._super.apply(this, arguments); + }, + + _contextDidChange: Ember.observer(function () { + Ember.run.once(this, this.rerender); + }, 'context', 'controller') + }); + }); +define("list-view/list_item_view_mixin", + ["exports"], + function(__exports__) { + "use strict"; + /*jshint validthis:true */ + + function samePosition(a, b) { + return a && b && a.x === b.x && a.y === b.y; + } + + function positionElement() { + var element, position, _position; + + Ember.instrument('view.updateContext.positionElement', this, function() { + element = this.element; + position = this.position; + _position = this._position; + + if (!position || !element) { + return; + } + + // // TODO: avoid needing this by avoiding unnecessary + // // calls to this method in the first place + if (samePosition(position, _position)) { + return; + } + + Ember.run.schedule('render', this, this._parentView.applyTransform, this, position.x, position.y); + this._position = position; + }, this); + } + + __exports__["default"] = Ember.Mixin.create({ + classNames: ['ember-list-item-view'], + style: '', + attributeBindings: ['style'], + _position: null, + _positionElement: positionElement, + + positionElementWhenInserted: Ember.on('init', function(){ + this.one('didInsertElement', positionElement); + }), + + updatePosition: function(position) { + this.position = position; + this._positionElement(); + } + }); + }); +define("list-view/list_view", + ["list-view/list_view_helper","list-view/list_view_mixin","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var ListViewHelper = __dependency1__["default"]; + var ListViewMixin = __dependency2__["default"]; + + var get = Ember.get; + + /** + The `Ember.ListView` view class renders a + [div](https://developer.mozilla.org/en/HTML/Element/div) HTML element, + with `ember-list-view` class. + + The context of each item element within the `Ember.ListView` are populated + from the objects in the `Element.ListView`'s `content` property. + + ### `content` as an Array of Objects + + The simplest version of an `Ember.ListView` takes an array of object as its + `content` property. The object will be used as the `context` each item element + inside the rendered `div`. + + Example: + + ```javascript + App.ContributorsRoute = Ember.Route.extend({ + model: function() { + return [{ name: 'Stefan Penner' }, { name: 'Alex Navasardyan' }, { name: 'Ray Cohen'}]; + } + }); + ``` + + ```handlebars + {{#ember-list items=contributors height=500 rowHeight=50}} + {{name}} + {{/ember-list}} + ``` + + Would result in the following HTML: + + ```html +
      +
      +
      + Stefan Penner +
      +
      + Alex Navasardyan +
      +
      + Rey Cohen +
      +
      +
      +
      + ``` + + By default `Ember.ListView` provides support for `height`, + `rowHeight`, `width`, `elementWidth`, `scrollTop` parameters. + + Note, that `height` and `rowHeight` are required parameters. + + ```handlebars + {{#ember-list items=this height=500 rowHeight=50}} + {{name}} + {{/ember-list}} + ``` + + If you would like to have multiple columns in your view layout, you can + set `width` and `elementWidth` parameters respectively. + + ```handlebars + {{#ember-list items=this height=500 rowHeight=50 width=500 elementWidth=80}} + {{name}} + {{/ember-list}} + ``` + + ### extending `Ember.ListView` + + Example: + + ```handlebars + {{view App.ListView contentBinding="content"}} + + + ``` + + ```javascript + App.ListView = Ember.ListView.extend({ + height: 500, + width: 500, + elementWidth: 80, + rowHeight: 20, + itemViewClass: Ember.ListItemView.extend({templateName: "row_item"}) + }); + ``` + + @extends Ember.ContainerView + @class ListView + @namespace Ember + */ + __exports__["default"] = Ember.ContainerView.extend(ListViewMixin, { + css: { + position: 'relative', + overflow: 'auto', + '-webkit-overflow-scrolling': 'touch', + 'overflow-scrolling': 'touch' + }, + + applyTransform: ListViewHelper.applyTransform, + + _scrollTo: function(scrollTop) { + var element = this.element; + + if (element) { element.scrollTop = scrollTop; } + }, + + didInsertElement: function() { + var that = this; + + this._updateScrollableHeight(); + + this._scroll = function(e) { that.scroll(e); }; + + Ember.$(this.element).on('scroll', this._scroll); + }, + + willDestroyElement: function() { + Ember.$(this.element).off('scroll', this._scroll); + }, + + scroll: function(e) { + this.scrollTo(e.target.scrollTop); + }, + + scrollTo: function(y) { + this._scrollTo(y); + this._scrollContentTo(y); + }, + + totalHeightDidChange: Ember.observer(function () { + Ember.run.scheduleOnce('afterRender', this, this._updateScrollableHeight); + }, 'totalHeight'), + + _updateScrollableHeight: function () { + var height, state; + + // Support old and new Ember versions + state = this._state || this.state; + + if (state === 'inDOM') { + // if the list is currently displaying the emptyView, remove the height + if (this._isChildEmptyView()) { + height = ''; + } else { + height = get(this, 'totalHeight'); + } + + this.$('.ember-list-container').css({ + height: height + }); + } + } + }); + }); +define("list-view/list_view_helper", + ["exports"], + function(__exports__) { + "use strict"; + // TODO - remove this! + var el = document.body || document.createElement('div'); + var style = el.style; + var set = Ember.set; + + function getElementStyle (prop) { + var uppercaseProp = prop.charAt(0).toUpperCase() + prop.slice(1); + + var props = [ + prop, + 'webkit' + prop, + 'webkit' + uppercaseProp, + 'Moz' + uppercaseProp, + 'moz' + uppercaseProp, + 'ms' + uppercaseProp, + 'ms' + prop + ]; + + for (var i=0; i < props.length; i++) { + var property = props[i]; + + if (property in style) { + return property; + } + } + + return null; + } + + function getCSSStyle (attr) { + var styleName = getElementStyle(attr); + var prefix = styleName.toLowerCase().replace(attr, ''); + + var dic = { + webkit: '-webkit-' + attr, + moz: '-moz-' + attr, + ms: '-ms-' + attr + }; + + if (prefix && dic[prefix]) { + return dic[prefix]; + } + + return styleName; + } + + var styleAttributeName = getElementStyle('transform'); + var transformProp = getCSSStyle('transform'); + var perspectiveProp = getElementStyle('perspective'); + var supports2D = !!transformProp; + var supports3D = !!perspectiveProp; + + function setStyle (optionalStyleString) { + return function (obj, x, y) { + var isElement = obj instanceof Element; + + if (optionalStyleString && (supports2D || supports3D)) { + var style = Ember.String.fmt(optionalStyleString, x, y); + + if (isElement) { + obj.style[styleAttributeName] = style; + } else { + set(obj, 'style', transformProp + ': ' + style); + } + } else { + if (isElement) { + obj.style.top = y; + obj.style.left = x; + } + } + }; + } + + __exports__["default"] = { + transformProp: transformProp, + applyTransform: (function () { + if (supports2D) { + return setStyle('translate(%@px, %@px)'); + } + + return setStyle(); + })(), + apply3DTransform: (function () { + if (supports3D) { + return setStyle('translate3d(%@px, %@px, 0)'); + } else if (supports2D) { + return setStyle('translate(%@px, %@px)'); + } + + return setStyle(); + })() + }; + }); +define("list-view/list_view_mixin", + ["list-view/reusable_list_item_view","exports"], + function(__dependency1__, __exports__) { + "use strict"; + /*jshint validthis:true */ + + var ReusableListItemView = __dependency1__["default"]; + + var get = Ember.get; + var set = Ember.set; + var min = Math.min; + var max = Math.max; + var floor = Math.floor; + var ceil = Math.ceil; + var forEach = Ember.ArrayPolyfills.forEach; + + function addContentArrayObserver() { + var content = get(this, 'content'); + if (content) { + content.addArrayObserver(this); + } + } + + function removeAndDestroy(object) { + this.removeObject(object); + object.destroy(); + } + + function syncChildViews() { + Ember.run.once(this, '_syncChildViews'); + } + + function sortByContentIndex (viewOne, viewTwo) { + return get(viewOne, 'contentIndex') - get(viewTwo, 'contentIndex'); + } + + function removeEmptyView() { + var emptyView = get(this, 'emptyView'); + if (emptyView && emptyView instanceof Ember.View) { + emptyView.removeFromParent(); + if (this.totalHeightDidChange !== undefined) { + this.totalHeightDidChange(); + } + } + } + + function addEmptyView() { + var emptyView = get(this, 'emptyView'); + + if (!emptyView) { + return; + } + + if ('string' === typeof emptyView) { + emptyView = get(emptyView) || emptyView; + } + + emptyView = this.createChildView(emptyView); + set(this, 'emptyView', emptyView); + + if (Ember.CoreView.detect(emptyView)) { + this._createdEmptyView = emptyView; + } + + this.unshiftObject(emptyView); + } + + function enableProfilingOutput() { + function before(name, time/*, payload*/) { + console.time(name); + } + + function after (name, time/*, payload*/) { + console.timeEnd(name); + } + + if (Ember.ENABLE_PROFILING) { + Ember.subscribe('view._scrollContentTo', { + before: before, + after: after + }); + Ember.subscribe('view.updateContext', { + before: before, + after: after + }); + } + } + + /** + @class Ember.ListViewMixin + @namespace Ember + */ + __exports__["default"] = Ember.Mixin.create({ + itemViewClass: ReusableListItemView, + emptyViewClass: Ember.View, + classNames: ['ember-list-view'], + attributeBindings: ['style'], + classNameBindings: ['_isGrid:ember-list-view-grid:ember-list-view-list'], + scrollTop: 0, + bottomPadding: 0, // TODO: maybe this can go away + _lastEndingIndex: 0, + paddingCount: 1, + _cachedPos: 0, + + _isGrid: Ember.computed('columnCount', function() { + return this.get('columnCount') > 1; + }).readOnly(), + + /** + @private + + Setup a mixin. + - adding observer to content array + - creating child views based on height and length of the content array + + @method init + */ + init: function() { + this._super(); + this._cachedHeights = [0]; + this.on('didInsertElement', this._syncListContainerWidth); + this.columnCountDidChange(); + this._syncChildViews(); + this._addContentArrayObserver(); + }, + + _addContentArrayObserver: Ember.beforeObserver(function() { + addContentArrayObserver.call(this); + }, 'content'), + + /** + Called on your view when it should push strings of HTML into a + `Ember.RenderBuffer`. + + Adds a [div](https://developer.mozilla.org/en-US/docs/HTML/Element/div) + with a required `ember-list-container` class. + + @method render + @param {Ember.RenderBuffer} buffer The render buffer + */ + render: function (buffer) { + var element = buffer.element(); + var dom = buffer.dom; + var container = dom.createElement('div'); + container.className = 'ember-list-container'; + element.appendChild(container); + + this._childViewsMorph = dom.createMorph(container, container, null); + + return container; + }, + + createChildViewsMorph: function (element) { + this._childViewsMorph = this._renderer._dom.createMorph(element.lastChild, element.lastChild, null); + return element; + }, + + willInsertElement: function() { + if (!this.get('height') || !this.get('rowHeight')) { + throw new Error('A ListView must be created with a height and a rowHeight.'); + } + this._super(); + }, + + /** + @private + + Sets inline styles of the view: + - height + - width + - position + - overflow + - -webkit-overflow + - overflow-scrolling + + Called while attributes binding. + + @property {Ember.ComputedProperty} style + */ + style: Ember.computed('height', 'width', function() { + var height, width, style, css; + + height = get(this, 'height'); + width = get(this, 'width'); + css = get(this, 'css'); + + style = ''; + + if (height) { + style += 'height:' + height + 'px;'; + } + + if (width) { + style += 'width:' + width + 'px;'; + } + + for ( var rule in css ) { + if (css.hasOwnProperty(rule)) { + style += rule + ':' + css[rule] + ';'; + } + } + + return style; + }), + + /** + @private + + Performs visual scrolling. Is overridden in Ember.ListView. + + @method scrollTo + */ + scrollTo: function(y) { + throw new Error('must override to perform the visual scroll and effectively delegate to _scrollContentTo'); + }, + + /** + @private + + Internal method used to force scroll position + + @method scrollTo + */ + _scrollTo: Ember.K, + + /** + @private + @method _scrollContentTo + */ + _scrollContentTo: function(y) { + var startingIndex, endingIndex, + contentIndex, visibleEndingIndex, maxContentIndex, + contentIndexEnd, contentLength, scrollTop, content; + + scrollTop = max(0, y); + + if (this.scrollTop === scrollTop) { + return; + } + + // allow a visual overscroll, but don't scroll the content. As we are doing needless + // recycyling, and adding unexpected nodes to the DOM. + var maxScrollTop = max(0, get(this, 'totalHeight') - get(this, 'height')); + scrollTop = min(scrollTop, maxScrollTop); + + content = get(this, 'content'); + contentLength = get(content, 'length'); + startingIndex = this._startingIndex(contentLength); + + Ember.instrument('view._scrollContentTo', { + scrollTop: scrollTop, + content: content, + startingIndex: startingIndex, + endingIndex: min(max(contentLength - 1, 0), startingIndex + this._numChildViewsForViewport()) + }, function () { + this.scrollTop = scrollTop; + + maxContentIndex = max(contentLength - 1, 0); + + startingIndex = this._startingIndex(); + visibleEndingIndex = startingIndex + this._numChildViewsForViewport(); + + endingIndex = min(maxContentIndex, visibleEndingIndex); + + if (startingIndex === this._lastStartingIndex && + endingIndex === this._lastEndingIndex) { + + this.trigger('scrollYChanged', y); + return; + } else { + + Ember.run(this, function() { + this._reuseChildren(); + + this._lastStartingIndex = startingIndex; + this._lastEndingIndex = endingIndex; + this.trigger('scrollYChanged', y); + }); + } + }, this); + + }, + + /** + @private + + Computes the height for a `Ember.ListView` scrollable container div. + You must specify `rowHeight` parameter for the height to be computed properly. + + @property {Ember.ComputedProperty} totalHeight + */ + totalHeight: Ember.computed('content.length', + 'rowHeight', + 'columnCount', + 'bottomPadding', function() { + if (typeof this.heightForIndex === 'function') { + return this._totalHeightWithHeightForIndex(); + } else { + return this._totalHeightWithStaticRowHeight(); + } + }), + + _doRowHeightDidChange: function() { + this._cachedHeights = [0]; + this._cachedPos = 0; + this._syncChildViews(); + }, + + _rowHeightDidChange: Ember.observer('rowHeight', function() { + Ember.run.once(this, this._doRowHeightDidChange); + }), + + _totalHeightWithHeightForIndex: function() { + var length = this.get('content.length'); + return this._cachedHeightLookup(length); + }, + + _totalHeightWithStaticRowHeight: function() { + var contentLength, rowHeight, columnCount, bottomPadding; + + contentLength = get(this, 'content.length'); + rowHeight = get(this, 'rowHeight'); + columnCount = get(this, 'columnCount'); + bottomPadding = get(this, 'bottomPadding'); + + return ((ceil(contentLength / columnCount)) * rowHeight) + bottomPadding; + }, + + /** + @private + @method _prepareChildForReuse + */ + _prepareChildForReuse: function(childView) { + childView.prepareForReuse(); + }, + + createChildView: function (_view) { + return this._super(_view, this._itemViewProps || {}); + }, + + /** + @private + @method _reuseChildForContentIndex + */ + _reuseChildForContentIndex: function(childView, contentIndex) { + var content, context, newContext, childsCurrentContentIndex, position, enableProfiling, oldChildView; + + var contentViewClass = this.itemViewForIndex(contentIndex); + + if (childView.constructor !== contentViewClass) { + // rather then associative arrays, lets move childView + contentEntry maping to a Map + var i = this._childViews.indexOf(childView); + childView.destroy(); + childView = this.createChildView(contentViewClass); + this.insertAt(i, childView); + } + + content = get(this, 'content'); + enableProfiling = get(this, 'enableProfiling'); + position = this.positionForIndex(contentIndex); + childView.updatePosition(position); + + set(childView, 'contentIndex', contentIndex); + + if (enableProfiling) { + Ember.instrument('view._reuseChildForContentIndex', position, function() { + + }, this); + } + + newContext = content.objectAt(contentIndex); + childView.updateContext(newContext); + }, + + /** + @private + @method positionForIndex + */ + positionForIndex: function(index) { + if (typeof this.heightForIndex !== 'function') { + return this._singleHeightPosForIndex(index); + } + else { + return this._multiHeightPosForIndex(index); + } + }, + + _singleHeightPosForIndex: function(index) { + var elementWidth, width, columnCount, rowHeight, y, x; + + elementWidth = get(this, 'elementWidth') || 1; + width = get(this, 'width') || 1; + columnCount = get(this, 'columnCount'); + rowHeight = get(this, 'rowHeight'); + + y = (rowHeight * floor(index/columnCount)); + x = (index % columnCount) * elementWidth; + + return { + y: y, + x: x + }; + }, + + // 0 maps to 0, 1 maps to heightForIndex(i) + _multiHeightPosForIndex: function(index) { + var elementWidth, width, columnCount, rowHeight, y, x; + + elementWidth = get(this, 'elementWidth') || 1; + width = get(this, 'width') || 1; + columnCount = get(this, 'columnCount'); + + x = (index % columnCount) * elementWidth; + y = this._cachedHeightLookup(index); + + return { + x: x, + y: y + }; + }, + + _cachedHeightLookup: function(index) { + for (var i = this._cachedPos; i < index; i++) { + this._cachedHeights[i + 1] = this._cachedHeights[i] + this.heightForIndex(i); + } + this._cachedPos = i; + return this._cachedHeights[index]; + }, + + /** + @private + @method _childViewCount + */ + _childViewCount: function() { + var contentLength, childViewCountForHeight; + + contentLength = get(this, 'content.length'); + childViewCountForHeight = this._numChildViewsForViewport(); + + return min(contentLength, childViewCountForHeight); + }, + + /** + @private + + Returns a number of columns in the Ember.ListView (for grid layout). + + If you want to have a multi column layout, you need to specify both + `width` and `elementWidth`. + + If no `elementWidth` is specified, it returns `1`. Otherwise, it will + try to fit as many columns as possible for a given `width`. + + @property {Ember.ComputedProperty} columnCount + */ + columnCount: Ember.computed('width', 'elementWidth', function() { + var elementWidth, width, count; + + elementWidth = get(this, 'elementWidth'); + width = get(this, 'width'); + + if (elementWidth && width > elementWidth) { + count = floor(width / elementWidth); + } else { + count = 1; + } + + return count; + }), + + /** + @private + + Fires every time column count is changed. + + @event columnCountDidChange + */ + columnCountDidChange: Ember.observer(function() { + var ratio, currentScrollTop, proposedScrollTop, maxScrollTop, + scrollTop, lastColumnCount, newColumnCount, element; + + lastColumnCount = this._lastColumnCount; + + currentScrollTop = this.scrollTop; + newColumnCount = get(this, 'columnCount'); + maxScrollTop = get(this, 'maxScrollTop'); + element = this.element; + + this._lastColumnCount = newColumnCount; + + if (lastColumnCount) { + ratio = (lastColumnCount / newColumnCount); + proposedScrollTop = currentScrollTop * ratio; + scrollTop = min(maxScrollTop, proposedScrollTop); + + this._scrollTo(scrollTop); + this.scrollTop = scrollTop; + } + + if (arguments.length > 0) { + // invoked by observer + Ember.run.schedule('afterRender', this, this._syncListContainerWidth); + } + }, 'columnCount'), + + /** + @private + + Computes max possible scrollTop value given the visible viewport + and scrollable container div height. + + @property {Ember.ComputedProperty} maxScrollTop + */ + maxScrollTop: Ember.computed('height', 'totalHeight', function(){ + var totalHeight, viewportHeight; + + totalHeight = get(this, 'totalHeight'); + viewportHeight = get(this, 'height'); + + return max(0, totalHeight - viewportHeight); + }), + + /** + @private + + Determines whether the emptyView is the current childView. + + @method _isChildEmptyView + */ + _isChildEmptyView: function() { + var emptyView = get(this, 'emptyView'); + + return emptyView && emptyView instanceof Ember.View && + this._childViews.length === 1 && this._childViews.indexOf(emptyView) === 0; + }, + + /** + @private + + Computes the number of views that would fit in the viewport area. + You must specify `height` and `rowHeight` parameters for the number of + views to be computed properly. + + @method _numChildViewsForViewport + */ + _numChildViewsForViewport: function() { + + if (this.heightForIndex) { + return this._numChildViewsForViewportWithMultiHeight(); + } else { + return this._numChildViewsForViewportWithoutMultiHeight(); + } + }, + + _numChildViewsForViewportWithoutMultiHeight: function() { + var height, rowHeight, paddingCount, columnCount; + + height = get(this, 'height'); + rowHeight = get(this, 'rowHeight'); + paddingCount = get(this, 'paddingCount'); + columnCount = get(this, 'columnCount'); + + return (ceil(height / rowHeight) * columnCount) + (paddingCount * columnCount); + }, + + _numChildViewsForViewportWithMultiHeight: function() { + var rowHeight, paddingCount, columnCount; + var scrollTop = this.scrollTop; + var viewportHeight = this.get('height'); + var length = this.get('content.length'); + var heightfromTop = 0; + var padding = get(this, 'paddingCount'); + + var startingIndex = this._calculatedStartingIndex(); + var currentHeight = 0; + + var offsetHeight = this._cachedHeightLookup(startingIndex); + for (var i = 0; i < length; i++) { + if (this._cachedHeightLookup(startingIndex + i + 1) - offsetHeight > viewportHeight) { + break; + } + } + + return i + padding + 1; + }, + + + /** + @private + + Computes the starting index of the item views array. + Takes `scrollTop` property of the element into account. + + Is used in `_syncChildViews`. + + @method _startingIndex + */ + _startingIndex: function(_contentLength) { + var scrollTop, rowHeight, columnCount, calculatedStartingIndex, + contentLength; + + if (_contentLength === undefined) { + contentLength = get(this, 'content.length'); + } else { + contentLength = _contentLength; + } + + scrollTop = this.scrollTop; + rowHeight = get(this, 'rowHeight'); + columnCount = get(this, 'columnCount'); + + if (this.heightForIndex) { + calculatedStartingIndex = this._calculatedStartingIndex(); + } else { + calculatedStartingIndex = floor(scrollTop / rowHeight) * columnCount; + } + + var viewsNeededForViewport = this._numChildViewsForViewport(); + var paddingCount = (1 * columnCount); + var largestStartingIndex = max(contentLength - viewsNeededForViewport, 0); + + return min(calculatedStartingIndex, largestStartingIndex); + }, + + _calculatedStartingIndex: function() { + var rowHeight, paddingCount, columnCount; + var scrollTop = this.scrollTop; + var viewportHeight = this.get('height'); + var length = this.get('content.length'); + var heightfromTop = 0; + var padding = get(this, 'paddingCount'); + + for (var i = 0; i < length; i++) { + if (this._cachedHeightLookup(i + 1) >= scrollTop) { + break; + } + } + + return i; + }, + + /** + @private + @event contentWillChange + */ + contentWillChange: Ember.beforeObserver(function() { + var content = get(this, 'content'); + + if (content) { + content.removeArrayObserver(this); + } + }, 'content'), + + /**), + @private + @event contentDidChange + */ + contentDidChange: Ember.observer(function() { + addContentArrayObserver.call(this); + syncChildViews.call(this); + }, 'content'), + + /** + @private + @property {Function} needsSyncChildViews + */ + needsSyncChildViews: Ember.observer(syncChildViews, 'height', 'width', 'columnCount'), + + /** + @private + + Returns a new item view. Takes `contentIndex` to set the context + of the returned view properly. + + @param {Number} contentIndex item index in the content array + @method _addItemView + */ + _addItemView: function (contentIndex) { + var itemViewClass, childView; + + itemViewClass = this.itemViewForIndex(contentIndex); + childView = this.createChildView(itemViewClass); + this.pushObject(childView); + }, + + /** + @public + + Returns a view class for the provided contentIndex. If the view is + different then the one currently present it will remove the existing view + and replace it with an instance of the class provided + + @param {Number} contentIndex item index in the content array + @method _addItemView + @returns {Ember.View} ember view class for this index + */ + itemViewForIndex: function(contentIndex) { + return get(this, 'itemViewClass'); + }, + + /** + @public + + Returns a view class for the provided contentIndex. If the view is + different then the one currently present it will remove the existing view + and replace it with an instance of the class provided + + @param {Number} contentIndex item index in the content array + @method _addItemView + @returns {Ember.View} ember view class for this index + */ + heightForIndex: null, + + /** + @private + + Intelligently manages the number of childviews. + + @method _syncChildViews + **/ + _syncChildViews: function () { + var childViews, childViewCount, + numberOfChildViews, numberOfChildViewsNeeded, + contentIndex, startingIndex, endingIndex, + contentLength, emptyView, count, delta; + + if (this.isDestroyed || this.isDestroying) { + return; + } + + contentLength = get(this, 'content.length'); + emptyView = get(this, 'emptyView'); + + childViewCount = this._childViewCount(); + childViews = this.positionOrderedChildViews(); + + if (this._isChildEmptyView()) { + removeEmptyView.call(this); + } + + startingIndex = this._startingIndex(); + endingIndex = startingIndex + childViewCount; + + numberOfChildViewsNeeded = childViewCount; + numberOfChildViews = childViews.length; + + delta = numberOfChildViewsNeeded - numberOfChildViews; + + if (delta === 0) { + // no change + } else if (delta > 0) { + // more views are needed + contentIndex = this._lastEndingIndex; + + for (count = 0; count < delta; count++, contentIndex++) { + this._addItemView(contentIndex); + } + } else { + // less views are needed + forEach.call( + childViews.splice(numberOfChildViewsNeeded, numberOfChildViews), + removeAndDestroy, + this + ); + } + + this._reuseChildren(); + + this._lastStartingIndex = startingIndex; + this._lastEndingIndex = this._lastEndingIndex + delta; + + if (contentLength === 0 || contentLength === undefined) { + addEmptyView.call(this); + } + }, + + /** + @private + + Applies an inline width style to the list container. + + @method _syncListContainerWidth + **/ + _syncListContainerWidth: function() { + var elementWidth, columnCount, containerWidth, element; + + elementWidth = get(this, 'elementWidth'); + columnCount = get(this, 'columnCount'); + containerWidth = elementWidth * columnCount; + element = this.$('.ember-list-container'); + + if (containerWidth && element) { + element.css('width', containerWidth); + } + }, + + /** + @private + @method _reuseChildren + */ + _reuseChildren: function(){ + var contentLength, childViews, childViewsLength, + startingIndex, endingIndex, childView, attrs, + contentIndex, visibleEndingIndex, maxContentIndex, + contentIndexEnd, scrollTop; + + scrollTop = this.scrollTop; + contentLength = get(this, 'content.length'); + maxContentIndex = max(contentLength - 1, 0); + childViews = this.getReusableChildViews(); + childViewsLength = childViews.length; + + startingIndex = this._startingIndex(); + visibleEndingIndex = startingIndex + this._numChildViewsForViewport(); + + endingIndex = min(maxContentIndex, visibleEndingIndex); + + contentIndexEnd = min(visibleEndingIndex, startingIndex + childViewsLength); + + for (contentIndex = startingIndex; contentIndex < contentIndexEnd; contentIndex++) { + childView = childViews[contentIndex % childViewsLength]; + this._reuseChildForContentIndex(childView, contentIndex); + } + }, + + /** + @private + @method getReusableChildViews + */ + getReusableChildViews: function() { + return this._childViews; + }, + + /** + @private + @method positionOrderedChildViews + */ + positionOrderedChildViews: function() { + return this.getReusableChildViews().sort(sortByContentIndex); + }, + + arrayWillChange: Ember.K, + + /** + @private + @event arrayDidChange + */ + // TODO: refactor + arrayDidChange: function(content, start, removedCount, addedCount) { + var index, contentIndex, state; + + if (this._isChildEmptyView()) { + removeEmptyView.call(this); + } + + // Support old and new Ember versions + state = this._state || this.state; + + if (state === 'inDOM') { + // ignore if all changes are out of the visible change + if (start >= this._lastStartingIndex || start < this._lastEndingIndex) { + index = 0; + // ignore all changes not in the visible range + // this can re-position many, rather then causing a cascade of re-renders + forEach.call( + this.positionOrderedChildViews(), + function(childView) { + contentIndex = this._lastStartingIndex + index; + this._reuseChildForContentIndex(childView, contentIndex); + index++; + }, + this + ); + } + + syncChildViews.call(this); + } + }, + + destroy: function () { + if (!this._super()) { + return; + } + + if (this._createdEmptyView) { + this._createdEmptyView.destroy(); + } + + return this; + } + }); + }); +define("list-view/main", + ["list-view/reusable_list_item_view","list-view/virtual_list_view","list-view/list_item_view","list-view/helper","list-view/list_view","list-view/list_view_helper"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__) { + "use strict"; + var ReusableListItemView = __dependency1__["default"]; + var VirtualListView = __dependency2__["default"]; + var ListItemView = __dependency3__["default"]; + var EmberList = __dependency4__.EmberList; + var EmberVirtualList = __dependency4__.EmberVirtualList; + var ListView = __dependency5__["default"]; + var ListViewHelper = __dependency6__["default"]; + + Ember.ReusableListItemView = ReusableListItemView; + Ember.VirtualListView = VirtualListView; + Ember.ListItemView = ListItemView; + Ember.ListView = ListView; + Ember.ListViewHelper = ListViewHelper; + + Ember.Handlebars.registerHelper('ember-list', EmberList); + Ember.Handlebars.registerHelper('ember-virtual-list', EmberVirtualList); + }); +define("list-view/reusable_list_item_view", + ["list-view/list_item_view_mixin","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var ListItemViewMixin = __dependency1__["default"]; + + var get = Ember.get, set = Ember.set; + + __exports__["default"] = Ember.View.extend(ListItemViewMixin, { + prepareForReuse: Ember.K, + + init: function () { + this._super(); + var context = Ember.ObjectProxy.create(); + this.set('context', context); + this._proxyContext = context; + }, + + isVisible: Ember.computed('context.content', function () { + return !!this.get('context.content'); + }), + + updateContext: function (newContext) { + var context = get(this._proxyContext, 'content'); + + // Support old and new Ember versions + var state = this._state || this.state; + + if (context !== newContext) { + if (state === 'inDOM') { + this.prepareForReuse(newContext); + } + + set(this._proxyContext, 'content', newContext); + + if (newContext && newContext.isController) { + set(this, 'controller', newContext); + } + } + } + }); + }); +define("list-view/virtual_list_scroller_events", + ["exports"], + function(__exports__) { + "use strict"; + /*jshint validthis:true */ + + var fieldRegex = /input|textarea|select/i, + hasTouch = ('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch, + handleStart, handleMove, handleEnd, handleCancel, + startEvent, moveEvent, endEvent, cancelEvent; + if (hasTouch) { + startEvent = 'touchstart'; + handleStart = function (e) { + var touch = e.touches[0], + target = touch && touch.target; + // avoid e.preventDefault() on fields + if (target && fieldRegex.test(target.tagName)) { + return; + } + bindWindow(this.scrollerEventHandlers); + this.willBeginScroll(e.touches, e.timeStamp); + e.preventDefault(); + }; + moveEvent = 'touchmove'; + handleMove = function (e) { + this.continueScroll(e.touches, e.timeStamp); + }; + endEvent = 'touchend'; + handleEnd = function (e) { + // if we didn't end up scrolling we need to + // synthesize click since we did e.preventDefault() + // on touchstart + if (!this._isScrolling) { + synthesizeClick(e); + } + unbindWindow(this.scrollerEventHandlers); + this.endScroll(e.timeStamp); + }; + cancelEvent = 'touchcancel'; + handleCancel = function (e) { + unbindWindow(this.scrollerEventHandlers); + this.endScroll(e.timeStamp); + }; + } else { + startEvent = 'mousedown'; + handleStart = function (e) { + if (e.which !== 1) { + return; + } + var target = e.target; + // avoid e.preventDefault() on fields + if (target && fieldRegex.test(target.tagName)) { + return; + } + bindWindow(this.scrollerEventHandlers); + this.willBeginScroll([e], e.timeStamp); + e.preventDefault(); + }; + moveEvent = 'mousemove'; + handleMove = function (e) { + this.continueScroll([e], e.timeStamp); + }; + endEvent = 'mouseup'; + handleEnd = function (e) { + unbindWindow(this.scrollerEventHandlers); + this.endScroll(e.timeStamp); + }; + cancelEvent = 'mouseout'; + handleCancel = function (e) { + if (e.relatedTarget) { + return; + } + unbindWindow(this.scrollerEventHandlers); + this.endScroll(e.timeStamp); + }; + } + + function handleWheel(e) { + this.mouseWheel(e); + e.preventDefault(); + } + + function bindElement(el, handlers) { + el.addEventListener(startEvent, handlers.start, false); + el.addEventListener('mousewheel', handlers.wheel, false); + } + + function unbindElement(el, handlers) { + el.removeEventListener(startEvent, handlers.start, false); + el.removeEventListener('mousewheel', handlers.wheel, false); + } + + function bindWindow(handlers) { + window.addEventListener(moveEvent, handlers.move, true); + window.addEventListener(endEvent, handlers.end, true); + window.addEventListener(cancelEvent, handlers.cancel, true); + } + + function unbindWindow(handlers) { + window.removeEventListener(moveEvent, handlers.move, true); + window.removeEventListener(endEvent, handlers.end, true); + window.removeEventListener(cancelEvent, handlers.cancel, true); + } + + __exports__["default"] = Ember.Mixin.create({ + init: function() { + this.on('didInsertElement', this, 'bindScrollerEvents'); + this.on('willDestroyElement', this, 'unbindScrollerEvents'); + this.scrollerEventHandlers = { + start: bind(this, handleStart), + move: bind(this, handleMove), + end: bind(this, handleEnd), + cancel: bind(this, handleCancel), + wheel: bind(this, handleWheel) + }; + return this._super(); + }, + scrollElement: Ember.computed.oneWay('element').readOnly(), + bindScrollerEvents: function() { + var el = this.get('scrollElement'), + handlers = this.scrollerEventHandlers; + bindElement(el, handlers); + }, + unbindScrollerEvents: function() { + var el = this.get('scrollElement'), + handlers = this.scrollerEventHandlers; + unbindElement(el, handlers); + unbindWindow(handlers); + } + }); + + function bind(view, handler) { + return function (evt) { + handler.call(view, evt); + }; + } + + function synthesizeClick(e) { + var point = e.changedTouches[0], + target = point.target, + ev; + if (target && fieldRegex.test(target.tagName)) { + ev = document.createEvent('MouseEvents'); + ev.initMouseEvent('click', true, true, e.view, 1, point.screenX, point.screenY, point.clientX, point.clientY, e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, 0, null); + return target.dispatchEvent(ev); + } + } + }); +define("list-view/virtual_list_view", + ["list-view/list_view_mixin","list-view/list_view_helper","list-view/virtual_list_scroller_events","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + /* + global Scroller + */ + + var ListViewMixin = __dependency1__["default"]; + var ListViewHelper = __dependency2__["default"]; + var VirtualListScrollerEvents = __dependency3__["default"]; + + var get = Ember.get; + + function updateScrollerDimensions(target) { + var width, height, totalHeight; + + target = target || this; // jshint ignore:line + + width = get(target, 'width'); + height = get(target, 'height'); + totalHeight = get(target, 'totalHeight'); // jshint ignore:line + + target.scroller.setDimensions(width, height, width, totalHeight); + target.trigger('scrollerDimensionsDidChange'); + } + + /** + VirtualListView + + @class VirtualListView + @namespace Ember + */ + __exports__["default"] = Ember.ContainerView.extend(ListViewMixin, VirtualListScrollerEvents, { + _isScrolling: false, + _mouseWheel: null, + css: { + position: 'relative', + overflow: 'hidden' + }, + + init: function(){ + this._super(); + this.setupScroller(); + this.setupPullToRefresh(); + }, + _scrollerTop: 0, + applyTransform: ListViewHelper.apply3DTransform, + + setupScroller: function(){ + var view = this; + + view.scroller = new Scroller(function(left, top/*, zoom*/) { + // Support old and new Ember versions + var state = view._state || view.state; + + if (state !== 'inDOM') { + return; + } + + if (view.listContainerElement) { + view._scrollerTop = top; + view._scrollContentTo(top); + view.applyTransform(view.listContainerElement, 0, -top); + } + }, { + scrollingX: false, + scrollingComplete: function(){ + view.trigger('scrollingDidComplete'); + } + }); + + view.trigger('didInitializeScroller'); + updateScrollerDimensions(view); + }, + setupPullToRefresh: function() { + if (!this.pullToRefreshViewClass) { + return; + } + + this._insertPullToRefreshView(); + this._activateScrollerPullToRefresh(); + }, + _insertPullToRefreshView: function(){ + this.pullToRefreshView = this.createChildView(this.pullToRefreshViewClass); + this.insertAt(0, this.pullToRefreshView); + + var view = this; + + this.pullToRefreshView.on('didInsertElement', function() { + Ember.run.scheduleOnce('afterRender', this, function(){ + view.applyTransform(this.element, 0, -1 * view.pullToRefreshViewHeight); + }); + }); + }, + _activateScrollerPullToRefresh: function(){ + var view = this; + function activatePullToRefresh(){ + view.pullToRefreshView.set('active', true); + view.trigger('activatePullToRefresh'); + } + function deactivatePullToRefresh() { + view.pullToRefreshView.set('active', false); + view.trigger('deactivatePullToRefresh'); + } + function startPullToRefresh() { + Ember.run(function(){ + view.pullToRefreshView.set('refreshing', true); + + function finishRefresh(){ + if (view && !view.get('isDestroyed') && !view.get('isDestroying')) { + view.scroller.finishPullToRefresh(); + view.pullToRefreshView.set('refreshing', false); + } + } + view.startRefresh(finishRefresh); + }); + } + this.scroller.activatePullToRefresh( + this.pullToRefreshViewHeight, + activatePullToRefresh, + deactivatePullToRefresh, + startPullToRefresh + ); + }, + + getReusableChildViews: function(){ + var firstView = this._childViews[0]; + if (firstView && firstView === this.pullToRefreshView) { + return this._childViews.slice(1); + } else { + return this._childViews; + } + }, + + scrollerDimensionsNeedToChange: Ember.observer(function() { + Ember.run.once(this, updateScrollerDimensions); + }, 'width', 'height', 'totalHeight'), + + didInsertElement: function() { + this.listContainerElement = this.$('> .ember-list-container')[0]; + }, + + willBeginScroll: function(touches, timeStamp) { + this._isScrolling = false; + this.trigger('scrollingDidStart'); + + this.scroller.doTouchStart(touches, timeStamp); + }, + + continueScroll: function(touches, timeStamp) { + var startingScrollTop, endingScrollTop, event; + + if (this._isScrolling) { + this.scroller.doTouchMove(touches, timeStamp); + } else { + startingScrollTop = this._scrollerTop; + + this.scroller.doTouchMove(touches, timeStamp); + + endingScrollTop = this._scrollerTop; + + if (startingScrollTop !== endingScrollTop) { + event = Ember.$.Event("scrollerstart"); + Ember.$(touches[0].target).trigger(event); + + this._isScrolling = true; + } + } + }, + + endScroll: function(timeStamp) { + this.scroller.doTouchEnd(timeStamp); + }, + + // api + scrollTo: function(y, animate) { + if (animate === undefined) { + animate = true; + } + + this.scroller.scrollTo(0, y, animate, 1); + }, + + // events + mouseWheel: function(e){ + var inverted, delta, candidatePosition; + + inverted = e.webkitDirectionInvertedFromDevice; + delta = e.wheelDeltaY * (inverted ? 0.8 : -0.8); + candidatePosition = this.scroller.__scrollTop + delta; + + if ((candidatePosition >= 0) && (candidatePosition <= this.scroller.__maxScrollTop)) { + this.scroller.scrollBy(0, delta, true); + e.stopPropagation(); + } + + return false; + } + }); + }); + requireModule('list-view/main'); +})(this); \ No newline at end of file diff --git a/vendor/assets/javascripts/loader.js b/vendor/assets/javascripts/loader.js index eaee097c7d..b05ae92b2e 100644 --- a/vendor/assets/javascripts/loader.js +++ b/vendor/assets/javascripts/loader.js @@ -1,4 +1,4 @@ -var define, requireModule, require, requirejs; +var define, requireModule, require, requirejs, hasModule; (function() { var registry = {}, seen = {}, state = {}; @@ -32,6 +32,10 @@ var define, requireModule, require, requirejs; }; } + hasModule = function(name){ + return !!registry[name]; + }; + requirejs = require = requireModule = function(name) { if (state[name] !== FAILED && seen.hasOwnProperty(name)) { diff --git a/vendor/assets/javascripts/production/ember.js b/vendor/assets/javascripts/production/ember.js index 8a0ea9c7cc..e5e33c4e7e 100644 --- a/vendor/assets/javascripts/production/ember.js +++ b/vendor/assets/javascripts/production/ember.js @@ -5,102 +5,102 @@ * Portions Copyright 2008-2011 Apple Inc. All rights reserved. * @license Licensed under MIT license * See https://raw.github.com/emberjs/ember.js/master/LICENSE - * @version 1.7.1 + * @version 1.9.0 */ (function() { -var define, requireModule, require, requirejs, Ember; +var enifed, requireModule, eriuqer, requirejs, Ember; (function() { Ember = this.Ember = this.Ember || {}; - if (typeof Ember === 'undefined') { Ember = {} }; + if (typeof Ember === 'undefined') { Ember = {}; }; + function UNDEFINED() { } if (typeof Ember.__loader === 'undefined') { var registry = {}, seen = {}; - define = function(name, deps, callback) { + enifed = function(name, deps, callback) { registry[name] = { deps: deps, callback: callback }; }; - requirejs = require = requireModule = function(name) { - if (seen.hasOwnProperty(name)) { return seen[name]; } + requirejs = eriuqer = requireModule = function(name) { + var s = seen[name]; + + if (s !== undefined) { return seen[name]; } + if (s === UNDEFINED) { return undefined; } + seen[name] = {}; if (!registry[name]) { throw new Error("Could not find module " + name); } - var mod = registry[name], - deps = mod.deps, - callback = mod.callback, - reified = [], - exports; + var mod = registry[name]; + var deps = mod.deps; + var callback = mod.callback; + var reified = []; + var exports; + var length = deps.length; - for (var i=0, l=deps.length; i 3 ? slice.call(arguments, 3) : undefined; + var stack = this.DEBUG ? new Error() : undefined; + var length = arguments.length; + var args; + + if (length > 3) { + args = new Array(length - 3); + for (var i = 3; i < length; i++) { + args[i-3] = arguments[i]; + } + } else { + args = undefined; + } + if (!this.currentInstance) { createAutorun(this); } return this.currentInstance.schedule(queueName, target, method, args, false, stack); }, @@ -231,15 +255,34 @@ define("backburner", method = target[method]; } - var stack = this.DEBUG ? new Error() : undefined, - args = arguments.length > 3 ? slice.call(arguments, 3) : undefined; - if (!this.currentInstance) { createAutorun(this); } + var stack = this.DEBUG ? new Error() : undefined; + var length = arguments.length; + var args; + + if (length > 3) { + args = new Array(length - 3); + for (var i = 3; i < length; i++) { + args[i-3] = arguments[i]; + } + } else { + args = undefined; + } + + if (!this.currentInstance) { + createAutorun(this); + } return this.currentInstance.schedule(queueName, target, method, args, true, stack); }, setTimeout: function() { - var args = slice.call(arguments), - length = args.length, + var l = arguments.length; + var args = new Array(l); + + for (var x = 0; x < l; x++) { + args[x] = arguments[x]; + } + + var length = args.length, method, wait, target, methodOrTarget, methodOrWait, methodOrArgs; @@ -285,7 +328,7 @@ define("backburner", } } - var executeAt = (+new Date()) + parseInt(wait, 10); + var executeAt = now() + parseInt(wait, 10); if (isString(method)) { method = target[method]; @@ -306,9 +349,9 @@ define("backburner", } // find position to insert - var i = searchTimer(executeAt, timers); + var i = searchTimer(executeAt, this._timers); - timers.splice(i, 0, executeAt, fn); + this._timers.splice(i, 0, executeAt, fn); updateLaterTimer(this, executeAt, wait); @@ -316,13 +359,10 @@ define("backburner", }, throttle: function(target, method /* , args, wait, [immediate] */) { - var self = this, - args = arguments, - immediate = pop.call(args), - wait, - throttler, - index, - timer; + var backburner = this; + var args = arguments; + var immediate = pop.call(args); + var wait, throttler, index, timer; if (isNumber(immediate) || isString(immediate)) { wait = immediate; @@ -338,16 +378,16 @@ define("backburner", timer = global.setTimeout(function() { if (!immediate) { - self.run.apply(self, args); + backburner.run.apply(backburner, args); } - var index = findThrottler(target, method, self._throttlers); + var index = findThrottler(target, method, backburner._throttlers); if (index > -1) { - self._throttlers.splice(index, 1); + backburner._throttlers.splice(index, 1); } }, wait); if (immediate) { - self.run.apply(self, args); + this.run.apply(this, args); } throttler = [target, method, timer]; @@ -358,13 +398,10 @@ define("backburner", }, debounce: function(target, method /* , args, wait, [immediate] */) { - var self = this, - args = arguments, - immediate = pop.call(args), - wait, - index, - debouncee, - timer; + var backburner = this; + var args = arguments; + var immediate = pop.call(args); + var wait, index, debouncee, timer; if (isNumber(immediate) || isString(immediate)) { wait = immediate; @@ -385,21 +422,25 @@ define("backburner", timer = global.setTimeout(function() { if (!immediate) { - self.run.apply(self, args); + backburner.run.apply(backburner, args); } - var index = findDebouncee(target, method, self._debouncees); + var index = findDebouncee(target, method, backburner._debouncees); if (index > -1) { - self._debouncees.splice(index, 1); + backburner._debouncees.splice(index, 1); } }, wait); if (immediate && index === -1) { - self.run.apply(self, args); + backburner.run.apply(backburner, args); } - debouncee = [target, method, timer]; + debouncee = [ + target, + method, + timer + ]; - self._debouncees.push(debouncee); + backburner._debouncees.push(debouncee); return debouncee; }, @@ -419,7 +460,7 @@ define("backburner", clearTimeout(this._laterTimer); this._laterTimer = null; } - timers = []; + this._timers = []; if (this._autorun) { clearTimeout(this._autorun); @@ -428,7 +469,7 @@ define("backburner", }, hasTimers: function() { - return !!timers.length || !!this._debouncees.length || !!this._throttlers.length || this._autorun; + return !!this._timers.length || !!this._debouncees.length || !!this._throttlers.length || this._autorun; }, cancel: function(timer) { @@ -437,9 +478,18 @@ define("backburner", if (timer && timerType === 'object' && timer.queue && timer.method) { // we're cancelling a deferOnce return timer.queue.cancel(timer); } else if (timerType === 'function') { // we're cancelling a setTimeout - for (var i = 0, l = timers.length; i < l; i += 2) { - if (timers[i + 1] === timer) { - timers.splice(i, 2); // remove the two elements + for (var i = 0, l = this._timers.length; i < l; i += 2) { + if (this._timers[i + 1] === timer) { + this._timers.splice(i, 2); // remove the two elements + if (i === 0) { + if (this._laterTimer) { // Active timer? Then clear timer and reset for future timer + clearTimeout(this._laterTimer); + this._laterTimer = null; + } + if (this._timers.length > 0) { // Update to next available timer when available + updateLaterTimer(this, this._timers[0], this._timers[0] - now()); + } + } return true; } } @@ -452,18 +502,17 @@ define("backburner", }, _cancelItem: function(findMethod, array, timer){ - var item, - index; + var item, index; if (timer.length < 3) { return false; } index = findMethod(timer[0], timer[1], array); - if(index > -1) { + if (index > -1) { item = array[index]; - if(item[2] === timer[2]){ + if (item[2] === timer[2]) { array.splice(index, 1); clearTimeout(timer[2]); return true; @@ -486,21 +535,10 @@ define("backburner", Backburner.prototype.end = wrapInTryCatch(originalEnd); } - function wrapInTryCatch(func) { - return function () { - try { - return func.apply(this, arguments); - } catch (e) { - throw e; - } - }; - } - function getOnError(options) { return options.onError || (options.onErrorTarget && options.onErrorTarget[options.onErrorMethod]); } - function createAutorun(backburner) { backburner.begin(); backburner._autorun = global.setTimeout(function() { @@ -509,33 +547,48 @@ define("backburner", }); } - function updateLaterTimer(self, executeAt, wait) { - if (!self._laterTimer || executeAt < self._laterTimerExpiresAt) { - self._laterTimer = global.setTimeout(function() { - self._laterTimer = null; - self._laterTimerExpiresAt = null; - executeTimers(self); + function updateLaterTimer(backburner, executeAt, wait) { + var n = now(); + if (!backburner._laterTimer || executeAt < backburner._laterTimerExpiresAt || backburner._laterTimerExpiresAt < n) { + + if (backburner._laterTimer) { + // Clear when: + // - Already expired + // - New timer is earlier + clearTimeout(backburner._laterTimer); + + if (backburner._laterTimerExpiresAt < n) { // If timer was never triggered + // Calculate the left-over wait-time + wait = Math.max(0, executeAt - n); + } + } + + backburner._laterTimer = global.setTimeout(function() { + backburner._laterTimer = null; + backburner._laterTimerExpiresAt = null; + executeTimers(backburner); }, wait); - self._laterTimerExpiresAt = executeAt; + + backburner._laterTimerExpiresAt = n + wait; } } - function executeTimers(self) { - var now = +new Date(), - time, fns, i, l; + function executeTimers(backburner) { + var n = now(); + var fns, i, l; - self.run(function() { - i = searchTimer(now, timers); + backburner.run(function() { + i = searchTimer(n, backburner._timers); - fns = timers.splice(0, i); + fns = backburner._timers.splice(0, i); for (i = 1, l = fns.length; i < l; i += 2) { - self.schedule(self.options.defaultQueue, null, fns[i]); + backburner.schedule(backburner.options.defaultQueue, null, fns[i]); } }); - if (timers.length) { - updateLaterTimer(self, timers[0], timers[0] - now); + if (backburner._timers.length) { + updateLaterTimer(backburner, backburner._timers[0], backburner._timers[0] - n); } } @@ -548,8 +601,8 @@ define("backburner", } function findItem(target, method, collection) { - var item, - index = -1; + var item; + var index = -1; for (var i = 0, l = collection.length; i < l; i++) { item = collection[i]; @@ -562,10 +615,31 @@ define("backburner", return index; } - function searchTimer(time, timers) { - var start = 0, - end = timers.length - 2, - middle, l; + __exports__["default"] = Backburner; + }); +enifed("backburner.umd", + ["./backburner"], + function(__dependency1__) { + "use strict"; + var Backburner = __dependency1__["default"]; + + /* global define:true module:true window: true */ + if (typeof enifed === 'function' && enifed.amd) { + enifed(function() { return Backburner; }); + } else if (typeof module !== 'undefined' && module.exports) { + module.exports = Backburner; + } else if (typeof this !== 'undefined') { + this['Backburner'] = Backburner; + } + }); +enifed("backburner/binary-search", + ["exports"], + function(__exports__) { + "use strict"; + __exports__["default"] = function binarySearch(time, timers) { + var start = 0; + var end = timers.length - 2; + var middle, l; while (start < end) { // since timers is an array of pairs 'l' will always @@ -585,40 +659,37 @@ define("backburner", return (time >= timers[start]) ? start + 2 : start; } - - __exports__.Backburner = Backburner; }); -define("backburner/deferred_action_queues", - ["backburner/utils","backburner/queue","exports"], +enifed("backburner/deferred-action-queues", + ["./utils","./queue","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; - var Utils = __dependency1__["default"]; - var Queue = __dependency2__.Queue; - - var each = Utils.each, - isString = Utils.isString; + var each = __dependency1__.each; + var Queue = __dependency2__["default"]; function DeferredActionQueues(queueNames, options) { - var queues = this.queues = {}; + var queues = this.queues = Object.create(null); this.queueNames = queueNames = queueNames || []; this.options = options; each(queueNames, function(queueName) { - queues[queueName] = new Queue(this, queueName, options); + queues[queueName] = new Queue(queueName, options[queueName], options); }); } + function noSuchQueue(name) { + throw new Error("You attempted to schedule an action in a queue (" + name + ") that doesn't exist"); + } + DeferredActionQueues.prototype = { - queueNames: null, - queues: null, - options: null, + schedule: function(name, target, method, args, onceFlag, stack) { + var queues = this.queues; + var queue = queues[name]; - schedule: function(queueName, target, method, args, onceFlag, stack) { - var queues = this.queues, - queue = queues[queueName]; - - if (!queue) { throw new Error("You attempted to schedule an action in a queue (" + queueName + ") that doesn't exist"); } + if (!queue) { + noSuchQueue(name); + } if (onceFlag) { return queue.pushUnique(target, method, args, stack); @@ -627,7 +698,150 @@ define("backburner/deferred_action_queues", } }, - invoke: function(target, method, args, _) { + flush: function() { + var queues = this.queues; + var queueNames = this.queueNames; + var queueName, queue, queueItems, priorQueueNameIndex; + var queueNameIndex = 0; + var numberOfQueues = queueNames.length; + var options = this.options; + + while (queueNameIndex < numberOfQueues) { + queueName = queueNames[queueNameIndex]; + queue = queues[queueName]; + + var numberOfQueueItems = queue._queue.length; + + if (numberOfQueueItems === 0) { + queueNameIndex++; + } else { + queue.flush(false /* async */); + queueNameIndex = 0; + } + } + } + }; + + __exports__["default"] = DeferredActionQueues; + }); +enifed("backburner/platform", + ["exports"], + function(__exports__) { + "use strict"; + // In IE 6-8, try/finally doesn't work without a catch. + // Unfortunately, this is impossible to test for since wrapping it in a parent try/catch doesn't trigger the bug. + // This tests for another broken try/catch behavior that only exhibits in the same versions of IE. + var needsIETryCatchFix = (function(e,x){ + try{ x(); } + catch(e) { } // jshint ignore:line + return !!e; + })(); + __exports__.needsIETryCatchFix = needsIETryCatchFix; + }); +enifed("backburner/queue", + ["./utils","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var isString = __dependency1__.isString; + + function Queue(name, options, globalOptions) { + this.name = name; + this.globalOptions = globalOptions || {}; + this.options = options; + this._queue = []; + this.targetQueues = Object.create(null); + this._queueBeingFlushed = undefined; + } + + Queue.prototype = { + push: function(target, method, args, stack) { + var queue = this._queue; + queue.push(target, method, args, stack); + + return { + queue: this, + target: target, + method: method + }; + }, + + pushUniqueWithoutGuid: function(target, method, args, stack) { + var queue = this._queue; + + for (var i = 0, l = queue.length; i < l; i += 4) { + var currentTarget = queue[i]; + var currentMethod = queue[i+1]; + + if (currentTarget === target && currentMethod === method) { + queue[i+2] = args; // replace args + queue[i+3] = stack; // replace stack + return; + } + } + + queue.push(target, method, args, stack); + }, + + targetQueue: function(targetQueue, target, method, args, stack) { + var queue = this._queue; + + for (var i = 0, l = targetQueue.length; i < l; i += 4) { + var currentMethod = targetQueue[i]; + var currentIndex = targetQueue[i + 1]; + + if (currentMethod === method) { + queue[currentIndex + 2] = args; // replace args + queue[currentIndex + 3] = stack; // replace stack + return; + } + } + + targetQueue.push( + method, + queue.push(target, method, args, stack) - 4 + ); + }, + + pushUniqueWithGuid: function(guid, target, method, args, stack) { + var hasLocalQueue = this.targetQueues[guid]; + + if (hasLocalQueue) { + this.targetQueue(hasLocalQueue, target, method, args, stack); + } else { + this.targetQueues[guid] = [ + method, + this._queue.push(target, method, args, stack) - 4 + ]; + } + + return { + queue: this, + target: target, + method: method + }; + }, + + pushUnique: function(target, method, args, stack) { + var queue = this._queue, currentTarget, currentMethod, i, l; + var KEY = this.globalOptions.GUID_KEY; + + if (target && KEY) { + var guid = target[KEY]; + if (guid) { + return this.pushUniqueWithGuid(guid, target, method, args, stack); + } + } + + this.pushUniqueWithoutGuid(target, method, args, stack); + + return { + queue: this, + target: target, + method: method + }; + }, + + invoke: function(target, method, args, _, _errorRecordedForStack) { if (args && args.length > 0) { method.apply(target, args); } else { @@ -635,7 +849,7 @@ define("backburner/deferred_action_queues", } }, - invokeWithOnError: function(target, method, args, onError) { + invokeWithOnError: function(target, method, args, onError, errorRecordedForStack) { try { if (args && args.length > 0) { method.apply(target, args); @@ -643,179 +857,100 @@ define("backburner/deferred_action_queues", method.call(target); } } catch(error) { - onError(error); + onError(error, errorRecordedForStack); } }, - flush: function() { - var queues = this.queues, - queueNames = this.queueNames, - queueName, queue, queueItems, priorQueueNameIndex, - queueNameIndex = 0, numberOfQueues = queueNames.length, - options = this.options, - onError = options.onError || (options.onErrorTarget && options.onErrorTarget[options.onErrorMethod]), - invoke = onError ? this.invokeWithOnError : this.invoke; - - outerloop: - while (queueNameIndex < numberOfQueues) { - queueName = queueNames[queueNameIndex]; - queue = queues[queueName]; - queueItems = queue._queueBeingFlushed = queue._queue.slice(); - queue._queue = []; - - var queueOptions = queue.options, // TODO: write a test for this - before = queueOptions && queueOptions.before, - after = queueOptions && queueOptions.after, - target, method, args, stack, - queueIndex = 0, numberOfQueueItems = queueItems.length; - - if (numberOfQueueItems && before) { before(); } - - while (queueIndex < numberOfQueueItems) { - target = queueItems[queueIndex]; - method = queueItems[queueIndex+1]; - args = queueItems[queueIndex+2]; - stack = queueItems[queueIndex+3]; // Debugging assistance - - if (isString(method)) { method = target[method]; } - - // method could have been nullified / canceled during flush - if (method) { - invoke(target, method, args, onError); - } - - queueIndex += 4; - } - - queue._queueBeingFlushed = null; - if (numberOfQueueItems && after) { after(); } - - if ((priorQueueNameIndex = indexOfPriorQueueWithActions(this, queueNameIndex)) !== -1) { - queueNameIndex = priorQueueNameIndex; - continue outerloop; - } - - queueNameIndex++; - } - } - }; - - function indexOfPriorQueueWithActions(daq, currentQueueIndex) { - var queueName, queue; - - for (var i = 0, l = currentQueueIndex; i <= l; i++) { - queueName = daq.queueNames[i]; - queue = daq.queues[queueName]; - if (queue._queue.length) { return i; } - } - - return -1; - } - - __exports__.DeferredActionQueues = DeferredActionQueues; - }); -define("backburner/queue", - ["exports"], - function(__exports__) { - "use strict"; - function Queue(daq, name, options) { - this.daq = daq; - this.name = name; - this.globalOptions = options; - this.options = options[name]; - this._queue = []; - } - - Queue.prototype = { - daq: null, - name: null, - options: null, - onError: null, - _queue: null, - - push: function(target, method, args, stack) { + flush: function(sync) { var queue = this._queue; - queue.push(target, method, args, stack); - return {queue: this, target: target, method: method}; - }, + var length = queue.length; - pushUnique: function(target, method, args, stack) { - var queue = this._queue, currentTarget, currentMethod, i, l; + if (length === 0) { + return; + } - for (i = 0, l = queue.length; i < l; i += 4) { - currentTarget = queue[i]; - currentMethod = queue[i+1]; + var globalOptions = this.globalOptions; + var options = this.options; + var before = options && options.before; + var after = options && options.after; + var onError = globalOptions.onError || (globalOptions.onErrorTarget && + globalOptions.onErrorTarget[globalOptions.onErrorMethod]); + var target, method, args, errorRecordedForStack; + var invoke = onError ? this.invokeWithOnError : this.invoke; - if (currentTarget === target && currentMethod === method) { - queue[i+2] = args; // replace args - queue[i+3] = stack; // replace stack - return {queue: this, target: target, method: method}; + this.targetQueues = Object.create(null); + var queueItems = this._queueBeingFlushed = this._queue.slice(); + this._queue = []; + + if (before) { + before(); + } + + for (var i = 0; i < length; i += 4) { + target = queueItems[i]; + method = queueItems[i+1]; + args = queueItems[i+2]; + errorRecordedForStack = queueItems[i+3]; // Debugging assistance + + if (isString(method)) { + method = target[method]; + } + + // method could have been nullified / canceled during flush + if (method) { + // + // ** Attention intrepid developer ** + // + // To find out the stack of this task when it was scheduled onto + // the run loop, add the following to your app.js: + // + // Ember.run.backburner.DEBUG = true; // NOTE: This slows your app, don't leave it on in production. + // + // Once that is in place, when you are at a breakpoint and navigate + // here in the stack explorer, you can look at `errorRecordedForStack.stack`, + // which will be the captured stack when this job was scheduled. + // + invoke(target, method, args, onError, errorRecordedForStack); } } - queue.push(target, method, args, stack); - return {queue: this, target: target, method: method}; - }, - - // TODO: remove me, only being used for Ember.run.sync - flush: function() { - var queue = this._queue, - globalOptions = this.globalOptions, - options = this.options, - before = options && options.before, - after = options && options.after, - onError = globalOptions.onError || (globalOptions.onErrorTarget && globalOptions.onErrorTarget[globalOptions.onErrorMethod]), - target, method, args, stack, i, l = queue.length; - - if (l && before) { before(); } - for (i = 0; i < l; i += 4) { - target = queue[i]; - method = queue[i+1]; - args = queue[i+2]; - stack = queue[i+3]; // Debugging assistance - - // TODO: error handling - if (args && args.length > 0) { - if (onError) { - try { - method.apply(target, args); - } catch (e) { - onError(e); - } - } else { - method.apply(target, args); - } - } else { - if (onError) { - try { - method.call(target); - } catch(e) { - onError(e); - } - } else { - method.call(target); - } - } + if (after) { + after(); } - if (l && after) { after(); } - // check if new items have been added - if (queue.length > l) { - this._queue = queue.slice(l); - this.flush(); - } else { - this._queue.length = 0; + this._queueBeingFlushed = undefined; + + if (sync !== false && + this._queue.length > 0) { + // check if new items have been added + this.flush(true); } }, cancel: function(actionToCancel) { var queue = this._queue, currentTarget, currentMethod, i, l; + var target = actionToCancel.target; + var method = actionToCancel.method; + var GUID_KEY = this.globalOptions.GUID_KEY; + + if (GUID_KEY && this.targetQueues && target) { + var targetQueue = this.targetQueues[target[GUID_KEY]]; + + if (targetQueue) { + for (i = 0, l = targetQueue.length; i < l; i++) { + if (targetQueue[i] === method) { + targetQueue.splice(i, 1); + } + } + } + } for (i = 0, l = queue.length; i < l; i += 4) { currentTarget = queue[i]; currentMethod = queue[i+1]; - if (currentTarget === actionToCancel.target && currentMethod === actionToCancel.method) { + if (currentTarget === target && + currentMethod === method) { queue.splice(i, 4); return true; } @@ -824,14 +959,17 @@ define("backburner/queue", // if not found in current queue // could be in the queue that is being flushed queue = this._queueBeingFlushed; + if (!queue) { return; } + for (i = 0, l = queue.length; i < l; i += 4) { currentTarget = queue[i]; currentMethod = queue[i+1]; - if (currentTarget === actionToCancel.target && currentMethod === actionToCancel.method) { + if (currentTarget === target && + currentMethod === method) { // don't mess with array during flush // just nullify the method queue[i+1] = null; @@ -841,44 +979,63 @@ define("backburner/queue", } }; - __exports__.Queue = Queue; + __exports__["default"] = Queue; }); -define("backburner/utils", +enifed("backburner/utils", ["exports"], function(__exports__) { "use strict"; - __exports__["default"] = { - each: function(collection, callback) { - for (var i = 0; i < collection.length; i++) { - callback(collection[i]); - } - }, + var NUMBER = /\d+/; - isString: function(suspect) { - return typeof suspect === 'string'; - }, - - isFunction: function(suspect) { - return typeof suspect === 'function'; - }, - - isNumber: function(suspect) { - return typeof suspect === 'number'; + function each(collection, callback) { + for (var i = 0; i < collection.length; i++) { + callback(collection[i]); } - }; - }); + } -define("calculateVersion", + __exports__.each = each;// Date.now is not available in browsers < IE9 + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now#Compatibility + var now = Date.now || function() { return new Date().getTime(); }; + __exports__.now = now; + function isString(suspect) { + return typeof suspect === 'string'; + } + + __exports__.isString = isString;function isFunction(suspect) { + return typeof suspect === 'function'; + } + + __exports__.isFunction = isFunction;function isNumber(suspect) { + return typeof suspect === 'number'; + } + + __exports__.isNumber = isNumber;function isCoercableNumber(number) { + return isNumber(number) || NUMBER.test(number); + } + + __exports__.isCoercableNumber = isCoercableNumber;function wrapInTryCatch(func) { + return function () { + try { + return func.apply(this, arguments); + } catch (e) { + throw e; + } + }; + } + + __exports__.wrapInTryCatch = wrapInTryCatch; + }); +enifed("calculateVersion", [], function() { "use strict"; 'use strict'; - var fs = require('fs'); - var path = require('path'); + var fs = eriuqer('fs'); + var path = eriuqer('path'); module.exports = function () { - var packageVersion = require('../package.json').version; + var packageVersion = eriuqer('../package.json').version; var output = [packageVersion]; var gitPath = path.join(__dirname,'..','.git'); var headFilePath = path.join(gitPath, 'HEAD'); @@ -909,7 +1066,7 @@ define("calculateVersion", } }; }); -define("container", +enifed("container", ["container/container","exports"], function(__dependency1__, __exports__) { "use strict"; @@ -936,13 +1093,14 @@ define("container", __exports__["default"] = Container; }); -define("container/container", - ["container/inheriting_dict","ember-metal/core","exports"], - function(__dependency1__, __dependency2__, __exports__) { +enifed("container/container", + ["ember-metal/core","ember-metal/keys","ember-metal/dictionary","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { "use strict"; - var InheritingDict = __dependency1__["default"]; - var Ember = __dependency2__["default"]; + var Ember = __dependency1__["default"]; // Ember.assert + var emberKeys = __dependency2__["default"]; + var dictionary = __dependency3__["default"]; // A lightweight container that helps to assemble and decouple components. // Public api for the container is still in flux. @@ -953,18 +1111,19 @@ define("container/container", this.resolver = parent && parent.resolver || function() {}; - this.registry = new InheritingDict(parent && parent.registry); - this.cache = new InheritingDict(parent && parent.cache); - this.factoryCache = new InheritingDict(parent && parent.factoryCache); - this.resolveCache = new InheritingDict(parent && parent.resolveCache); - this.typeInjections = new InheritingDict(parent && parent.typeInjections); - this.injections = {}; + this.registry = dictionary(parent ? parent.registry : null); + this.cache = dictionary(parent ? parent.cache : null); + this.factoryCache = dictionary(parent ? parent.factoryCache : null); + this.resolveCache = dictionary(parent ? parent.resolveCache : null); + this.typeInjections = dictionary(parent ? parent.typeInjections : null); + this.injections = dictionary(null); + this.normalizeCache = dictionary(null); - this.factoryTypeInjections = new InheritingDict(parent && parent.factoryTypeInjections); - this.factoryInjections = {}; + this.factoryTypeInjections = dictionary(parent ? parent.factoryTypeInjections : null); + this.factoryInjections = dictionary(null); - this._options = new InheritingDict(parent && parent._options); - this._typeOptions = new InheritingDict(parent && parent._typeOptions); + this._options = dictionary(parent ? parent._options : null); + this._typeOptions = dictionary(parent ? parent._typeOptions : null); } Container.prototype = { @@ -1044,20 +1203,6 @@ define("container/container", return container; }, - /** - Sets a key-value pair on the current container. If a parent container, - has the same key, once set on a child, the parent and child will diverge - as expected. - - @method set - @param {Object} object - @param {String} key - @param {any} value - */ - set: function(object, key, value) { - object[key] = value; - }, - /** Registers a factory for later injection. @@ -1084,12 +1229,12 @@ define("container/container", var normalizedName = this.normalize(fullName); - if (this.cache.has(normalizedName)) { + if (normalizedName in this.cache) { throw new Error('Cannot re-register: `' + fullName +'`, as it has already been looked up.'); } - this.registry.set(normalizedName, factory); - this._options.set(normalizedName, options || {}); + this.registry[normalizedName] = factory; + this._options[normalizedName] = (options || {}); }, /** @@ -1112,11 +1257,11 @@ define("container/container", var normalizedName = this.normalize(fullName); - this.registry.remove(normalizedName); - this.cache.remove(normalizedName); - this.factoryCache.remove(normalizedName); - this.resolveCache.remove(normalizedName); - this._options.remove(normalizedName); + delete this.registry[normalizedName]; + delete this.cache[normalizedName]; + delete this.factoryCache[normalizedName]; + delete this.resolveCache[normalizedName]; + delete this._options[normalizedName]; }, /** @@ -1174,12 +1319,25 @@ define("container/container", /** A hook to enable custom fullName normalization behaviour + @method normalizeFullName + @param {String} fullName + @return {string} normalized fullName + */ + normalizeFullName: function(fullName) { + return fullName; + }, + + /** + normalize a fullName based on the applications conventions + @method normalize @param {String} fullName @return {string} normalized fullName */ normalize: function(fullName) { - return fullName; + return this.normalizeCache[fullName] || ( + this.normalizeCache[fullName] = this.normalizeFullName(fullName) + ); }, /** @@ -1289,16 +1447,18 @@ define("container/container", optionsForType: function(type, options) { if (this.parent) { illegalChildOperation('optionsForType'); } - this._typeOptions.set(type, options); + this._typeOptions[type] = options; }, /** @method options - @param {String} type + @param {String} fullName @param {Object} options */ - options: function(type, options) { - this.optionsForType(type, options); + options: function(fullName, options) { + options = options || {}; + var normalizedName = this.normalize(fullName); + this._options[normalizedName] = options; }, /** @@ -1337,12 +1497,17 @@ define("container/container", @param {String} fullName */ typeInjection: function(type, property, fullName) { - if (this.parent) { illegalChildOperation('typeInjection'); } + + if (this.parent) { illegalChildOperation('typeInjection'); } var fullNameType = fullName.split(':')[0]; - if(fullNameType === type) { - throw new Error('Cannot inject a `' + fullName + '` on other ' + type + '(s). Register the `' + fullName + '` as a different type and perform the typeInjection.'); + if (fullNameType === type) { + throw new Error('Cannot inject a `' + fullName + + '` on other ' + type + + '(s). Register the `' + fullName + + '` as a different type and perform the typeInjection.'); } + addTypeInjection(this.typeInjections, type, property, fullName); }, @@ -1402,10 +1567,14 @@ define("container/container", var normalizedName = this.normalize(fullName); - if (this.cache.has(normalizedName)) { - throw new Error("Attempted to register an injection for a type that has already been looked up. ('" + normalizedName + "', '" + property + "', '" + injectionName + "')"); + if (this.cache[normalizedName]) { + throw new Error("Attempted to register an injection for a type that has already been looked up. ('" + + normalizedName + "', '" + + property + "', '" + + injectionName + "')"); } - addInjection(this.injections, normalizedName, property, normalizedInjectionName); + + addInjection(initRules(this.injections, normalizedName), property, normalizedInjectionName); }, @@ -1506,12 +1675,12 @@ define("container/container", } - if (this.factoryCache.has(normalizedName)) { + if (this.factoryCache[normalizedName]) { throw new Error('Attempted to register a factoryInjection for a type that has already ' + 'been looked up. (\'' + normalizedName + '\', \'' + property + '\', \'' + injectionName + '\')'); } - addInjection(this.factoryInjections, normalizedName, property, normalizedInjectionName); + addInjection(initRules(this.factoryInjections, normalizedName), property, normalizedInjectionName); }, /** @@ -1548,28 +1717,28 @@ define("container/container", }; function resolve(container, normalizedName) { - var cached = container.resolveCache.get(normalizedName); + var cached = container.resolveCache[normalizedName]; if (cached) { return cached; } - var resolved = container.resolver(normalizedName) || container.registry.get(normalizedName); - container.resolveCache.set(normalizedName, resolved); + var resolved = container.resolver(normalizedName) || container.registry[normalizedName]; + container.resolveCache[normalizedName] = resolved; return resolved; } function has(container, fullName){ - if (container.cache.has(fullName)) { + if (container.cache[fullName]) { return true; } - return !!container.resolve(fullName); + return container.resolve(fullName) !== undefined; } function lookup(container, fullName, options) { options = options || {}; - if (container.cache.has(fullName) && options.singleton !== false) { - return container.cache.get(fullName); + if (container.cache[fullName] && options.singleton !== false) { + return container.cache[fullName]; } var value = instantiate(container, fullName); @@ -1577,7 +1746,7 @@ define("container/container", if (value === undefined) { return; } if (isSingleton(container, fullName) && options.singleton !== false) { - container.cache.set(fullName, value); + container.cache[fullName] = value; } return value; @@ -1598,31 +1767,41 @@ define("container/container", if (!injections) { return hash; } - var injection, injectable; + validateInjections(container, injections); + + var injection; for (var i = 0, length = injections.length; i < length; i++) { injection = injections[i]; - injectable = lookup(container, injection.fullName); - - if (injectable !== undefined) { - hash[injection.property] = injectable; - } else { - throw new Error('Attempting to inject an unknown injection: `' + injection.fullName + '`'); - } + hash[injection.property] = lookup(container, injection.fullName); } return hash; } + function validateInjections(container, injections) { + if (!injections) { return; } + + var fullName; + + for (var i = 0, length = injections.length; i < length; i++) { + fullName = injections[i].fullName; + + if (!container.has(fullName)) { + throw new Error('Attempting to inject an unknown injection: `' + fullName + '`'); + } + } + } + function option(container, fullName, optionName) { - var options = container._options.get(fullName); + var options = container._options[fullName]; if (options && options[optionName] !== undefined) { return options[optionName]; } var type = fullName.split(':')[0]; - options = container._typeOptions.get(type); + options = container._typeOptions[type]; if (options) { return options[optionName]; @@ -1631,8 +1810,8 @@ define("container/container", function factoryFor(container, fullName) { var cache = container.factoryCache; - if (cache.has(fullName)) { - return cache.get(fullName); + if (cache[fullName]) { + return cache[fullName]; } var factory = container.resolve(fullName); if (factory === undefined) { return; } @@ -1641,6 +1820,7 @@ define("container/container", if (!factory || typeof factory.extend !== 'function' || (!Ember.MODEL_FACTORY_INJECTIONS && type === 'model')) { // TODO: think about a 'safe' merge style extension // for now just fallback to create time injection + cache[fullName] = factory; return factory; } else { var injections = injectionsFor(container, fullName); @@ -1651,18 +1831,18 @@ define("container/container", var injectedFactory = factory.extend(injections); injectedFactory.reopenClass(factoryInjections); - cache.set(fullName, injectedFactory); + cache[fullName] = injectedFactory; return injectedFactory; } } function injectionsFor(container, fullName) { - var splitName = fullName.split(':'), - type = splitName[0], - injections = []; + var splitName = fullName.split(':'); + var type = splitName[0]; + var injections = []; - injections = injections.concat(container.typeInjections.get(type) || []); + injections = injections.concat(container.typeInjections[type] || []); injections = injections.concat(container.injections[fullName] || []); injections = buildInjections(container, injections); @@ -1673,11 +1853,11 @@ define("container/container", } function factoryInjectionsFor(container, fullName) { - var splitName = fullName.split(':'), - type = splitName[0], - factoryInjections = []; + var splitName = fullName.split(':'); + var type = splitName[0]; + var factoryInjections = []; - factoryInjections = factoryInjections.concat(container.factoryTypeInjections.get(type) || []); + factoryInjections = factoryInjections.concat(container.factoryTypeInjections[type] || []); factoryInjections = factoryInjections.concat(container.factoryInjections[fullName] || []); factoryInjections = buildInjections(container, factoryInjections); @@ -1686,8 +1866,22 @@ define("container/container", return factoryInjections; } + function normalizeInjectionsHash(hash) { + var injections = []; + + for (var key in hash) { + if (hash.hasOwnProperty(key)) { + + addInjection(injections, key, hash[key]); + } + } + + return injections; + } + function instantiate(container, fullName) { var factory = factoryFor(container, fullName); + var lazyInjections; if (option(container, fullName, 'instantiate') === false) { return factory; @@ -1699,6 +1893,7 @@ define("container/container", 'Most likely an improperly defined class or an invalid module export.'); } + if (typeof factory.extend === 'function') { // assume the factory was extendable and is already injected return factory.create(); @@ -1712,26 +1907,34 @@ define("container/container", } function eachDestroyable(container, callback) { - container.cache.eachLocal(function(key, value) { - if (option(container, key, 'instantiate') === false) { return; } - callback(value); - }); + var cache = container.cache; + var keys = emberKeys(cache); + var key, value; + + for (var i = 0, l = keys.length; i < l; i++) { + key = keys[i]; + value = cache[key]; + + if (option(container, key, 'instantiate') !== false) { + callback(value); + } + } } function resetCache(container) { - container.cache.eachLocal(function(key, value) { - if (option(container, key, 'instantiate') === false) { return; } + eachDestroyable(container, function(value) { value.destroy(); }); - container.cache.dict = {}; + + container.cache.dict = dictionary(null); } function addTypeInjection(rules, type, property, fullName) { - var injections = rules.get(type); + var injections = rules[type]; if (!injections) { injections = []; - rules.set(type, injections); + rules[type] = injections; } injections.push({ @@ -1748,130 +1951,221 @@ define("container/container", return true; } - function addInjection(rules, factoryName, property, injectionName) { - var injections = rules[factoryName] = rules[factoryName] || []; - injections.push({ property: property, fullName: injectionName }); + function initRules(rules, factoryName) { + return rules[factoryName] || (rules[factoryName] = []); + } + + function addInjection(injections, property, injectionName) { + injections.push({ + property: property, + fullName: injectionName + }); } __exports__["default"] = Container; }); -define("container/inheriting_dict", +enifed("dag-map", ["exports"], function(__exports__) { "use strict"; - // A safe and simple inheriting object. - function InheritingDict(parent) { - this.parent = parent; - this.dict = {}; + function visit(vertex, fn, visited, path) { + var name = vertex.name; + var vertices = vertex.incoming; + var names = vertex.incomingNames; + var len = names.length; + var i; + + if (!visited) { + visited = {}; + } + if (!path) { + path = []; + } + if (visited.hasOwnProperty(name)) { + return; + } + path.push(name); + visited[name] = true; + for (i = 0; i < len; i++) { + visit(vertices[names[i]], fn, visited, path); + } + fn(vertex, path); + path.pop(); } - InheritingDict.prototype = { - /** - @property parent - @type InheritingDict - @default null - */ + /** + * DAG stands for Directed acyclic graph. + * + * It is used to build a graph of dependencies checking that there isn't circular + * dependencies. p.e Registering initializers with a certain precedence order. + * + * @class DAG + * @constructor + */ + function DAG() { + this.names = []; + this.vertices = Object.create(null); + } - parent: null, + /** + * DAG Vertex + * + * @class Vertex + * @constructor + */ - /** - Object used to store the current nodes data. + function Vertex(name) { + this.name = name; + this.incoming = {}; + this.incomingNames = []; + this.hasOutgoing = false; + this.value = null; + } - @property dict - @type Object - @default Object - */ - dict: null, + /** + * Adds a vertex entry to the graph unless it is already added. + * + * @private + * @method add + * @param {String} name The name of the vertex to add + */ + DAG.prototype.add = function(name) { + if (!name) { + throw new Error("Can't add Vertex without name"); + } + if (this.vertices[name] !== undefined) { + return this.vertices[name]; + } + var vertex = new Vertex(name); + this.vertices[name] = vertex; + this.names.push(name); + return vertex; + }; - /** - Retrieve the value given a key, if the value is present at the current - level use it, otherwise walk up the parent hierarchy and try again. If - no matching key is found, return undefined. + /** + * Adds a vertex to the graph and sets its value. + * + * @private + * @method map + * @param {String} name The name of the vertex. + * @param value The value to put in the vertex. + */ + DAG.prototype.map = function(name, value) { + this.add(name).value = value; + }; - @method get - @param {String} key - @return {any} - */ - get: function(key) { - var dict = this.dict; - - if (dict.hasOwnProperty(key)) { - return dict[key]; + /** + * Connects the vertices with the given names, adding them to the graph if + * necessary, only if this does not produce is any circular dependency. + * + * @private + * @method addEdge + * @param {String} fromName The name the vertex where the edge starts. + * @param {String} toName The name the vertex where the edge ends. + */ + DAG.prototype.addEdge = function(fromName, toName) { + if (!fromName || !toName || fromName === toName) { + return; + } + var from = this.add(fromName); + var to = this.add(toName); + if (to.incoming.hasOwnProperty(fromName)) { + return; + } + function checkCycle(vertex, path) { + if (vertex.name === toName) { + throw new Error("cycle detected: " + toName + " <- " + path.join(" <- ")); } + } + visit(from, checkCycle); + from.hasOutgoing = true; + to.incoming[fromName] = from; + to.incomingNames.push(fromName); + }; - if (this.parent) { - return this.parent.get(key); + /** + * Visits all the vertex of the graph calling the given function with each one, + * ensuring that the vertices are visited respecting their precedence. + * + * @method topsort + * @param {Function} fn The function to be invoked on each vertex. + */ + DAG.prototype.topsort = function(fn) { + var visited = {}; + var vertices = this.vertices; + var names = this.names; + var len = names.length; + var i, vertex; + + for (i = 0; i < len; i++) { + vertex = vertices[names[i]]; + if (!vertex.hasOutgoing) { + visit(vertex, fn, visited); } - }, + } + }; - /** - Set the given value for the given key, at the current level. - - @method set - @param {String} key - @param {Any} value - */ - set: function(key, value) { - this.dict[key] = value; - }, - - /** - Delete the given key - - @method remove - @param {String} key - */ - remove: function(key) { - delete this.dict[key]; - }, - - /** - Check for the existence of given a key, if the key is present at the current - level return true, otherwise walk up the parent hierarchy and try again. If - no matching key is found, return false. - - @method has - @param {String} key - @return {Boolean} - */ - has: function(key) { - var dict = this.dict; - - if (dict.hasOwnProperty(key)) { - return true; + /** + * Adds a vertex with the given name and value to the graph and joins it with the + * vertices referenced in _before_ and _after_. If there isn't vertices with those + * names, they are added too. + * + * If either _before_ or _after_ are falsy/empty, the added vertex will not have + * an incoming/outgoing edge. + * + * @method addEdges + * @param {String} name The name of the vertex to be added. + * @param value The value of that vertex. + * @param before An string or array of strings with the names of the vertices before + * which this vertex must be visited. + * @param after An string or array of strings with the names of the vertex after + * which this vertex must be visited. + * + */ + DAG.prototype.addEdges = function(name, value, before, after) { + var i; + this.map(name, value); + if (before) { + if (typeof before === 'string') { + this.addEdge(name, before); + } else { + for (i = 0; i < before.length; i++) { + this.addEdge(name, before[i]); + } } - - if (this.parent) { - return this.parent.has(key); - } - - return false; - }, - - /** - Iterate and invoke a callback for each local key-value pair. - - @method eachLocal - @param {Function} callback - @param {Object} binding - */ - eachLocal: function(callback, binding) { - var dict = this.dict; - - for (var prop in dict) { - if (dict.hasOwnProperty(prop)) { - callback.call(binding, prop, dict[prop]); + } + if (after) { + if (typeof after === 'string') { + this.addEdge(after, name); + } else { + for (i = 0; i < after.length; i++) { + this.addEdge(after[i], name); } } } }; - __exports__["default"] = InheritingDict; + __exports__["default"] = DAG; }); -define("ember-application", - ["ember-metal/core","ember-runtime/system/lazy_load","ember-application/system/dag","ember-application/system/resolver","ember-application/system/application","ember-application/ext/controller"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__) { +enifed("dag-map.umd", + ["./dag-map"], + function(__dependency1__) { + "use strict"; + var DAG = __dependency1__["default"]; + + /* global define:true module:true window: true */ + if (typeof enifed === 'function' && enifed.amd) { + enifed(function() { return DAG; }); + } else if (typeof module !== 'undefined' && module.exports) { + module.exports = DAG; + } else if (typeof this !== 'undefined') { + this['DAG'] = DAG; + } + }); +enifed("ember-application", + ["ember-metal/core","ember-runtime/system/lazy_load","ember-application/system/resolver","ember-application/system/application","ember-application/ext/controller"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) { "use strict"; var Ember = __dependency1__["default"]; var runLoadHooks = __dependency2__.runLoadHooks; @@ -1884,22 +2178,20 @@ define("ember-application", @requires ember-views, ember-routing */ - var DAG = __dependency3__["default"]; - var Resolver = __dependency4__.Resolver; - var DefaultResolver = __dependency4__["default"]; - var Application = __dependency5__["default"]; + var Resolver = __dependency3__.Resolver; + var DefaultResolver = __dependency3__["default"]; + var Application = __dependency4__["default"]; // side effect of extending ControllerMixin Ember.Application = Application; - Ember.DAG = DAG; Ember.Resolver = Resolver; Ember.DefaultResolver = DefaultResolver; runLoadHooks('Ember.Application', Application); }); -define("ember-application/ext/controller", - ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/error","ember-metal/utils","ember-metal/computed","ember-runtime/mixins/controller","ember-routing/system/controller_for","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) { +enifed("ember-application/ext/controller", + ["ember-metal/core","ember-metal/property_get","ember-metal/error","ember-metal/utils","ember-metal/computed","ember-runtime/mixins/controller","ember-routing/system/controller_for","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) { "use strict"; /** @module ember @@ -1909,16 +2201,16 @@ define("ember-application/ext/controller", var Ember = __dependency1__["default"]; // Ember.assert var get = __dependency2__.get; - var set = __dependency3__.set; - var EmberError = __dependency4__["default"]; - var inspect = __dependency5__.inspect; - var computed = __dependency6__.computed; - var ControllerMixin = __dependency7__["default"]; - var meta = __dependency5__.meta; - var controllerFor = __dependency8__["default"]; + var EmberError = __dependency3__["default"]; + var inspect = __dependency4__.inspect; + var computed = __dependency5__.computed; + var ControllerMixin = __dependency6__["default"]; + var meta = __dependency4__.meta; + var controllerFor = __dependency7__["default"]; function verifyNeedsDependencies(controller, container, needs) { - var dependency, i, l, missing = []; + var dependency, i, l; + var missing = []; for (i=0, l=needs.length; i 1 ? 'they' : 'it') + " could not be found"); + throw new EmberError(inspect(controller) + " needs [ " + missing.join(', ') + + " ] but " + (missing.length > 1 ? 'they' : 'it') + " could not be found"); } } @@ -1945,8 +2238,9 @@ define("ember-application/ext/controller", needs: get(controller, 'needs'), container: get(controller, 'container'), unknownProperty: function(controllerName) { - var needs = this.needs, - dependency, i, l; + var needs = this.needs; + var dependency, i, l; + for (i=0, l=needs.length; i Ember.TEMPLATES['post'] - 'template:posts/byline' //=> Ember.TEMPLATES['posts/byline'] - 'template:posts.byline' //=> Ember.TEMPLATES['posts/byline'] - 'template:blogPost' //=> Ember.TEMPLATES['blogPost'] - // OR - // Ember.TEMPLATES['blog_post'] - 'controller:post' //=> App.PostController - 'controller:posts.index' //=> App.PostsIndexController - 'controller:blog/post' //=> Blog.PostController - 'controller:basic' //=> Ember.Controller - 'route:post' //=> App.PostRoute - 'route:posts.index' //=> App.PostsIndexRoute - 'route:blog/post' //=> Blog.PostRoute - 'route:basic' //=> Ember.Route - 'view:post' //=> App.PostView - 'view:posts.index' //=> App.PostsIndexView - 'view:blog/post' //=> Blog.PostView - 'view:basic' //=> Ember.View - 'foo:post' //=> App.PostFoo - 'model:post' //=> App.Post + 'template:post' //=> Ember.TEMPLATES['post'] + 'template:posts/byline' //=> Ember.TEMPLATES['posts/byline'] + 'template:posts.byline' //=> Ember.TEMPLATES['posts/byline'] + 'template:blogPost' //=> Ember.TEMPLATES['blogPost'] + // OR + // Ember.TEMPLATES['blog_post'] + 'controller:post' //=> App.PostController + 'controller:posts.index' //=> App.PostsIndexController + 'controller:blog/post' //=> Blog.PostController + 'controller:basic' //=> Ember.Controller + 'route:post' //=> App.PostRoute + 'route:posts.index' //=> App.PostsIndexRoute + 'route:blog/post' //=> Blog.PostRoute + 'route:basic' //=> Ember.Route + 'view:post' //=> App.PostView + 'view:posts.index' //=> App.PostsIndexView + 'view:blog/post' //=> Blog.PostView + 'view:basic' //=> Ember.View + 'foo:post' //=> App.PostFoo + 'model:post' //=> App.Post ``` @class DefaultResolver @namespace Ember @extends Ember.Object */ + var dictionary = __dependency8__["default"]; __exports__["default"] = EmberObject.extend({ /** @@ -3396,21 +3550,28 @@ define("ember-application/system/resolver", */ namespace: null, + init: function() { + this._parseNameCache = dictionary(null); + }, normalize: function(fullName) { - var split = fullName.split(':', 2), - type = split[0], - name = split[1]; + var split = fullName.split(':', 2); + var type = split[0]; + var name = split[1]; if (type !== 'template') { var result = name; if (result.indexOf('.') > -1) { - result = result.replace(/\.(.)/g, function(m) { return m.charAt(1).toUpperCase(); }); + result = result.replace(/\.(.)/g, function(m) { + return m.charAt(1).toUpperCase(); + }); } if (name.indexOf('_') > -1) { - result = result.replace(/_(.)/g, function(m) { return m.charAt(1).toUpperCase(); }); + result = result.replace(/_(.)/g, function(m) { + return m.charAt(1).toUpperCase(); + }); } return type + ':' + result; @@ -3430,9 +3591,9 @@ define("ember-application/system/resolver", @return {Object} the resolved factory */ resolve: function(fullName) { - var parsedName = this.parseName(fullName), - resolveMethodName = parsedName.resolveMethodName, - resolved; + var parsedName = this.parseName(fullName); + var resolveMethodName = parsedName.resolveMethodName; + var resolved; if (!(parsedName.name && parsedName.type)) { throw new TypeError('Invalid fullName: `' + fullName + '`, must be of the form `type:name` '); @@ -3461,12 +3622,19 @@ define("ember-application/system/resolver", @param {String} fullName the lookup string @method parseName */ + parseName: function(fullName) { - var nameParts = fullName.split(':'), - type = nameParts[0], fullNameWithoutType = nameParts[1], - name = fullNameWithoutType, - namespace = get(this, 'namespace'), - root = namespace; + return this._parseNameCache[fullName] || ( + this._parseNameCache[fullName] = this._parseName(fullName) + ); + }, + + _parseName: function(fullName) { + var nameParts = fullName.split(':'); + var type = nameParts[0], fullNameWithoutType = nameParts[1]; + var name = fullNameWithoutType; + var namespace = get(this, 'namespace'); + var root = namespace; if (type !== 'template' && name.indexOf('/') !== -1) { var parts = name.split('/'); @@ -3504,7 +3672,10 @@ define("ember-application/system/resolver", } var description = parsedName.root + '.' + classify(parsedName.name); - if (parsedName.type !== 'model') { description += classify(parsedName.type); } + + if (parsedName.type !== 'model') { + description += classify(parsedName.type); + } return description; }, @@ -3547,6 +3718,7 @@ define("ember-application/system/resolver", return Ember.TEMPLATES[templateName]; } }, + /** Lookup the view using `resolveOther` @@ -3559,6 +3731,7 @@ define("ember-application/system/resolver", this.useRouterNaming(parsedName); return this.resolveOther(parsedName); }, + /** Lookup the controller using `resolveOther` @@ -3647,7 +3820,7 @@ define("ember-application/system/resolver", } }); }); -define("ember-extension-support", +enifed("ember-extension-support", ["ember-metal/core","ember-extension-support/data_adapter","ember-extension-support/container_debug_adapter"], function(__dependency1__, __dependency2__, __dependency3__) { "use strict"; @@ -3666,7 +3839,7 @@ define("ember-extension-support", Ember.DataAdapter = DataAdapter; Ember.ContainerDebugAdapter = ContainerDebugAdapter; }); -define("ember-extension-support/container_debug_adapter", +enifed("ember-extension-support/container_debug_adapter", ["ember-metal/core","ember-runtime/system/native_array","ember-metal/utils","ember-runtime/system/string","ember-runtime/system/namespace","ember-runtime/system/object","exports"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) { "use strict"; @@ -3743,7 +3916,7 @@ define("ember-extension-support/container_debug_adapter", classes in the resolver for a given type. @method canCatalogEntriesByType - @param {string} type The type. e.g. "model", "controller", "route" + @param {String} type The type. e.g. "model", "controller", "route" @return {boolean} whether a list is available for this type. */ canCatalogEntriesByType: function(type) { @@ -3755,11 +3928,11 @@ define("ember-extension-support/container_debug_adapter", Returns the available classes a given type. @method catalogEntriesByType - @param {string} type The type. e.g. "model", "controller", "route" + @param {String} type The type. e.g. "model", "controller", "route" @return {Array} An array of strings. */ catalogEntriesByType: function(type) { - var namespaces = emberA(Namespace.NAMESPACES), types = emberA(), self = this; + var namespaces = emberA(Namespace.NAMESPACES), types = emberA(); var typeSuffixRegex = new RegExp(classify(type) + "$"); namespaces.forEach(function(namespace) { @@ -3779,7 +3952,7 @@ define("ember-extension-support/container_debug_adapter", } }); }); -define("ember-extension-support/data_adapter", +enifed("ember-extension-support/data_adapter", ["ember-metal/core","ember-metal/property_get","ember-metal/run_loop","ember-runtime/system/string","ember-runtime/system/namespace","ember-runtime/system/object","ember-runtime/system/native_array","ember-application/system/application","exports"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) { "use strict"; @@ -3917,8 +4090,10 @@ define("ember-extension-support/data_adapter", @return {Function} Method to call to remove all observers */ watchModelTypes: function(typesAdded, typesUpdated) { - var modelTypes = this.getModelTypes(), - self = this, typesToSend, releaseMethods = emberA(); + var modelTypes = this.getModelTypes(); + var self = this; + var releaseMethods = emberA(); + var typesToSend; typesToSend = modelTypes.map(function(type) { var klass = type.klass; @@ -4059,7 +4234,8 @@ define("ember-extension-support/data_adapter", */ observeModelType: function(type, typesUpdated) { - var self = this, records = this.getRecords(type); + var self = this; + var records = this.getRecords(type); var onChange = function() { typesUpdated([self.wrapModelType(type)]); @@ -4099,8 +4275,8 @@ define("ember-extension-support/data_adapter", release: {Function} The function to remove observers */ wrapModelType: function(type, name) { - var release, records = this.getRecords(type), - typeToSend, self = this; + var records = this.getRecords(type); + var typeToSend; typeToSend = { name: name || type.toString(), @@ -4122,8 +4298,9 @@ define("ember-extension-support/data_adapter", @return {Array} Array of model types */ getModelTypes: function() { - var types, self = this, - containerDebugAdapter = this.get('containerDebugAdapter'); + var self = this; + var containerDebugAdapter = this.get('containerDebugAdapter'); + var types; if (containerDebugAdapter.canCatalogEntriesByType('model')) { types = containerDebugAdapter.catalogEntriesByType('model'); @@ -4154,9 +4331,9 @@ define("ember-extension-support/data_adapter", @return {Array} Array of model type strings */ _getObjectsOnNamespaces: function() { - var namespaces = emberA(Namespace.NAMESPACES), - types = emberA(), - self = this; + var namespaces = emberA(Namespace.NAMESPACES); + var types = emberA(); + var self = this; namespaces.forEach(function(namespace) { for (var key in namespace) { @@ -4199,7 +4376,7 @@ define("ember-extension-support/data_adapter", searchKeywords: {Array} */ wrapRecord: function(record) { - var recordToSend = { object: record }, columnValues = {}, self = this; + var recordToSend = { object: record }; recordToSend.columnValues = this.getRecordColumnValues(record); recordToSend.searchKeywords = this.getRecordKeywords(record); @@ -4272,17 +4449,21 @@ define("ember-extension-support/data_adapter", } }); }); -define("ember-extension-support/initializers", +enifed("ember-extension-support/initializers", [], function() { "use strict"; }); -define("ember-handlebars-compiler", +enifed("ember-handlebars-compiler", ["ember-metal/core","exports"], function(__dependency1__, __exports__) { - "use strict"; - /* global Handlebars:true */ + /* global Handlebars:true */ + + // Remove "use strict"; from transpiled module (in browser builds only) until + // https://bugs.webkit.org/show_bug.cgi?id=138038 is fixed + // + // REMOVE_USE_STRICT: true /** @module ember @@ -4307,8 +4488,8 @@ define("ember-handlebars-compiler", // ES6Todo: when ember-debug is es6'ed import this. // var emberAssert = Ember.assert; var Handlebars = (Ember.imports && Ember.imports.Handlebars) || (this && this.Handlebars); - if (!Handlebars && typeof require === 'function') { - Handlebars = require('handlebars'); + if (!Handlebars && typeof eriuqer === 'function') { + Handlebars = eriuqer('handlebars'); } @@ -4327,7 +4508,7 @@ define("ember-handlebars-compiler", @class Handlebars @namespace Ember */ - var EmberHandlebars = Ember.Handlebars = objectCreate(Handlebars); + var EmberHandlebars = Ember.Handlebars = Handlebars.create(); /** Register a bound helper or custom view helper. @@ -4370,7 +4551,7 @@ define("ember-handlebars-compiler", Which is functionally equivalent to: ```handlebars - {{view App.CalendarView}} + {{view 'calendar'}} ``` Options in the helper will be passed to the view in exactly the same @@ -4469,44 +4650,6 @@ define("ember-handlebars-compiler", return "data.buffer.push("+string+");"; }; - // Hacks ahead: - // Handlebars presently has a bug where the `blockHelperMissing` hook - // doesn't get passed the name of the missing helper name, but rather - // gets passed the value of that missing helper evaluated on the current - // context, which is most likely `undefined` and totally useless. - // - // So we alter the compiled template function to pass the name of the helper - // instead, as expected. - // - // This can go away once the following is closed: - // https://github.com/wycats/handlebars.js/issues/634 - - var DOT_LOOKUP_REGEX = /helpers\.(.*?)\)/, - BRACKET_STRING_LOOKUP_REGEX = /helpers\['(.*?)'/, - INVOCATION_SPLITTING_REGEX = /(.*blockHelperMissing\.call\(.*)(stack[0-9]+)(,.*)/; - - EmberHandlebars.JavaScriptCompiler.stringifyLastBlockHelperMissingInvocation = function(source) { - var helperInvocation = source[source.length - 1], - helperName = (DOT_LOOKUP_REGEX.exec(helperInvocation) || BRACKET_STRING_LOOKUP_REGEX.exec(helperInvocation))[1], - matches = INVOCATION_SPLITTING_REGEX.exec(helperInvocation); - - source[source.length - 1] = matches[1] + "'" + helperName + "'" + matches[3]; - }; - - var stringifyBlockHelperMissing = EmberHandlebars.JavaScriptCompiler.stringifyLastBlockHelperMissingInvocation; - - var originalBlockValue = EmberHandlebars.JavaScriptCompiler.prototype.blockValue; - EmberHandlebars.JavaScriptCompiler.prototype.blockValue = function() { - originalBlockValue.apply(this, arguments); - stringifyBlockHelperMissing(this.source); - }; - - var originalAmbiguousBlockValue = EmberHandlebars.JavaScriptCompiler.prototype.ambiguousBlockValue; - EmberHandlebars.JavaScriptCompiler.prototype.ambiguousBlockValue = function() { - originalAmbiguousBlockValue.apply(this, arguments); - stringifyBlockHelperMissing(this.source); - }; - /** Rewrite simple mustaches from `{{foo}}` to `{{bind "foo"}}`. This means that all simple mustaches in Ember's Handlebars will also set up an observer to @@ -4541,12 +4684,12 @@ define("ember-handlebars-compiler", @method precompile @for Ember.Handlebars @static - @param {String} string The template to precompile + @param {String|Object} value The template to precompile or an Handlebars AST @param {Boolean} asObject optional parameter, defaulting to true, of whether or not the compiled template should be returned as an Object or a String */ - EmberHandlebars.precompile = function(string, asObject) { - var ast = Handlebars.parse(string); + EmberHandlebars.precompile = function(value, asObject) { + var ast = Handlebars.parse(value); var options = { knownHelpers: { @@ -4595,9 +4738,9 @@ define("ember-handlebars-compiler", __exports__["default"] = EmberHandlebars; }); -define("ember-handlebars", - ["ember-handlebars-compiler","ember-metal/core","ember-runtime/system/lazy_load","ember-handlebars/loader","ember-handlebars/ext","ember-handlebars/string","ember-handlebars/helpers/shared","ember-handlebars/helpers/binding","ember-handlebars/helpers/collection","ember-handlebars/helpers/view","ember-handlebars/helpers/unbound","ember-handlebars/helpers/debug","ember-handlebars/helpers/each","ember-handlebars/helpers/template","ember-handlebars/helpers/partial","ember-handlebars/helpers/yield","ember-handlebars/helpers/loc","ember-handlebars/controls/checkbox","ember-handlebars/controls/select","ember-handlebars/controls/text_area","ember-handlebars/controls/text_field","ember-handlebars/controls/text_support","ember-handlebars/controls","ember-handlebars/component_lookup","ember-handlebars/views/handlebars_bound_view","ember-handlebars/views/metamorph_view","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __dependency18__, __dependency19__, __dependency20__, __dependency21__, __dependency22__, __dependency23__, __dependency24__, __dependency25__, __dependency26__, __exports__) { +enifed("ember-handlebars", + ["ember-handlebars-compiler","ember-metal/core","ember-runtime/system/lazy_load","ember-handlebars/loader","ember-handlebars/ext","ember-handlebars/string","ember-handlebars/helpers/binding","ember-handlebars/helpers/if_unless","ember-handlebars/helpers/with","ember-handlebars/helpers/bind_attr","ember-handlebars/helpers/collection","ember-handlebars/helpers/view","ember-handlebars/helpers/unbound","ember-handlebars/helpers/debug","ember-handlebars/helpers/each","ember-handlebars/helpers/template","ember-handlebars/helpers/partial","ember-handlebars/helpers/yield","ember-handlebars/helpers/loc","ember-handlebars/controls/checkbox","ember-handlebars/controls/select","ember-handlebars/controls/text_area","ember-handlebars/controls/text_field","ember-handlebars/controls/text_support","ember-handlebars/controls","ember-handlebars/component_lookup","ember-handlebars/views/handlebars_bound_view","ember-handlebars/views/metamorph_view","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __dependency18__, __dependency19__, __dependency20__, __dependency21__, __dependency22__, __dependency23__, __dependency24__, __dependency25__, __dependency26__, __dependency27__, __dependency28__, __exports__) { "use strict"; var EmberHandlebars = __dependency1__["default"]; var Ember = __dependency2__["default"]; @@ -4606,68 +4749,61 @@ define("ember-handlebars", var runLoadHooks = __dependency3__.runLoadHooks; var bootstrap = __dependency4__["default"]; - var normalizePath = __dependency5__.normalizePath; - var template = __dependency5__.template; var makeBoundHelper = __dependency5__.makeBoundHelper; var registerBoundHelper = __dependency5__.registerBoundHelper; - var resolveHash = __dependency5__.resolveHash; - var resolveParams = __dependency5__.resolveParams; - var getEscaped = __dependency5__.getEscaped; - var handlebarsGet = __dependency5__.handlebarsGet; - var evaluateUnboundHelper = __dependency5__.evaluateUnboundHelper; var helperMissingHelper = __dependency5__.helperMissingHelper; var blockHelperMissingHelper = __dependency5__.blockHelperMissingHelper; + var handlebarsGet = __dependency5__.handlebarsGet; // side effect of extending StringUtils of htmlSafe - var resolvePaths = __dependency7__["default"]; - var bind = __dependency8__.bind; - var _triageMustacheHelper = __dependency8__._triageMustacheHelper; - var resolveHelper = __dependency8__.resolveHelper; - var bindHelper = __dependency8__.bindHelper; + var bind = __dependency7__.bind; + var _triageMustacheHelper = __dependency7__._triageMustacheHelper; + var resolveHelper = __dependency7__.resolveHelper; + var bindHelper = __dependency7__.bindHelper; + + var ifHelper = __dependency8__.ifHelper; var boundIfHelper = __dependency8__.boundIfHelper; var unboundIfHelper = __dependency8__.unboundIfHelper; - var withHelper = __dependency8__.withHelper; - var ifHelper = __dependency8__.ifHelper; var unlessHelper = __dependency8__.unlessHelper; - var bindAttrHelper = __dependency8__.bindAttrHelper; - var bindAttrHelperDeprecated = __dependency8__.bindAttrHelperDeprecated; - var bindClasses = __dependency8__.bindClasses; - var collectionHelper = __dependency9__["default"]; - var ViewHelper = __dependency10__.ViewHelper; - var viewHelper = __dependency10__.viewHelper; - var unboundHelper = __dependency11__["default"]; - var logHelper = __dependency12__.logHelper; - var debuggerHelper = __dependency12__.debuggerHelper; - var EachView = __dependency13__.EachView; - var GroupedEach = __dependency13__.GroupedEach; - var eachHelper = __dependency13__.eachHelper; - var templateHelper = __dependency14__["default"]; - var partialHelper = __dependency15__["default"]; - var yieldHelper = __dependency16__["default"]; - var locHelper = __dependency17__["default"]; + var withHelper = __dependency9__["default"]; + + var bindAttrHelper = __dependency10__.bindAttrHelper; + var bindAttrHelperDeprecated = __dependency10__.bindAttrHelperDeprecated; + var bindClasses = __dependency10__.bindClasses; + + var collectionHelper = __dependency11__["default"]; + var ViewHelper = __dependency12__.ViewHelper; + var viewHelper = __dependency12__.viewHelper; + var unboundHelper = __dependency13__["default"]; + var logHelper = __dependency14__.logHelper; + var debuggerHelper = __dependency14__.debuggerHelper; + var EachView = __dependency15__.EachView; + var eachHelper = __dependency15__.eachHelper; + var templateHelper = __dependency16__["default"]; + var partialHelper = __dependency17__["default"]; + var yieldHelper = __dependency18__["default"]; + var locHelper = __dependency19__["default"]; - var Checkbox = __dependency18__["default"]; - var Select = __dependency19__.Select; - var SelectOption = __dependency19__.SelectOption; - var SelectOptgroup = __dependency19__.SelectOptgroup; - var TextArea = __dependency20__["default"]; - var TextField = __dependency21__["default"]; - var TextSupport = __dependency22__["default"]; - var inputHelper = __dependency23__.inputHelper; - var textareaHelper = __dependency23__.textareaHelper; + var Checkbox = __dependency20__["default"]; + var Select = __dependency21__.Select; + var SelectOption = __dependency21__.SelectOption; + var SelectOptgroup = __dependency21__.SelectOptgroup; + var TextArea = __dependency22__["default"]; + var TextField = __dependency23__["default"]; + var TextSupport = __dependency24__["default"]; + var inputHelper = __dependency25__.inputHelper; + var textareaHelper = __dependency25__.textareaHelper; - - var ComponentLookup = __dependency24__["default"]; - var _HandlebarsBoundView = __dependency25__._HandlebarsBoundView; - var SimpleHandlebarsView = __dependency25__.SimpleHandlebarsView; - var _wrapMap = __dependency26__._wrapMap; - var _SimpleMetamorphView = __dependency26__._SimpleMetamorphView; - var _MetamorphView = __dependency26__._MetamorphView; - var _Metamorph = __dependency26__._Metamorph; + var ComponentLookup = __dependency26__["default"]; + var _HandlebarsBoundView = __dependency27__._HandlebarsBoundView; + var SimpleHandlebarsView = __dependency27__.SimpleHandlebarsView; + var _MetamorphView = __dependency28__["default"]; + var _SimpleMetamorphView = __dependency28__._SimpleMetamorphView; + var _Metamorph = __dependency28__._Metamorph; /** @@ -4680,33 +4816,24 @@ define("ember-handlebars", // Ember.Handlebars.Globals EmberHandlebars.bootstrap = bootstrap; - EmberHandlebars.template = template; EmberHandlebars.makeBoundHelper = makeBoundHelper; EmberHandlebars.registerBoundHelper = registerBoundHelper; - EmberHandlebars.resolveHash = resolveHash; - EmberHandlebars.resolveParams = resolveParams; EmberHandlebars.resolveHelper = resolveHelper; - EmberHandlebars.get = handlebarsGet; - EmberHandlebars.getEscaped = getEscaped; - EmberHandlebars.evaluateUnboundHelper = evaluateUnboundHelper; EmberHandlebars.bind = bind; EmberHandlebars.bindClasses = bindClasses; EmberHandlebars.EachView = EachView; - EmberHandlebars.GroupedEach = GroupedEach; - EmberHandlebars.resolvePaths = resolvePaths; EmberHandlebars.ViewHelper = ViewHelper; - EmberHandlebars.normalizePath = normalizePath; // Ember Globals Ember.Handlebars = EmberHandlebars; + EmberHandlebars.get = handlebarsGet; Ember.ComponentLookup = ComponentLookup; Ember._SimpleHandlebarsView = SimpleHandlebarsView; Ember._HandlebarsBoundView = _HandlebarsBoundView; Ember._SimpleMetamorphView = _SimpleMetamorphView; Ember._MetamorphView = _MetamorphView; Ember._Metamorph = _Metamorph; - Ember._metamorphWrapMap = _wrapMap; Ember.TextSupport = TextSupport; Ember.Checkbox = Checkbox; Ember.Select = Select; @@ -4746,20 +4873,20 @@ define("ember-handlebars", __exports__["default"] = EmberHandlebars; }); -define("ember-handlebars/component_lookup", +enifed("ember-handlebars/component_lookup", ["ember-runtime/system/object","exports"], function(__dependency1__, __exports__) { "use strict"; var EmberObject = __dependency1__["default"]; - var ComponentLookup = EmberObject.extend({ + __exports__["default"] = EmberObject.extend({ lookupFactory: function(name, container) { container = container || this.container; - var fullName = 'component:' + name, - templateFullName = 'template:components/' + name, - templateRegistered = container && container.has(templateFullName); + var fullName = 'component:' + name; + var templateFullName = 'template:components/' + name; + var templateRegistered = container && container.has(templateFullName); if (templateRegistered) { container.injection(fullName, 'layout', templateFullName); @@ -4778,12 +4905,10 @@ define("ember-handlebars/component_lookup", } } }); - - __exports__["default"] = ComponentLookup; }); -define("ember-handlebars/controls", - ["ember-handlebars/controls/checkbox","ember-handlebars/controls/text_field","ember-handlebars/controls/text_area","ember-metal/core","ember-handlebars-compiler","ember-handlebars/ext","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) { +enifed("ember-handlebars/controls", + ["ember-handlebars/controls/checkbox","ember-handlebars/controls/text_field","ember-handlebars/controls/text_area","ember-metal/core","ember-handlebars-compiler","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { "use strict"; var Checkbox = __dependency1__["default"]; var TextField = __dependency2__["default"]; @@ -4794,21 +4919,12 @@ define("ember-handlebars/controls", // var emberAssert = Ember.assert; var EmberHandlebars = __dependency5__["default"]; - var handlebarsGet = __dependency6__.handlebarsGet; - var helpers = EmberHandlebars.helpers; + /** @module ember @submodule ember-handlebars-compiler */ - function _resolveOption(context, options, key) { - if (options.hashTypes[key] === "ID") { - return handlebarsGet(context, options.hash[key], options); - } else { - return options.hash[key]; - } - } - /** The `{{input}}` helper inserts an HTML `` tag into the template, @@ -4872,13 +4988,15 @@ define("ember-handlebars/controls", The helper can send multiple actions based on user events. - The action property defines the action which is send when + The action property defines the action which is sent when the user presses the return key. + ```handlebars {{input action="submit"}} ``` + The helper allows some user events to send actions. * `enter` @@ -4888,13 +5006,16 @@ define("ember-handlebars/controls", * `focus-out` * `key-press` + For example, if you desire an action to be sent when the input is blurred, you only need to setup the action name to the event name property. + ```handlebars {{input focus-in="alertMessage"}} ``` + See more about [Text Support Actions](/api/classes/Ember.TextField.html) ## Extension @@ -4986,22 +5107,29 @@ define("ember-handlebars/controls", */ function inputHelper(options) { - var hash = options.hash, - types = options.hashTypes, - inputType = _resolveOption(this, options, 'type'), - onEvent = hash.on; + var view = options.data.view; + var hash = options.hash; + var types = options.hashTypes; + var onEvent = hash.on; + var inputType; + + if (types.type === 'ID') { + inputType = view.getStream(hash.type).value(); + } else { + inputType = hash.type; + } if (inputType === 'checkbox') { delete hash.type; delete types.type; - return helpers.view.call(this, Checkbox, options); + return EmberHandlebars.helpers.view.call(this, Checkbox, options); } else { delete hash.on; hash.onEvent = onEvent || 'enter'; - return helpers.view.call(this, TextField, options); + return EmberHandlebars.helpers.view.call(this, TextField, options); } } @@ -5168,7 +5296,7 @@ define("ember-handlebars/controls", Internally, `{{textarea}}` creates an instance of `Ember.TextArea`, passing arguments from the helper to `Ember.TextArea`'s `create` method. You can extend the capabilities of text areas in your application by reopening this - class. For example, if you are building a Bootstrap project where `data-*` + class. For example, if you are building a Bootstrap project where `data-*` attributes are used, you can globally add support for a `data-*` attribute on all `{{textarea}}`s' in your app by reopening `Ember.TextArea` or `Ember.TextSupport` and adding it to the `attributeBindings` concatenated @@ -5192,15 +5320,12 @@ define("ember-handlebars/controls", */ function textareaHelper(options) { - var hash = options.hash, - types = options.hashTypes; - - return helpers.view.call(this, TextArea, options); + return EmberHandlebars.helpers.view.call(this, TextArea, options); } __exports__.textareaHelper = textareaHelper; }); -define("ember-handlebars/controls/checkbox", +enifed("ember-handlebars/controls/checkbox", ["ember-metal/property_get","ember-metal/property_set","ember-views/views/view","exports"], function(__dependency1__, __dependency2__, __dependency3__, __exports__) { "use strict"; @@ -5276,7 +5401,7 @@ define("ember-handlebars/controls/checkbox", } }); }); -define("ember-handlebars/controls/select", +enifed("ember-handlebars/controls/select", ["ember-handlebars-compiler","ember-metal/enumerable_utils","ember-metal/property_get","ember-metal/property_set","ember-views/views/view","ember-views/views/collection_view","ember-metal/utils","ember-metal/is_none","ember-metal/computed","ember-runtime/system/native_array","ember-metal/mixin","ember-metal/properties","exports"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __exports__) { "use strict"; @@ -5303,7 +5428,6 @@ define("ember-handlebars/controls/select", var observer = __dependency11__.observer; var defineProperty = __dependency12__.defineProperty; - var precompileTemplate = EmberHandlebars.compile; var SelectOption = View.extend({ instrumentDisplay: 'Ember.SelectOption', @@ -5324,8 +5448,8 @@ define("ember-handlebars/controls/select", }, selected: computed(function() { - var content = get(this, 'content'), - selection = get(this, 'parentView.selection'); + var content = get(this, 'content'); + var selection = get(this, 'parentView.selection'); if (get(this, 'parentView.multiple')) { return selection && indexOf(selection, content.valueOf()) > -1; } else { @@ -5395,7 +5519,7 @@ define("ember-handlebars/controls/select", ``` ```handlebars - {{view Ember.Select content=names}} + {{view "select" content=names}} ``` Would result in the following HTML: @@ -5418,10 +5542,7 @@ define("ember-handlebars/controls/select", ``` ```handlebars - {{view Ember.Select - content=names - value=selectedName - }} + {{view "select" content=names value=selectedName}} ``` Would result in the following HTML with the `"); return buffer; - } - - function program3(depth0,data) { - + },"3":function(depth0,helpers,partials,data) { var stack1; - stack1 = helpers.each.call(depth0, "view.groupedContent", {hash:{},hashTypes:{},hashContexts:{},inverse:self.noop,fn:self.program(4, program4, data),contexts:[depth0],types:["ID"],data:data}); - if(stack1 || stack1 === 0) { data.buffer.push(stack1); } + stack1 = helpers.each.call(depth0, "group", "in", "view.groupedContent", {"name":"each","hash":{},"hashTypes":{},"hashContexts":{},"fn":this.program(4, data),"inverse":this.noop,"types":["ID","ID","ID"],"contexts":[depth0,depth0,depth0],"data":data}); + if (stack1 != null) { data.buffer.push(stack1); } else { data.buffer.push(''); } - } - function program4(depth0,data) { - - - data.buffer.push(escapeExpression(helpers.view.call(depth0, "view.groupView", {hash:{ - 'content': ("content"), - 'label': ("label") - },hashTypes:{'content': "ID",'label': "ID"},hashContexts:{'content': depth0,'label': depth0},contexts:[depth0],types:["ID"],data:data}))); - } - - function program6(depth0,data) { - + },"4":function(depth0,helpers,partials,data) { + var escapeExpression=this.escapeExpression; + data.buffer.push(escapeExpression(helpers.view.call(depth0, "view.groupView", {"name":"view","hash":{ + 'label': ("group.label"), + 'content': ("group.content") + },"hashTypes":{'label': "ID",'content': "ID"},"hashContexts":{'label': depth0,'content': depth0},"types":["ID"],"contexts":[depth0],"data":data}))); + },"6":function(depth0,helpers,partials,data) { var stack1; - stack1 = helpers.each.call(depth0, "view.content", {hash:{},hashTypes:{},hashContexts:{},inverse:self.noop,fn:self.program(7, program7, data),contexts:[depth0],types:["ID"],data:data}); - if(stack1 || stack1 === 0) { data.buffer.push(stack1); } + stack1 = helpers.each.call(depth0, "item", "in", "view.content", {"name":"each","hash":{},"hashTypes":{},"hashContexts":{},"fn":this.program(7, data),"inverse":this.noop,"types":["ID","ID","ID"],"contexts":[depth0,depth0,depth0],"data":data}); + if (stack1 != null) { data.buffer.push(stack1); } else { data.buffer.push(''); } - } - function program7(depth0,data) { - - - data.buffer.push(escapeExpression(helpers.view.call(depth0, "view.optionView", {hash:{ - 'content': ("") - },hashTypes:{'content': "ID"},hashContexts:{'content': depth0},contexts:[depth0],types:["ID"],data:data}))); - } - - stack1 = helpers['if'].call(depth0, "view.prompt", {hash:{},hashTypes:{},hashContexts:{},inverse:self.noop,fn:self.program(1, program1, data),contexts:[depth0],types:["ID"],data:data}); - if(stack1 || stack1 === 0) { data.buffer.push(stack1); } - stack1 = helpers['if'].call(depth0, "view.optionGroupPath", {hash:{},hashTypes:{},hashContexts:{},inverse:self.program(6, program6, data),fn:self.program(3, program3, data),contexts:[depth0],types:["ID"],data:data}); - if(stack1 || stack1 === 0) { data.buffer.push(stack1); } + },"7":function(depth0,helpers,partials,data) { + var escapeExpression=this.escapeExpression; + data.buffer.push(escapeExpression(helpers.view.call(depth0, "view.optionView", {"name":"view","hash":{ + 'content': ("item") + },"hashTypes":{'content': "ID"},"hashContexts":{'content': depth0},"types":["ID"],"contexts":[depth0],"data":data}))); + },"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) { + var stack1, buffer = ''; + stack1 = helpers['if'].call(depth0, "view.prompt", {"name":"if","hash":{},"hashTypes":{},"hashContexts":{},"fn":this.program(1, data),"inverse":this.noop,"types":["ID"],"contexts":[depth0],"data":data}); + if (stack1 != null) { data.buffer.push(stack1); } + stack1 = helpers['if'].call(depth0, "view.optionGroupPath", {"name":"if","hash":{},"hashTypes":{},"hashContexts":{},"fn":this.program(3, data),"inverse":this.program(6, data),"types":["ID"],"contexts":[depth0],"data":data}); + if (stack1 != null) { data.buffer.push(stack1); } return buffer; - - }), + },"useData":true}), attributeBindings: ['multiple', 'disabled', 'tabindex', 'name', 'required', 'autofocus', 'form', 'size'], @@ -5723,7 +5818,8 @@ define("ember-handlebars/controls/select", Otherwise, this should be a list of objects. For instance: ```javascript - Ember.Select.create({ + var App = Ember.Application.create(); + var App.MySelect = Ember.Select.extend({ content: Ember.A([ { id: 1, firstName: 'Yehuda' }, { id: 2, firstName: 'Tom' } @@ -5866,11 +5962,11 @@ define("ember-handlebars/controls/select", }), valueDidChange: observer('value', function() { - var content = get(this, 'content'), - value = get(this, 'value'), - valuePath = get(this, 'optionValuePath').replace(/^content\.?/, ''), - selectedValue = (valuePath ? get(this, 'selection.' + valuePath) : get(this, 'selection')), - selection; + var content = get(this, 'content'); + var value = get(this, 'value'); + var valuePath = get(this, 'optionValuePath').replace(/^content\.?/, ''); + var selectedValue = (valuePath ? get(this, 'selection.' + valuePath) : get(this, 'selection')); + var selection; if (value !== selectedValue) { selection = content ? content.find(function(obj) { @@ -5893,9 +5989,9 @@ define("ember-handlebars/controls/select", }, _changeSingle: function() { - var selectedIndex = this.$()[0].selectedIndex, - content = get(this, 'content'), - prompt = get(this, 'prompt'); + var selectedIndex = this.$()[0].selectedIndex; + var content = get(this, 'content'); + var prompt = get(this, 'prompt'); if (!content || !get(content, 'length')) { return; } if (prompt && selectedIndex === 0) { set(this, 'selection', null); return; } @@ -5906,11 +6002,11 @@ define("ember-handlebars/controls/select", _changeMultiple: function() { - var options = this.$('option:selected'), - prompt = get(this, 'prompt'), - offset = prompt ? 1 : 0, - content = get(this, 'content'), - selection = get(this, 'selection'); + var options = this.$('option:selected'); + var prompt = get(this, 'prompt'); + var offset = prompt ? 1 : 0; + var content = get(this, 'content'); + var selection = get(this, 'selection'); if (!content) { return; } if (options) { @@ -5931,23 +6027,23 @@ define("ember-handlebars/controls/select", var el = this.get('element'); if (!el) { return; } - var content = get(this, 'content'), - selection = get(this, 'selection'), - selectionIndex = content ? indexOf(content, selection) : -1, - prompt = get(this, 'prompt'); + var content = get(this, 'content'); + var selection = get(this, 'selection'); + var selectionIndex = content ? indexOf(content, selection) : -1; + var prompt = get(this, 'prompt'); if (prompt) { selectionIndex += 1; } if (el) { el.selectedIndex = selectionIndex; } }, _selectionDidChangeMultiple: function() { - var content = get(this, 'content'), - selection = get(this, 'selection'), - selectedIndexes = content ? indexesOf(content, selection) : [-1], - prompt = get(this, 'prompt'), - offset = prompt ? 1 : 0, - options = this.$('option'), - adjusted; + var content = get(this, 'content'); + var selection = get(this, 'selection'); + var selectedIndexes = content ? indexesOf(content, selection) : [-1]; + var prompt = get(this, 'prompt'); + var offset = prompt ? 1 : 0; + var options = this.$('option'); + var adjusted; if (options) { options.each(function() { @@ -5969,7 +6065,7 @@ define("ember-handlebars/controls/select", __exports__.SelectOption = SelectOption; __exports__.SelectOptgroup = SelectOptgroup; }); -define("ember-handlebars/controls/text_area", +enifed("ember-handlebars/controls/text_area", ["ember-metal/property_get","ember-views/views/component","ember-handlebars/controls/text_support","ember-metal/mixin","exports"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { "use strict"; @@ -6006,14 +6102,23 @@ define("ember-handlebars/controls/text_area", classNames: ['ember-text-area'], tagName: "textarea", - attributeBindings: ['rows', 'cols', 'name', 'selectionEnd', 'selectionStart', 'wrap'], + attributeBindings: [ + 'rows', + 'cols', + 'name', + 'selectionEnd', + 'selectionStart', + 'wrap', + 'lang', + 'dir' + ], rows: null, cols: null, _updateElementValue: observer('value', function() { // We do this check so cursor position doesn't get affected in IE - var value = get(this, 'value'), - $el = this.$(); + var value = get(this, 'value'); + var $el = this.$(); if ($el && value !== $el.val()) { $el.val(value); } @@ -6025,19 +6130,16 @@ define("ember-handlebars/controls/text_area", } }); }); -define("ember-handlebars/controls/text_field", - ["ember-metal/property_get","ember-metal/property_set","ember-views/views/component","ember-handlebars/controls/text_support","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { +enifed("ember-handlebars/controls/text_field", + ["ember-views/views/component","ember-handlebars/controls/text_support","exports"], + function(__dependency1__, __dependency2__, __exports__) { "use strict"; /** @module ember @submodule ember-handlebars */ - - var get = __dependency1__.get; - var set = __dependency2__.set; - var Component = __dependency3__["default"]; - var TextSupport = __dependency4__["default"]; + var Component = __dependency1__["default"]; + var TextSupport = __dependency2__["default"]; /** @@ -6062,11 +6164,31 @@ define("ember-handlebars/controls/text_field", classNames: ['ember-text-field'], tagName: "input", - attributeBindings: ['type', 'value', 'size', 'pattern', 'name', 'min', 'max', - 'accept', 'autocomplete', 'autosave', 'formaction', - 'formenctype', 'formmethod', 'formnovalidate', 'formtarget', - 'height', 'inputmode', 'list', 'multiple', 'step', - 'width'], + attributeBindings: [ + 'accept', + 'autocomplete', + 'autosave', + 'dir', + 'formaction', + 'formenctype', + 'formmethod', + 'formnovalidate', + 'formtarget', + 'height', + 'inputmode', + 'lang', + 'list', + 'max', + 'min', + 'multiple', + 'name', + 'pattern', + 'size', + 'step', + 'type', + 'value', + 'width' + ], /** The `value` attribute of the input element. As the user inputs text, this @@ -6126,7 +6248,7 @@ define("ember-handlebars/controls/text_field", max: null }); }); -define("ember-handlebars/controls/text_support", +enifed("ember-handlebars/controls/text_support", ["ember-metal/property_get","ember-metal/property_set","ember-metal/mixin","ember-runtime/mixins/target_action_support","exports"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { "use strict"; @@ -6152,21 +6274,30 @@ define("ember-handlebars/controls/text_support", var TextSupport = Mixin.create(TargetActionSupport, { value: "", - attributeBindings: ['placeholder', 'disabled', 'maxlength', 'tabindex', 'readonly', - 'autofocus', 'form', 'selectionDirection', 'spellcheck', 'required', - 'title', 'autocapitalize', 'autocorrect'], + attributeBindings: [ + 'autocapitalize', + 'autocorrect', + 'autofocus', + 'disabled', + 'form', + 'maxlength', + 'placeholder', + 'readonly', + 'required', + 'selectionDirection', + 'spellcheck', + 'tabindex', + 'title' + ], placeholder: null, disabled: false, maxlength: null, init: function() { this._super(); - this.on("focusOut", this, this._elementValueDidChange); - this.on("change", this, this._elementValueDidChange); this.on("paste", this, this._elementValueDidChange); this.on("cut", this, this._elementValueDidChange); this.on("input", this, this._elementValueDidChange); - this.on("keyUp", this, this.interpretKeyEvents); }, /** @@ -6197,7 +6328,7 @@ define("ember-handlebars/controls/text_support", onEvent: 'enter', /** - Whether they `keyUp` event that triggers an `action` to be sent continues + Whether the `keyUp` event that triggers an `action` to be sent continues propagating to other views. By default, when the user presses the return key on their keyboard and @@ -6252,6 +6383,10 @@ define("ember-handlebars/controls/text_support", sendAction('escape-press', this, event); }, + change: function(event) { + this._elementValueDidChange(event); + }, + /** Called when the text area is focused. @@ -6265,7 +6400,7 @@ define("ember-handlebars/controls/text_support", }, /** - Called when the text area is blurred. + Called when the text area is blurred. Uses sendAction to send the `focus-out` action. @@ -6273,6 +6408,7 @@ define("ember-handlebars/controls/text_support", @param {Event} event */ focusOut: function(event) { + this._elementValueDidChange(event); sendAction('focus-out', this, event); }, @@ -6287,8 +6423,36 @@ define("ember-handlebars/controls/text_support", */ keyPress: function(event) { sendAction('key-press', this, event); - } + }, + /** + Called when the browser triggers a `keyup` event on the element. + + Uses sendAction to send the `key-up` action passing the current value + and event as parameters. + + @method keyUp + @param {Event} event + */ + keyUp: function(event) { + this.interpretKeyEvents(event); + + this.sendAction('key-up', get(this, 'value'), event); + }, + + /** + Called when the browser triggers a `keydown` event on the element. + + Uses sendAction to send the `key-down` action passing the current value + and event as parameters. Note that generally in key-down the value is unchanged + (as the key pressing has not completed yet). + + @method keyDown + @param {Event} event + */ + keyDown: function(event) { + this.sendAction('key-down', get(this, 'value'), event); + } }); TextSupport.KEY_EVENTS = { @@ -6300,9 +6464,9 @@ define("ember-handlebars/controls/text_support", // sendAction semantics for TextField are different from // the component semantics so this method normalizes them. function sendAction(eventName, view, event) { - var action = get(view, eventName), - on = get(view, 'onEvent'), - value = get(view, 'value'); + var action = get(view, eventName); + var on = get(view, 'onEvent'); + var value = get(view, 'value'); // back-compat support for keyPress as an event name even though // it's also a method name that consumes the event (and therefore @@ -6322,9 +6486,9 @@ define("ember-handlebars/controls/text_support", __exports__["default"] = TextSupport; }); -define("ember-handlebars/ext", - ["ember-metal/core","ember-runtime/system/string","ember-handlebars-compiler","ember-metal/property_get","ember-metal/binding","ember-metal/error","ember-metal/mixin","ember-metal/is_empty","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) { +enifed("ember-handlebars/ext", + ["ember-metal/core","ember-runtime/system/string","ember-handlebars-compiler","ember-metal/property_get","ember-metal/error","ember-metal/mixin","ember-views/views/view","ember-metal/path_cache","ember-metal/streams/stream","ember-metal/streams/read","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __exports__) { "use strict"; var Ember = __dependency1__["default"]; // Ember.FEATURES, Ember.assert, Ember.Handlebars, Ember.lookup @@ -6333,62 +6497,22 @@ define("ember-handlebars/ext", var fmt = __dependency2__.fmt; var EmberHandlebars = __dependency3__["default"]; - var helpers = EmberHandlebars.helpers; var get = __dependency4__.get; - var isGlobalPath = __dependency5__.isGlobalPath; - var EmberError = __dependency6__["default"]; - var IS_BINDING = __dependency7__.IS_BINDING; + var EmberError = __dependency5__["default"]; + var IS_BINDING = __dependency6__.IS_BINDING; + + var View = __dependency7__["default"]; + var detectIsGlobal = __dependency8__.isGlobal; // late bound via requireModule because of circular dependencies. - var resolveHelper, - SimpleHandlebarsView; + var resolveHelper, SimpleHandlebarsView; - var isEmpty = __dependency8__["default"]; - - var slice = [].slice, originalTemplate = EmberHandlebars.template; - - /** - If a path starts with a reserved keyword, returns the root - that should be used. - - @private - @method normalizePath - @for Ember - @param root {Object} - @param path {String} - @param data {Hash} - */ - function normalizePath(root, path, data) { - var keywords = (data && data.keywords) || {}, - keyword, isKeyword; - - // Get the first segment of the path. For example, if the - // path is "foo.bar.baz", returns "foo". - keyword = path.split('.', 1)[0]; - - // Test to see if the first path is a keyword that has been - // passed along in the view's data hash. If so, we will treat - // that object as the new root. - if (keywords.hasOwnProperty(keyword)) { - // Look up the value in the template's data hash. - root = keywords[keyword]; - isKeyword = true; - - // Handle cases where the entire path is the reserved - // word. In that case, return the object itself. - if (path === keyword) { - path = ''; - } else { - // Strip the keyword from the path and look up - // the remainder from the newly found root. - path = path.substr(keyword.length+1); - } - } - - return { root: root, path: path, isKeyword: isKeyword }; - } + var Stream = __dependency9__["default"]; + var readArray = __dependency10__.readArray; + var readHash = __dependency10__.readHash; + var slice = [].slice; /** Lookup both on root and on window. If the path starts with @@ -6400,90 +6524,89 @@ define("ember-handlebars/ext", @param {Object} root The object to look up the property on @param {String} path The path to be lookedup @param {Object} options The template's option hash + @deprecated */ function handlebarsGet(root, path, options) { - var data = options && options.data, - normalizedPath = normalizePath(root, path, data), - value; - - root = normalizedPath.root; - path = normalizedPath.path; + return options.data.view.getStream(path).value(); + } - value = get(root, path); + /** + handlebarsGetView resolves a view based on strings passed into a template. + For example: - if (value === undefined && root !== Ember.lookup && isGlobalPath(path)) { - value = get(Ember.lookup, path); + ```handlebars + {{view "some-view"}} + {{view view.someView}} + {{view App.SomeView}} {{! deprecated }} + ``` + + A value is first checked to be a string- non-strings are presumed to be + an object and returned. This handles the "access a view on a context" + case (line 2 in the above examples). + + Next a string is normalized, then called on the context with `get`. If + there is still no value, a GlobalPath will be fetched from the global + context (raising a deprecation) and a localPath will be passed to the + container to be looked up. + + @private + @for Ember.Handlebars + @param {Object} context The context of the template being rendered + @param {String} path The path to be lookedup + @param {Object} container The container + @param {Object} data The template's data hash + */ + function handlebarsGetView(context, path, container, data) { + var viewClass; + if ('string' === typeof path) { + if (!data) { + throw new Error("handlebarsGetView: must pass data"); } + + // Only lookup view class on context if there is a context. If not, + // the global lookup path on get may kick in. + var lazyValue = data.view.getStream(path); + viewClass = lazyValue.value(); + var isGlobal = detectIsGlobal(path); + + if (!viewClass && !isGlobal) { + viewClass = container.lookupFactory('view:'+path); + } + if (!viewClass && isGlobal) { + var globalViewClass = get(path); + if (globalViewClass) { + viewClass = globalViewClass; + } + } + } else { + viewClass = path; + } + + // Sometimes a view's value is yet another path + if ('string' === typeof viewClass && data && data.view) { + viewClass = handlebarsGetView(data.view, viewClass, container, data); + } + + return viewClass; + } + + function stringifyValue(value, shouldEscape) { + if (value === null || value === undefined) { + value = ""; + } else if (!(value instanceof Handlebars.SafeString)) { + value = String(value); + } + + if (shouldEscape) { + value = Handlebars.Utils.escapeExpression(value); + } return value; } - /** - This method uses `Ember.Handlebars.get` to lookup a value, then ensures - that the value is escaped properly. - - If `unescaped` is a truthy value then the escaping will not be performed. - - @method getEscaped - @for Ember.Handlebars - @param {Object} root The object to look up the property on - @param {String} path The path to be lookedup - @param {Object} options The template's option hash - @since 1.4.0 - */ - function getEscaped(root, path, options) { - var result = handlebarsGet(root, path, options); - - if (result === null || result === undefined) { - result = ""; - } else if (!(result instanceof Handlebars.SafeString)) { - result = String(result); - } - if (!options.hash.unescaped){ - result = Handlebars.Utils.escapeExpression(result); - } - - return result; - } - - __exports__.getEscaped = getEscaped;function resolveParams(context, params, options) { - var resolvedParams = [], types = options.types, param, type; - - for (var i=0, l=params.length; i 0) { + var firstParam = params[0]; + // Only bother with subscriptions if the first argument + // is a stream itself, and not a primitive. + if (firstParam && firstParam.isStream) { + var onDependentKeyNotify = function onDependentKeyNotify(stream) { + stream.value(); + lazyValue.notify(); + }; + for (i = 0; i < dependentKeys.length; i++) { + var childParam = firstParam.get(dependentKeys[i]); + childParam.value(); + childParam.subscribe(onDependentKeyNotify); + } + } + } } } - helper._rawFunction = fn; return helper; } - /** - Renders the unbound form of an otherwise bound helper function. - - @private - @method evaluateUnboundHelper - @param {Function} fn - @param {Object} context - @param {Array} normalizedProperties - @param {String} options - */ - function evaluateUnboundHelper(context, fn, normalizedProperties, options) { - var args = [], - hash = options.hash, - boundOptions = hash.boundOptions, - types = slice.call(options.types, 1), - loc, - len, - property, - propertyType, - boundOption; - - for (boundOption in boundOptions) { - if (!boundOptions.hasOwnProperty(boundOption)) { continue; } - hash[boundOption] = handlebarsGet(context, boundOptions[boundOption], options); - } - - for(loc = 0, len = normalizedProperties.length; loc < len; ++loc) { - property = normalizedProperties[loc]; - propertyType = types[loc]; - if(propertyType === "ID") { - args.push(handlebarsGet(property.root, property.path, options)); - } else { - args.push(property.path); - } - } - args.push(options); - return fn.apply(context, args); - } - - /** - Overrides Handlebars.template so that we can distinguish - user-created, top-level templates from inner contexts. - - @private - @method template - @for Ember.Handlebars - @param {String} spec - */ - function template(spec) { - var t = originalTemplate(spec); - t.isTop = true; - return t; - } - - __exports__.template = template;__exports__.normalizePath = normalizePath; __exports__.makeBoundHelper = makeBoundHelper; + __exports__.handlebarsGetView = handlebarsGetView; __exports__.handlebarsGet = handlebarsGet; - __exports__.evaluateUnboundHelper = evaluateUnboundHelper; }); -define("ember-handlebars/helpers/binding", - ["ember-metal/core","ember-handlebars-compiler","ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-runtime/system/string","ember-metal/platform","ember-metal/is_none","ember-metal/enumerable_utils","ember-metal/array","ember-views/views/view","ember-metal/run_loop","ember-metal/observer","ember-metal/binding","ember-views/system/jquery","ember-handlebars/ext","ember-runtime/keys","ember-handlebars/views/handlebars_bound_view","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __dependency18__, __exports__) { +enifed("ember-handlebars/helpers/bind_attr", + ["ember-metal/core","ember-handlebars-compiler","ember-metal/utils","ember-runtime/system/string","ember-metal/array","ember-views/views/view","ember-metal/keys","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) { "use strict"; /** @module ember @@ -6880,585 +6912,19 @@ define("ember-handlebars/helpers/binding", */ var Ember = __dependency1__["default"]; - // Ember.assert, Ember.warn, uuid - // var emberAssert = Ember.assert, Ember.warn = Ember.warn; - + // Ember.assert var EmberHandlebars = __dependency2__["default"]; - var get = __dependency3__.get; - var set = __dependency4__.set; - var apply = __dependency5__.apply; - var uuid = __dependency5__.uuid; - var fmt = __dependency6__.fmt; - var o_create = __dependency7__.create; - var isNone = __dependency8__["default"]; - var EnumerableUtils = __dependency9__["default"]; - var forEach = __dependency10__.forEach; - var View = __dependency11__["default"]; - var run = __dependency12__["default"]; - var removeObserver = __dependency13__.removeObserver; - var isGlobalPath = __dependency14__.isGlobalPath; - var emberBind = __dependency14__.bind; - var jQuery = __dependency15__["default"]; - var isArray = __dependency5__.isArray; - var handlebarsGetEscaped = __dependency16__.getEscaped; - var keys = __dependency17__["default"]; - var _HandlebarsBoundView = __dependency18__._HandlebarsBoundView; - var SimpleHandlebarsView = __dependency18__.SimpleHandlebarsView; - - var normalizePath = __dependency16__.normalizePath; - var handlebarsGet = __dependency16__.handlebarsGet; - var getEscaped = __dependency16__.getEscaped; - - var guidFor = __dependency5__.guidFor; - var typeOf = __dependency5__.typeOf; + var uuid = __dependency3__.uuid; + var fmt = __dependency4__.fmt; + var typeOf = __dependency3__.typeOf; + var forEach = __dependency5__.forEach; + var View = __dependency6__["default"]; + var keys = __dependency7__["default"]; var helpers = EmberHandlebars.helpers; var SafeString = EmberHandlebars.SafeString; - function exists(value) { - return !isNone(value); - } - - var WithView = _HandlebarsBoundView.extend({ - init: function() { - var controller; - - apply(this, this._super, arguments); - - var keywords = this.templateData.keywords; - var keywordName = this.templateHash.keywordName; - var keywordPath = this.templateHash.keywordPath; - var controllerName = this.templateHash.controller; - var preserveContext = this.preserveContext; - - if (controllerName) { - var previousContext = this.previousContext; - controller = this.container.lookupFactory('controller:'+controllerName).create({ - parentController: previousContext, - target: previousContext - }); - - this._generatedController = controller; - - if (!preserveContext) { - this.set('controller', controller); - - this.valueNormalizerFunc = function(result) { - controller.set('model', result); - return controller; - }; - } else { - var controllerPath = jQuery.expando + guidFor(controller); - keywords[controllerPath] = controller; - emberBind(keywords, controllerPath + '.model', keywordPath); - keywordPath = controllerPath; - } - } - - if (preserveContext) { - emberBind(keywords, keywordName, keywordPath); - } - - }, - willDestroy: function() { - this._super(); - - if (this._generatedController) { - this._generatedController.destroy(); - } - } - }); - - // Binds a property into the DOM. This will create a hook in DOM that the - // KVO system will look for and update if the property changes. - function bind(property, options, preserveContext, shouldDisplay, valueNormalizer, childProperties) { - var data = options.data, - fn = options.fn, - inverse = options.inverse, - view = data.view, - normalized, observer, i; - - // we relied on the behavior of calling without - // context to mean this === window, but when running - // "use strict", it's possible for this to === undefined; - var currentContext = this || window; - - normalized = normalizePath(currentContext, property, data); - - // Set up observers for observable objects - if ('object' === typeof this) { - if (data.insideGroup) { - observer = function() { - while (view._contextView) { - view = view._contextView; - } - run.once(view, 'rerender'); - }; - - var template, context, result = handlebarsGet(currentContext, property, options); - - result = valueNormalizer ? valueNormalizer(result) : result; - - context = preserveContext ? currentContext : result; - if (shouldDisplay(result)) { - template = fn; - } else if (inverse) { - template = inverse; - } - - template(context, { data: options.data }); - } else { - var viewClass = _HandlebarsBoundView; - var viewOptions = { - preserveContext: preserveContext, - shouldDisplayFunc: shouldDisplay, - valueNormalizerFunc: valueNormalizer, - displayTemplate: fn, - inverseTemplate: inverse, - path: property, - pathRoot: currentContext, - previousContext: currentContext, - isEscaped: !options.hash.unescaped, - templateData: options.data, - templateHash: options.hash, - helperName: options.helperName - }; - - if (options.isWithHelper) { - viewClass = WithView; - } - - // Create the view that will wrap the output of this template/property - // and add it to the nearest view's childViews array. - // See the documentation of Ember._HandlebarsBoundView for more. - var bindView = view.createChildView(viewClass, viewOptions); - - view.appendChild(bindView); - - observer = function() { - run.scheduleOnce('render', bindView, 'rerenderIfNeeded'); - }; - } - - // Observes the given property on the context and - // tells the Ember._HandlebarsBoundView to re-render. If property - // is an empty string, we are printing the current context - // object ({{this}}) so updating it is not our responsibility. - if (normalized.path !== '') { - view.registerObserver(normalized.root, normalized.path, observer); - if (childProperties) { - for (i=0; i{{user.name}} - -
      -
      {{user.role.label}}
      - {{user.role.id}} - -

      {{user.role.description}}

      -
      - ``` - - `{{with}}` can be our best friend in these cases, - instead of writing `user.role.*` over and over, we use `{{#with user.role}}`. - Now the context within the `{{#with}} .. {{/with}}` block is `user.role` so you can do the following: - - ```handlebars -
      {{user.name}}
      - -
      - {{#with user.role}} -
      {{label}}
      - {{id}} - -

      {{description}}

      - {{/with}} -
      - ``` - - ### `as` operator - - This operator aliases the scope to a new name. It's helpful for semantic clarity and to retain - default scope or to reference from another `{{with}}` block. - - ```handlebars - // posts might not be - {{#with user.posts as blogPosts}} -
      - There are {{blogPosts.length}} blog posts written by {{user.name}}. -
      - - {{#each post in blogPosts}} -
    • {{post.title}}
    • - {{/each}} - {{/with}} - ``` - - Without the `as` operator, it would be impossible to reference `user.name` in the example above. - - NOTE: The alias should not reuse a name from the bound property path. - For example: `{{#with foo.bar as foo}}` is not supported because it attempts to alias using - the first part of the property path, `foo`. Instead, use `{{#with foo.bar as baz}}`. - - ### `controller` option - - Adding `controller='something'` instructs the `{{with}}` helper to create and use an instance of - the specified controller with the new context as its content. - - This is very similar to using an `itemController` option with the `{{each}}` helper. - - ```handlebars - {{#with users.posts controller='userBlogPosts'}} - {{!- The current context is wrapped in our controller instance }} - {{/with}} - ``` - - In the above example, the template provided to the `{{with}}` block is now wrapped in the - `userBlogPost` controller, which provides a very elegant way to decorate the context with custom - functions/properties. - - @method with - @for Ember.Handlebars.helpers - @param {Function} context - @param {Hash} options - @return {String} HTML string - */ - function withHelper(context, options) { - var bindContext, preserveContext, controller, helperName = 'with'; - - if (arguments.length === 4) { - var keywordName, path, rootPath, normalized, contextPath; - - options = arguments[3]; - keywordName = arguments[2]; - path = arguments[0]; - - if (path) { - helperName += ' ' + path + ' as ' + keywordName; - } - - - var localizedOptions = o_create(options); - localizedOptions.data = o_create(options.data); - localizedOptions.data.keywords = o_create(options.data.keywords || {}); - - if (isGlobalPath(path)) { - contextPath = path; - } else { - normalized = normalizePath(this, path, options.data); - path = normalized.path; - rootPath = normalized.root; - - // This is a workaround for the fact that you cannot bind separate objects - // together. When we implement that functionality, we should use it here. - var contextKey = jQuery.expando + guidFor(rootPath); - localizedOptions.data.keywords[contextKey] = rootPath; - // if the path is '' ("this"), just bind directly to the current context - contextPath = path ? contextKey + '.' + path : contextKey; - } - - localizedOptions.hash.keywordName = keywordName; - localizedOptions.hash.keywordPath = contextPath; - - bindContext = this; - context = contextPath; - options = localizedOptions; - preserveContext = true; - } else { - - helperName += ' ' + context; - bindContext = options.contexts[0]; - preserveContext = false; - } - - options.helperName = helperName; - options.isWithHelper = true; - - return bind.call(bindContext, context, options, preserveContext, exists); - } - /** - See [boundIf](/api/classes/Ember.Handlebars.helpers.html#method_boundIf) - and [unboundIf](/api/classes/Ember.Handlebars.helpers.html#method_unboundIf) - - @method if - @for Ember.Handlebars.helpers - @param {Function} context - @param {Hash} options - @return {String} HTML string - */ - function ifHelper(context, options) { - - options.helperName = options.helperName || ('if ' + context); - - if (options.data.isUnbound) { - return helpers.unboundIf.call(options.contexts[0], context, options); - } else { - return helpers.boundIf.call(options.contexts[0], context, options); - } - } - - /** - @method unless - @for Ember.Handlebars.helpers - @param {Function} context - @param {Hash} options - @return {String} HTML string - */ - function unlessHelper(context, options) { - - var fn = options.fn, inverse = options.inverse, helperName = 'unless'; - - if (context) { - helperName += ' ' + context; - } - - options.fn = inverse; - options.inverse = fn; - - options.helperName = options.helperName || helperName; - - if (options.data.isUnbound) { - return helpers.unboundIf.call(options.contexts[0], context, options); - } else { - return helpers.boundIf.call(options.contexts[0], context, options); - } - } - /** `bind-attr` allows you to create a binding between DOM element attributes and Ember objects. For example: @@ -7613,44 +7079,23 @@ define("ember-handlebars/helpers/binding", // For each attribute passed, create an observer and emit the // current value of the property as an attribute. forEach.call(attrKeys, function(attr) { - var path = attrs[attr], - normalized; + var path = attrs[attr]; - normalized = normalizePath(ctx, path, options.data); - - var value = (path === 'this') ? normalized.root : handlebarsGet(ctx, path, options), - type = typeOf(value); + var lazyValue = view.getStream(path); + var value = lazyValue.value(); + var type = typeOf(value); - var observer; - - observer = function observer() { - var result = handlebarsGet(ctx, path, options); + lazyValue.subscribe(view._wrapAsScheduled(function applyAttributeBindings() { + var result = lazyValue.value(); var elem = view.$("[data-bindattr-" + dataId + "='" + dataId + "']"); - // If we aren't able to find the element, it means the element - // to which we were bound has been removed from the view. - // In that case, we can assume the template has been re-rendered - // and we need to clean up the observer. - if (!elem || elem.length === 0) { - removeObserver(normalized.root, normalized.path, observer); - return; - } - + View.applyAttributeBindings(elem, attr, result); - }; - - // Add an observer to the view for when the property changes. - // When the observer fires, find the element using the - // unique data id and update the attribute to the new value. - // Note: don't add observer when path is 'this' or path - // is whole keyword e.g. {{#each x in list}} ... {{bind-attr attr="x"}} - if (path !== 'this' && !(normalized.isKeyword && normalized.path === '' )) { - view.registerObserver(normalized.root, normalized.path, observer); - } + })); // if this changes, also change the logic in ember-views/lib/views/view.js if ((type === 'string' || (type === 'number' && !isNaN(value)))) { @@ -7678,7 +7123,8 @@ define("ember-handlebars/helpers/binding", @return {String} HTML string */ function bindAttrHelperDeprecated() { - return helpers['bind-attr'].apply(this, arguments); + + return helpers['bind-attr'].apply(this, arguments); } /** @@ -7705,25 +7151,8 @@ define("ember-handlebars/helpers/binding", @return {Array} An array of class names to add */ function bindClasses(context, classBindings, view, bindAttrId, options) { - var ret = [], newClass, value, elem; - - // Helper method to retrieve the property from the context and - // determine which class string to return, based on whether it is - // a Boolean or not. - var classStringForPath = function(root, parsedPath, options) { - var val, - path = parsedPath.path; - - if (path === 'this') { - val = root; - } else if (path === '') { - val = true; - } else { - val = handlebarsGet(root, path, options); - } - - return View._classStringForValue(path, val, parsedPath.className, parsedPath.falsyClassName); - }; + var ret = []; + var newClass, value, elem; // For each property passed, loop through and setup // an observer. @@ -7733,33 +7162,25 @@ define("ember-handlebars/helpers/binding", // closes over this variable, so it knows which string to remove when // the property changes. var oldClass; + var parsedPath = View._parsePropertyPath(binding); + var path = parsedPath.path; + var initialValue; - var observer; + if (path === '') { + initialValue = true; + } else { + var lazyValue = view.getStream(path); + initialValue = lazyValue.value(); - var parsedPath = View._parsePropertyPath(binding), - path = parsedPath.path, - pathRoot = context, - normalized; + // Set up an observer on the context. If the property changes, toggle the + // class name. + lazyValue.subscribe(view._wrapAsScheduled(function applyClassNameBindings() { + // Get the current value of the property + var value = lazyValue.value(); + newClass = classStringForParsedPath(parsedPath, value); + elem = bindAttrId ? view.$("[data-bindattr-" + bindAttrId + "='" + bindAttrId + "']") : view.$(); - if (path !== '' && path !== 'this') { - normalized = normalizePath(context, path, options.data); - - pathRoot = normalized.root; - path = normalized.path; - } - - // Set up an observer on the context. If the property changes, toggle the - // class name. - observer = function() { - // Get the current value of the property - newClass = classStringForPath(context, parsedPath, options); - elem = bindAttrId ? view.$("[data-bindattr-" + bindAttrId + "='" + bindAttrId + "']") : view.$(); - - // If we can't find the element anymore, a parent template has been - // re-rendered and we've been nuked. Remove the observer. - if (!elem || elem.length === 0) { - removeObserver(pathRoot, path, observer); - } else { + // If we had previously added a class to the element, remove it. if (oldClass) { elem.removeClass(oldClass); @@ -7773,16 +7194,12 @@ define("ember-handlebars/helpers/binding", } else { oldClass = null; } - } - }; - - if (path !== '' && path !== 'this') { - view.registerObserver(pathRoot, path, observer); + })); } // We've already setup the observer; now we just need to figure out the // correct behavior right now on the first pass through. - value = classStringForPath(context, parsedPath, options); + value = classStringForParsedPath(parsedPath, initialValue); if (value) { ret.push(value); @@ -7796,22 +7213,234 @@ define("ember-handlebars/helpers/binding", return ret; } - __exports__.bind = bind; - __exports__._triageMustacheHelper = _triageMustacheHelper; - __exports__.resolveHelper = resolveHelper; - __exports__.bindHelper = bindHelper; - __exports__.boundIfHelper = boundIfHelper; - __exports__.unboundIfHelper = unboundIfHelper; - __exports__.withHelper = withHelper; - __exports__.ifHelper = ifHelper; - __exports__.unlessHelper = unlessHelper; + function classStringForParsedPath(parsedPath, value) { + return View._classStringForValue(parsedPath.path, value, parsedPath.className, parsedPath.falsyClassName); + } + + __exports__["default"] = bindAttrHelper; + __exports__.bindAttrHelper = bindAttrHelper; __exports__.bindAttrHelperDeprecated = bindAttrHelperDeprecated; __exports__.bindClasses = bindClasses; }); -define("ember-handlebars/helpers/collection", - ["ember-metal/core","ember-metal/utils","ember-handlebars-compiler","ember-runtime/system/string","ember-metal/property_get","ember-handlebars/ext","ember-handlebars/helpers/view","ember-metal/computed","ember-views/views/collection_view","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __exports__) { +enifed("ember-handlebars/helpers/binding", + ["ember-metal/core","ember-handlebars-compiler","ember-metal/is_none","ember-metal/run_loop","ember-metal/cache","ember-metal/streams/simple","ember-handlebars/views/handlebars_bound_view","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-handlebars + */ + + var Ember = __dependency1__["default"]; + // Ember.assert + var EmberHandlebars = __dependency2__["default"]; + + var isNone = __dependency3__["default"]; + var run = __dependency4__["default"]; + var Cache = __dependency5__["default"]; + var SimpleStream = __dependency6__["default"]; + + var _HandlebarsBoundView = __dependency7__._HandlebarsBoundView; + var SimpleHandlebarsView = __dependency7__.SimpleHandlebarsView; + + var helpers = EmberHandlebars.helpers; + + function exists(value) { + return !isNone(value); + } + + // Binds a property into the DOM. This will create a hook in DOM that the + // KVO system will look for and update if the property changes. + function bind(property, options, preserveContext, shouldDisplay, valueNormalizer, childProperties, _viewClass) { + var data = options.data; + var view = data.view; + + // we relied on the behavior of calling without + // context to mean this === window, but when running + // "use strict", it's possible for this to === undefined; + var currentContext = this || window; + + var valueStream = view.getStream(property); + var lazyValue; + + if (childProperties) { + lazyValue = new SimpleStream(valueStream); + + var subscriber = function(childStream) { + childStream.value(); + lazyValue.notify(); + }; + + for (var i = 0; i < childProperties.length; i++) { + var childStream = valueStream.get(childProperties[i]); + childStream.value(); + childStream.subscribe(subscriber); + } + } else { + lazyValue = valueStream; + } + + // Set up observers for observable objects + var viewClass = _viewClass || _HandlebarsBoundView; + var viewOptions = { + preserveContext: preserveContext, + shouldDisplayFunc: shouldDisplay, + valueNormalizerFunc: valueNormalizer, + displayTemplate: options.fn, + inverseTemplate: options.inverse, + lazyValue: lazyValue, + previousContext: currentContext, + isEscaped: !options.hash.unescaped, + templateData: options.data, + templateHash: options.hash, + helperName: options.helperName + }; + + if (options.keywords) { + viewOptions._keywords = options.keywords; + } + + // Create the view that will wrap the output of this template/property + // and add it to the nearest view's childViews array. + // See the documentation of Ember._HandlebarsBoundView for more. + var bindView = view.createChildView(viewClass, viewOptions); + + view.appendChild(bindView); + + lazyValue.subscribe(view._wrapAsScheduled(function() { + run.scheduleOnce('render', bindView, 'rerenderIfNeeded'); + })); + } + + function simpleBind(currentContext, lazyValue, options) { + var data = options.data; + var view = data.view; + + var bindView = new SimpleHandlebarsView( + lazyValue, !options.hash.unescaped + ); + + bindView._parentView = view; + view.appendChild(bindView); + + lazyValue.subscribe(view._wrapAsScheduled(function() { + run.scheduleOnce('render', bindView, 'rerender'); + })); + } + + /** + '_triageMustache' is used internally select between a binding, helper, or component for + the given context. Until this point, it would be hard to determine if the + mustache is a property reference or a regular helper reference. This triage + helper resolves that. + + This would not be typically invoked by directly. + + @private + @method _triageMustache + @for Ember.Handlebars.helpers + @param {String} property Property/helperID to triage + @param {Object} options hash of template/rendering options + @return {String} HTML string + */ + function _triageMustacheHelper(property, options) { + + var helper = EmberHandlebars.resolveHelper(options.data.view.container, property); + if (helper) { + return helper.call(this, options); + } + + return helpers.bind.call(this, property, options); + } + + var ISNT_HELPER_CACHE = new Cache(1000, function(key) { + return key.indexOf('-') === -1; + }); + __exports__.ISNT_HELPER_CACHE = ISNT_HELPER_CACHE; + /** + Used to lookup/resolve handlebars helpers. The lookup order is: + + * Look for a registered helper + * If a dash exists in the name: + * Look for a helper registed in the container + * Use Ember.ComponentLookup to find an Ember.Component that resolves + to the given name + + @private + @method resolveHelper + @param {Container} container + @param {String} name the name of the helper to lookup + @return {Handlebars Helper} + */ + function resolveHelper(container, name) { + if (helpers[name]) { + return helpers[name]; + } + + if (!container || ISNT_HELPER_CACHE.get(name)) { + return; + } + + var helper = container.lookup('helper:' + name); + if (!helper) { + var componentLookup = container.lookup('component-lookup:main'); + + var Component = componentLookup.lookupFactory(name, container); + if (Component) { + helper = EmberHandlebars.makeViewHelper(Component); + container.register('helper:' + name, helper); + } + } + return helper; + } + + + /** + `bind` can be used to display a value, then update that value if it + changes. For example, if you wanted to print the `title` property of + `content`: + + ```handlebars + {{bind "content.title"}} + ``` + + This will return the `title` property as a string, then create a new observer + at the specified path. If it changes, it will update the value in DOM. Note + that if you need to support IE7 and IE8 you must modify the model objects + properties using `Ember.get()` and `Ember.set()` for this to work as it + relies on Ember's KVO system. For all other browsers this will be handled for + you automatically. + + @private + @method bind + @for Ember.Handlebars.helpers + @param {String} property Property to bind + @param {Function} fn Context to provide for rendering + @return {String} HTML string + */ + function bindHelper(property, options) { + + var context = (options.contexts && options.contexts.length) ? options.contexts[0] : this; + + if (!options.fn) { + var lazyValue = options.data.view.getStream(property); + return simpleBind(context, lazyValue, options); + } + + options.helperName = 'bind'; + + return bind.call(context, property, options, false, exists); + } + + __exports__.bind = bind; + __exports__._triageMustacheHelper = _triageMustacheHelper; + __exports__.resolveHelper = resolveHelper; + __exports__.bindHelper = bindHelper; + }); +enifed("ember-handlebars/helpers/collection", + ["ember-metal/core","ember-handlebars-compiler","ember-metal/mixin","ember-runtime/system/string","ember-metal/property_get","ember-metal/streams/simple","ember-handlebars/ext","ember-handlebars/helpers/view","ember-views/views/view","ember-views/views/collection_view","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __exports__) { "use strict"; /** @module ember @@ -7820,22 +7449,21 @@ define("ember-handlebars/helpers/collection", var Ember = __dependency1__["default"]; // Ember.assert, Ember.deprecate - var inspect = __dependency2__.inspect; // var emberAssert = Ember.assert; // emberDeprecate = Ember.deprecate; - var EmberHandlebars = __dependency3__["default"]; - var helpers = EmberHandlebars.helpers; + var EmberHandlebars = __dependency2__["default"]; + var IS_BINDING = __dependency3__.IS_BINDING; var fmt = __dependency4__.fmt; var get = __dependency5__.get; - var handlebarsGet = __dependency6__.handlebarsGet; - var ViewHelper = __dependency7__.ViewHelper; - var computed = __dependency8__.computed; - var CollectionView = __dependency9__["default"]; + var SimpleStream = __dependency6__["default"]; + var handlebarsGetView = __dependency7__.handlebarsGetView; + var ViewHelper = __dependency8__.ViewHelper; + var View = __dependency9__["default"]; + var CollectionView = __dependency10__["default"]; - var alias = computed.alias; /** `{{collection}}` is a `Ember.Handlebars` helper for adding instances of `Ember.CollectionView` to a template. See [Ember.CollectionView](/api/classes/Ember.CollectionView.html) @@ -7852,7 +7480,8 @@ define("ember-handlebars/helpers/collection", Given an empty `` the following template: ```handlebars - {{#collection contentBinding="App.items"}} + {{! application.hbs }} + {{#collection content=model}} Hi {{view.content.name}} {{/collection}} ``` @@ -7860,44 +7489,45 @@ define("ember-handlebars/helpers/collection", And the following application code ```javascript - App = Ember.Application.create() - App.items = [ - Ember.Object.create({name: 'Dave'}), - Ember.Object.create({name: 'Mary'}), - Ember.Object.create({name: 'Sara'}) - ] + App = Ember.Application.create(); + App.ApplicationRoute = Ember.Route.extend({ + model: function(){ + return [{name: 'Yehuda'},{name: 'Tom'},{name: 'Peter'}]; + } + }); ``` - Will result in the HTML structure below + The following HTML will result: ```html
      -
      Hi Dave
      -
      Hi Mary
      -
      Hi Sara
      +
      Hi Yehuda
      +
      Hi Tom
      +
      Hi Peter
      ``` - ### Blockless use in a collection + ### Non-block version of collection - If you provide an `itemViewClass` option that has its own `template` you can + If you provide an `itemViewClass` option that has its own `template` you may omit the block. The following template: ```handlebars - {{collection contentBinding="App.items" itemViewClass="App.AnItemView"}} + {{! application.hbs }} + {{collection content=model itemViewClass="an-item"}} ``` And application code ```javascript App = Ember.Application.create(); - App.items = [ - Ember.Object.create({name: 'Dave'}), - Ember.Object.create({name: 'Mary'}), - Ember.Object.create({name: 'Sara'}) - ]; + App.ApplicationRoute = Ember.Route.extend({ + model: function(){ + return [{name: 'Yehuda'},{name: 'Tom'},{name: 'Peter'}]; + } + }); App.AnItemView = Ember.View.extend({ template: Ember.Handlebars.compile("Greetings {{view.content.name}}") @@ -7908,9 +7538,9 @@ define("ember-handlebars/helpers/collection", ```html
      -
      Greetings Dave
      -
      Greetings Mary
      -
      Greetings Sara
      +
      Greetings Yehuda
      +
      Greetings Tom
      +
      Greetings Peter
      ``` @@ -7921,11 +7551,13 @@ define("ember-handlebars/helpers/collection", the helper by passing it as the first argument: ```handlebars - {{#collection App.MyCustomCollectionClass contentBinding="App.items"}} + {{#collection "my-custom-collection" content=model}} Hi {{view.content.name}} {{/collection}} ``` + This example would look for the class `App.MyCustomCollection`. + ### Forwarded `item.*`-named Options As with the `{{view}}`, helper options passed to the `{{collection}}` will be @@ -7934,7 +7566,7 @@ define("ember-handlebars/helpers/collection", item (note the camelcasing): ```handlebars - {{#collection contentBinding="App.items" + {{#collection content=model itemTagName="p" itemClassNames="greeting"}} Howdy {{view.content.name}} @@ -7945,9 +7577,9 @@ define("ember-handlebars/helpers/collection", ```html
      -

      Howdy Dave

      -

      Howdy Mary

      -

      Howdy Sara

      +

      Howdy Yehuda

      +

      Howdy Tom

      +

      Howdy Peter

      ``` @@ -7967,55 +7599,72 @@ define("ember-handlebars/helpers/collection", } else { } - var fn = options.fn; - var data = options.data; - var inverse = options.inverse; - var view = options.data.view; + var fn = options.fn, + data = options.data, + inverse = options.inverse, + view = options.data.view, + // This should be deterministic, and should probably come from a + // parent view and not the controller. + container = (view.controller && view.controller.container ? view.controller.container : view.container); - - var controller, container; // If passed a path string, convert that into an object. // Otherwise, just default to the standard class. var collectionClass; if (path) { - controller = data.keywords.controller; - container = controller && controller.container; - collectionClass = handlebarsGet(this, path, options) || container.lookupFactory('view:' + path); + collectionClass = handlebarsGetView(this, path, container, options.data); } else { collectionClass = CollectionView; } - var hash = options.hash, itemHash = {}, match; + var hash = options.hash; + var hashTypes = options.hashTypes; + var itemHash = {}; + var match; // Extract item view class if provided else default to the standard class - var collectionPrototype = collectionClass.proto(), itemViewClass; + var collectionPrototype = collectionClass.proto(); + var itemViewClass; if (hash.itemView) { - controller = data.keywords.controller; - container = controller.container; - itemViewClass = container.lookupFactory('view:' + hash.itemView); - } else if (hash.itemViewClass) { - itemViewClass = handlebarsGet(collectionPrototype, hash.itemViewClass, options); + itemViewClass = hash.itemView; + } else if (hash.itemViewClass) { + if (hashTypes.itemViewClass === 'ID') { + var itemViewClassStream = view.getStream(hash.itemViewClass); + itemViewClass = itemViewClassStream.value(); + } else { + itemViewClass = hash.itemViewClass; + } } else { itemViewClass = collectionPrototype.itemViewClass; } + if (typeof itemViewClass === 'string') { + itemViewClass = container.lookupFactory('view:'+itemViewClass); + } + delete hash.itemViewClass; delete hash.itemView; + delete hashTypes.itemViewClass; + delete hashTypes.itemView; // Go through options passed to the {{collection}} helper and extract options // that configure item views instead of the collection itself. for (var prop in hash) { + if (prop === 'itemController' || prop === 'itemClassBinding') { + continue; + } if (hash.hasOwnProperty(prop)) { match = prop.match(/^item(.)(.*)$/); + if (match) { + var childProp = match[1].toLowerCase() + match[2]; - if (match && prop !== 'itemController') { - // Convert itemShouldFoo -> shouldFoo - itemHash[match[1].toLowerCase() + match[2]] = hash[prop]; - // Delete from hash as this will end up getting passed to the - // {{view}} helper method. + if (hashTypes[prop] === 'ID' || IS_BINDING.test(prop)) { + itemHash[childProp] = view._getBindingForStream(hash[prop]); + } else { + itemHash[childProp] = hash[prop]; + } delete hash[prop]; } } @@ -8034,29 +7683,47 @@ define("ember-handlebars/helpers/collection", tagName: itemHash.tagName }); } else if (hash.emptyViewClass) { - emptyViewClass = handlebarsGet(this, hash.emptyViewClass, options); + emptyViewClass = handlebarsGetView(this, hash.emptyViewClass, container, options.data); } if (emptyViewClass) { hash.emptyView = emptyViewClass; } if (hash.keyword) { - itemHash._context = this; + itemHash._contextBinding = '_parentView.context'; } else { - itemHash._context = alias('content'); + itemHash._contextBinding = 'content'; } var viewOptions = ViewHelper.propertiesFromHTMLOptions({ data: data, hash: itemHash }, this); - hash.itemViewClass = itemViewClass.extend(viewOptions); + + if (hash.itemClassBinding) { + var itemClassBindings = hash.itemClassBinding.split(' '); + + for (var i = 0; i < itemClassBindings.length; i++) { + var parsedPath = View._parsePropertyPath(itemClassBindings[i]); + if (parsedPath.path === '') { + parsedPath.stream = new SimpleStream(true); + } else { + parsedPath.stream = view.getStream(parsedPath.path); + } + itemClassBindings[i] = parsedPath; + } + + viewOptions.classNameBindings = itemClassBindings; + } + + hash.itemViewClass = itemViewClass; + hash._itemViewProps = viewOptions; options.helperName = options.helperName || 'collection'; - return helpers.view.call(this, collectionClass, options); + return EmberHandlebars.helpers.view.call(this, collectionClass, options); } __exports__["default"] = collectionHelper; }); -define("ember-handlebars/helpers/debug", - ["ember-metal/core","ember-metal/utils","ember-metal/logger","ember-metal/property_get","ember-handlebars/ext","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { +enifed("ember-handlebars/helpers/debug", + ["ember-metal/core","ember-metal/utils","ember-metal/logger","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { "use strict"; /*jshint debug:true*/ @@ -8069,10 +7736,6 @@ define("ember-handlebars/helpers/debug", var inspect = __dependency2__.inspect; var Logger = __dependency3__["default"]; - var get = __dependency4__.get; - var normalizePath = __dependency5__.normalizePath; - var handlebarsGet = __dependency5__.handlebarsGet; - var a_slice = [].slice; /** @@ -8088,24 +7751,16 @@ define("ember-handlebars/helpers/debug", @param {String} property */ function logHelper() { - var params = a_slice.call(arguments, 0, -1), - options = arguments[arguments.length - 1], - logger = Logger.log, - values = [], - allowPrimitives = true; + var params = a_slice.call(arguments, 0, -1); + var options = arguments[arguments.length - 1]; + var view = options.data.view; + var logger = Logger.log; + var values = []; for (var i = 0; i < params.length; i++) { - var type = options.types[i]; - - if (type === 'ID' || !allowPrimitives) { - var context = (options.contexts && options.contexts[i]) || this, - normalized = normalizePath(context, params[i], options.data); - - if (normalized.path === 'this') { - values.push(normalized.root); - } else { - values.push(handlebarsGet(normalized.root, normalized.path, options)); - } + if (options.types[i] === 'ID') { + var stream = view.getStream(params[i]); + values.push(stream.value()); } else { values.push(params[i]); } @@ -8151,6 +7806,7 @@ define("ember-handlebars/helpers/debug", function debuggerHelper(options) { // These are helpful values you can inspect while debugging. + /* jshint unused: false */ var templateContext = this; var typeOfTemplateContext = inspect(templateContext); @@ -8160,9 +7816,9 @@ define("ember-handlebars/helpers/debug", __exports__.logHelper = logHelper; __exports__.debuggerHelper = debuggerHelper; }); -define("ember-handlebars/helpers/each", - ["ember-metal/core","ember-handlebars-compiler","ember-runtime/system/string","ember-metal/property_get","ember-metal/property_set","ember-views/views/collection_view","ember-metal/binding","ember-runtime/mixins/controller","ember-runtime/controllers/array_controller","ember-runtime/mixins/array","ember-runtime/copy","ember-metal/run_loop","ember-metal/events","ember-handlebars/ext","ember-metal/computed","ember-metal/observer","ember-handlebars/views/metamorph_view","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __exports__) { +enifed("ember-handlebars/helpers/each", + ["ember-metal/core","ember-handlebars-compiler","ember-runtime/system/string","ember-metal/property_get","ember-metal/property_set","ember-views/views/collection_view","ember-metal/binding","ember-runtime/mixins/controller","ember-runtime/controllers/array_controller","ember-runtime/mixins/array","ember-metal/observer","ember-handlebars/views/metamorph_view","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __exports__) { "use strict"; /** @@ -8171,11 +7827,8 @@ define("ember-handlebars/helpers/each", */ var Ember = __dependency1__["default"]; // Ember.assert;, Ember.K - // var emberAssert = Ember.assert, - var K = Ember.K; var EmberHandlebars = __dependency2__["default"]; - var helpers = EmberHandlebars.helpers; var fmt = __dependency3__.fmt; var get = __dependency4__.get; @@ -8185,19 +7838,14 @@ define("ember-handlebars/helpers/each", var ControllerMixin = __dependency8__["default"]; var ArrayController = __dependency9__["default"]; var EmberArray = __dependency10__["default"]; - var copy = __dependency11__["default"]; - var run = __dependency12__["default"]; - var on = __dependency13__.on; - var handlebarsGet = __dependency14__.handlebarsGet; - var computed = __dependency15__.computed; - var addObserver = __dependency16__.addObserver; - var removeObserver = __dependency16__.removeObserver; - var addBeforeObserver = __dependency16__.addBeforeObserver; - var removeBeforeObserver = __dependency16__.removeBeforeObserver; + var addObserver = __dependency11__.addObserver; + var removeObserver = __dependency11__.removeObserver; + var addBeforeObserver = __dependency11__.addBeforeObserver; + var removeBeforeObserver = __dependency11__.removeBeforeObserver; - var _Metamorph = __dependency17__._Metamorph; - var _MetamorphView = __dependency17__._MetamorphView; + var _MetamorphView = __dependency12__["default"]; + var _Metamorph = __dependency12__._Metamorph; var EachView = CollectionView.extend(_Metamorph, { @@ -8250,22 +7898,11 @@ define("ember-handlebars/helpers/each", createChildView: function(view, attrs) { view = this._super(view, attrs); - // At the moment, if a container view subclass wants - // to insert keywords, it is responsible for cloning - // the keywords hash. This will be fixed momentarily. - var keyword = get(this, 'keyword'); var content = get(view, 'content'); + var keyword = get(this, 'keyword'); if (keyword) { - var data = get(view, 'templateData'); - - data = copy(data); - data.keywords = view.cloneKeywords(); - set(view, 'templateData', data); - - // In this case, we do not bind, because the `content` of - // a #each item cannot change. - data.keywords[keyword] = content; + view._keywords[keyword] = content; } // If {{#each}} is looping over an array of controllers, @@ -8290,341 +7927,336 @@ define("ember-handlebars/helpers/each", } }); - // Defeatureify doesn't seem to like nested functions that need to be removed - function _addMetamorphCheck() { - EachView.reopen({ - _checkMetamorph: on('didInsertElement', function() { - }) - }); - } - - // until ember-debug is es6ed - var runInDebug = function(f){ f(); }; - - var GroupedEach = EmberHandlebars.GroupedEach = function(context, path, options) { - var self = this, - normalized = EmberHandlebars.normalizePath(context, path, options.data); - - this.context = context; - this.path = path; - this.options = options; - this.template = options.fn; - this.containingView = options.data.view; - this.normalizedRoot = normalized.root; - this.normalizedPath = normalized.path; - this.content = this.lookupContent(); - - this.addContentObservers(); - this.addArrayObservers(); - - this.containingView.on('willClearRender', function() { - self.destroy(); - }); - }; - - GroupedEach.prototype = { - contentWillChange: function() { - this.removeArrayObservers(); - }, - - contentDidChange: function() { - this.content = this.lookupContent(); - this.addArrayObservers(); - this.rerenderContainingView(); - }, - - contentArrayWillChange: K, - - contentArrayDidChange: function() { - this.rerenderContainingView(); - }, - - lookupContent: function() { - return handlebarsGet(this.normalizedRoot, this.normalizedPath, this.options); - }, - - addArrayObservers: function() { - if (!this.content) { return; } - - this.content.addArrayObserver(this, { - willChange: 'contentArrayWillChange', - didChange: 'contentArrayDidChange' - }); - }, - - removeArrayObservers: function() { - if (!this.content) { return; } - - this.content.removeArrayObserver(this, { - willChange: 'contentArrayWillChange', - didChange: 'contentArrayDidChange' - }); - }, - - addContentObservers: function() { - addBeforeObserver(this.normalizedRoot, this.normalizedPath, this, this.contentWillChange); - addObserver(this.normalizedRoot, this.normalizedPath, this, this.contentDidChange); - }, - - removeContentObservers: function() { - removeBeforeObserver(this.normalizedRoot, this.normalizedPath, this.contentWillChange); - removeObserver(this.normalizedRoot, this.normalizedPath, this.contentDidChange); - }, - - render: function() { - if (!this.content) { return; } - - var content = this.content, - contentLength = get(content, 'length'), - options = this.options, - data = options.data, - template = this.template; - - data.insideEach = true; - for (var i = 0; i < contentLength; i++) { - var context = content.objectAt(i); - options.data.keywords[options.hash.keyword] = context; - template(context, { data: data }); - } - }, - - rerenderContainingView: function() { - var self = this; - run.scheduleOnce('render', this, function() { - // It's possible it's been destroyed after we enqueued a re-render call. - if (!self.destroyed) { - self.containingView.rerender(); - } - }); - }, - - destroy: function() { - this.removeContentObservers(); - if (this.content) { - this.removeArrayObservers(); - } - this.destroyed = true; - } - }; - /** - The `{{#each}}` helper loops over elements in a collection, rendering its - block once for each item. It is an extension of the base Handlebars `{{#each}}` - helper: + The `{{#each}}` helper loops over elements in a collection. It is an extension + of the base Handlebars `{{#each}}` helper. + + The default behavior of `{{#each}}` is to yield its inner block once for every + item in an array. ```javascript - Developers = [{name: 'Yehuda'},{name: 'Tom'}, {name: 'Paul'}]; + var developers = [{name: 'Yehuda'},{name: 'Tom'}, {name: 'Paul'}]; ``` ```handlebars - {{#each Developers}} + {{#each person in developers}} + {{person.name}} + {{! `this` is whatever it was outside the #each }} + {{/each}} + ``` + + The same rules apply to arrays of primitives, but the items may need to be + references with `{{this}}`. + + ```javascript + var developerNames = ['Yehuda', 'Tom', 'Paul'] + ``` + + ```handlebars + {{#each name in developerNames}} {{name}} {{/each}} ``` - `{{each}}` supports an alternative syntax with element naming: - - ```handlebars - {{#each person in Developers}} - {{person.name}} - {{/each}} - ``` - - When looping over objects that do not have properties, `{{this}}` can be used - to render the object: - - ```javascript - DeveloperNames = ['Yehuda', 'Tom', 'Paul'] - ``` - - ```handlebars - {{#each DeveloperNames}} - {{this}} - {{/each}} - ``` ### {{else}} condition + `{{#each}}` can have a matching `{{else}}`. The contents of this block will render if the collection is empty. ``` - {{#each person in Developers}} + {{#each person in developers}} {{person.name}} {{else}}

      Sorry, nobody is available for this task.

      {{/each}} ``` - ### Specifying a View class for items - If you provide an `itemViewClass` option that references a view class - with its own `template` you can omit the block. + + ### Specifying an alternative view for each item + + `itemViewClass` can control which view will be used during the render of each + item's template. The following template: ```handlebars - {{#view App.MyView }} - {{each view.items itemViewClass="App.AnItemView"}} - {{/view}} +
        + {{#each developer in developers itemViewClass="person"}} + {{developer.name}} + {{/each}} +
      ``` - And application code + Will use the following view for each item ```javascript - App = Ember.Application.create({ - MyView: Ember.View.extend({ - items: [ - Ember.Object.create({name: 'Dave'}), - Ember.Object.create({name: 'Mary'}), - Ember.Object.create({name: 'Sara'}) - ] - }) - }); - - App.AnItemView = Ember.View.extend({ - template: Ember.Handlebars.compile("Greetings {{name}}") + App.PersonView = Ember.View.extend({ + tagName: 'li' }); ``` - Will result in the HTML structure below + Resulting in HTML output that looks like the following: ```html -
      -
      Greetings Dave
      -
      Greetings Mary
      -
      Greetings Sara
      -
      +
        +
      • Yehuda
      • +
      • Tom
      • +
      • Paul
      • +
      ``` - If an `itemViewClass` is defined on the helper, and therefore the helper is not - being used as a block, an `emptyViewClass` can also be provided optionally. - The `emptyViewClass` will match the behavior of the `{{else}}` condition - described above. That is, the `emptyViewClass` will render if the collection - is empty. - - ### Representing each item with a Controller. - By default the controller lookup within an `{{#each}}` block will be - the controller of the template where the `{{#each}}` was used. If each - item needs to be presented by a custom controller you can provide a - `itemController` option which references a controller by lookup name. - Each item in the loop will be wrapped in an instance of this controller - and the item itself will be set to the `model` property of that controller. - - This is useful in cases where properties of model objects need transformation - or synthesis for display: + `itemViewClass` also enables a non-block form of `{{each}}`. The view + must {{#crossLink "Ember.View/toc_templates"}}provide its own template{{/crossLink}}, + and then the block should be dropped. An example that outputs the same HTML + as the previous one: ```javascript - App.DeveloperController = Ember.ObjectController.extend({ + App.PersonView = Ember.View.extend({ + tagName: 'li', + template: '{{developer.name}}' + }); + ``` + + ```handlebars +
        + {{each developer in developers itemViewClass="person"}} +
      + ``` + + ### Specifying an alternative view for no items (else) + + The `emptyViewClass` option provides the same flexibility to the `{{else}}` + case of the each helper. + + ```javascript + App.NoPeopleView = Ember.View.extend({ + tagName: 'li', + template: 'No person is available, sorry' + }); + ``` + + ```handlebars +
        + {{#each developer in developers emptyViewClass="no-people"}} +
      • {{developer.name}}
      • + {{/each}} +
      + ``` + + ### Wrapping each item in a controller + + Controllers in Ember manage state and decorate data. In many cases, + providing a controller for each item in a list can be useful. + Specifically, an {{#crossLink "Ember.ObjectController"}}Ember.ObjectController{{/crossLink}} + should probably be used. Item controllers are passed the item they + will present as a `model` property, and an object controller will + proxy property lookups to `model` for us. + + This allows state and decoration to be added to the controller + while any other property lookups are delegated to the model. An example: + + ```javascript + App.RecruitController = Ember.ObjectController.extend({ isAvailableForHire: function() { - return !this.get('model.isEmployed') && this.get('model.isSeekingWork'); + return !this.get('isEmployed') && this.get('isSeekingWork'); }.property('isEmployed', 'isSeekingWork') }) ``` ```handlebars - {{#each person in developers itemController="developer"}} + {{#each person in developers itemController="recruit"}} {{person.name}} {{#if person.isAvailableForHire}}Hire me!{{/if}} {{/each}} ``` - Each itemController will receive a reference to the current controller as - a `parentController` property. - - ### (Experimental) Grouped Each - - When used in conjunction with the experimental [group helper](https://github.com/emberjs/group-helper), - you can inform Handlebars to re-render an entire group of items instead of - re-rendering them one at a time (in the event that they are changed en masse - or an item is added/removed). - - ```handlebars - {{#group}} - {{#each people}} - {{firstName}} {{lastName}} - {{/each}} - {{/group}} - ``` - - This can be faster than the normal way that Handlebars re-renders items - in some cases. - - If for some reason you have a group with more than one `#each`, you can make - one of the collections be updated in normal (non-grouped) fashion by setting - the option `groupedRows=true` (counter-intuitive, I know). - - For example, - - ```handlebars - {{dealershipName}} - - {{#group}} - {{#each dealers}} - {{firstName}} {{lastName}} - {{/each}} - - {{#each car in cars groupedRows=true}} - {{car.make}} {{car.model}} {{car.color}} - {{/each}} - {{/group}} - ``` - Any change to `dealershipName` or the `dealers` collection will cause the - entire group to be re-rendered. However, changes to the `cars` collection - will be re-rendered individually (as normal). - - Note that `group` behavior is also disabled by specifying an `itemViewClass`. - @method each @for Ember.Handlebars.helpers @param [name] {String} name for item (used with `in`) @param [path] {String} path @param [options] {Object} Handlebars key/value pairs of options @param [options.itemViewClass] {String} a path to a view class used for each item + @param [options.emptyViewClass] {String} a path to a view class used for each item @param [options.itemController] {String} name of a controller to be created for each item - @param [options.groupedRows] {boolean} enable normal item-by-item rendering when inside a `#group` helper */ - function eachHelper(path, options) { - var ctx, helperName = 'each'; + function eachHelper(path) { + var options = arguments[arguments.length - 1]; + var helperName = 'each'; + var keywordName; if (arguments.length === 4) { - var keywordName = arguments[0]; - - - options = arguments[3]; + keywordName = arguments[0]; path = arguments[2]; helperName += ' ' + keywordName + ' in ' + path; - if (path === '') { path = "this"; } - options.hash.keyword = keywordName; - } else if (arguments.length === 1) { - options = path; - path = 'this'; + path = ''; } else { helperName += ' ' + path; } + + options.hash.emptyViewClass = Ember._MetamorphView; options.hash.dataSourceBinding = path; - // Set up emptyView as a metamorph with no tag - //options.hash.emptyViewClass = Ember._MetamorphView; - - // can't rely on this default behavior when use strict - ctx = this || window; - + options.hashTypes.dataSourceBinding = 'STRING'; options.helperName = options.helperName || helperName; - if (options.data.insideGroup && !options.hash.groupedRows && !options.hash.itemViewClass) { - new GroupedEach(ctx, path, options).render(); - } else { - // ES6TODO: figure out how to do this without global lookup. - return helpers.collection.call(ctx, 'Ember.Handlebars.EachView', options); - } + return EmberHandlebars.helpers.collection.call(this, EmberHandlebars.EachView, options); } __exports__.EachView = EachView; - __exports__.GroupedEach = GroupedEach; __exports__.eachHelper = eachHelper; }); -define("ember-handlebars/helpers/loc", +enifed("ember-handlebars/helpers/if_unless", + ["ember-metal/core","ember-handlebars-compiler","ember-handlebars/helpers/binding","ember-metal/property_get","ember-metal/utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-handlebars + */ + + var Ember = __dependency1__["default"]; + // Ember.assert + var EmberHandlebars = __dependency2__["default"]; + + var bind = __dependency3__.bind; + + var get = __dependency4__.get; + var isArray = __dependency5__.isArray; + + var helpers = EmberHandlebars.helpers; + + function shouldDisplayIfHelperContent(result) { + var truthy = result && get(result, 'isTruthy'); + if (typeof truthy === 'boolean') { return truthy; } + + if (isArray(result)) { + return get(result, 'length') !== 0; + } else { + return !!result; + } + } + + /** + Use the `boundIf` helper to create a conditional that re-evaluates + whenever the truthiness of the bound value changes. + + ```handlebars + {{#boundIf "content.shouldDisplayTitle"}} + {{content.title}} + {{/boundIf}} + ``` + + @private + @method boundIf + @for Ember.Handlebars.helpers + @param {String} property Property to bind + @param {Function} fn Context to provide for rendering + @return {String} HTML string + */ + function boundIfHelper(property, fn) { + var context = (fn.contexts && fn.contexts.length) ? fn.contexts[0] : this; + + fn.helperName = fn.helperName || 'boundIf'; + + return bind.call(context, property, fn, true, shouldDisplayIfHelperContent, shouldDisplayIfHelperContent, [ + 'isTruthy', + 'length' + ]); + } + + /** + @private + + Use the `unboundIf` helper to create a conditional that evaluates once. + + ```handlebars + {{#unboundIf "content.shouldDisplayTitle"}} + {{content.title}} + {{/unboundIf}} + ``` + + @method unboundIf + @for Ember.Handlebars.helpers + @param {String} property Property to bind + @param {Function} fn Context to provide for rendering + @return {String} HTML string + @since 1.4.0 + */ + function unboundIfHelper(property, fn) { + var context = (fn.contexts && fn.contexts.length) ? fn.contexts[0] : this; + var data = fn.data; + var view = data.view; + var template = fn.fn; + var inverse = fn.inverse; + + var propertyValue = view.getStream(property).value(); + + if (!shouldDisplayIfHelperContent(propertyValue)) { + template = inverse; + } + + template(context, { data: data }); + } + + /** + See [boundIf](/api/classes/Ember.Handlebars.helpers.html#method_boundIf) + and [unboundIf](/api/classes/Ember.Handlebars.helpers.html#method_unboundIf) + + @method if + @for Ember.Handlebars.helpers + @param {Function} context + @param {Hash} options + @return {String} HTML string + */ + function ifHelper(context, options) { + + options.helperName = options.helperName || ('if ' + context); + + if (options.data.isUnbound) { + return helpers.unboundIf.call(options.contexts[0], context, options); + } else { + return helpers.boundIf.call(options.contexts[0], context, options); + } + } + + /** + @method unless + @for Ember.Handlebars.helpers + @param {Function} context + @param {Hash} options + @return {String} HTML string + */ + function unlessHelper(context, options) { + + var fn = options.fn; + var inverse = options.inverse; + var helperName = 'unless'; + + if (context) { + helperName += ' ' + context; + } + + options.fn = inverse; + options.inverse = fn; + + options.helperName = options.helperName || helperName; + + if (options.data.isUnbound) { + return helpers.unboundIf.call(options.contexts[0], context, options); + } else { + return helpers.boundIf.call(options.contexts[0], context, options); + } + } + + __exports__.ifHelper = ifHelper; + __exports__.boundIfHelper = boundIfHelper; + __exports__.unboundIfHelper = unboundIfHelper; + __exports__.unlessHelper = unlessHelper; + }); +enifed("ember-handlebars/helpers/loc", ["ember-runtime/system/string","exports"], function(__dependency1__, __exports__) { "use strict"; @@ -8635,26 +8267,31 @@ define("ember-handlebars/helpers/loc", @submodule ember-handlebars */ - // ES6TODO: - // Pretty sure this can be expressed as - // var locHelper EmberStringUtils.loc ? - /** Calls [Ember.String.loc](/api/classes/Ember.String.html#method_loc) with the provided string. - This is a convenient way to localize text. For example: + This is a convenient way to localize text within a template: - ```html - + ```javascript + Ember.STRINGS = { + '_welcome_': 'Bonjour' + }; ``` - Take note that `"welcome"` is a string and not an object - reference. + ```handlebars +
      + {{loc '_welcome_'}} +
      + ``` - See [Ember.String.loc](/api/classes/Ember.String.html#method_loc) for how to + ```html +
      + Bonjour +
      + ``` + + See [Ember.String.loc](/api/classes/Ember.String.html#method_loc) for how to set up localized string references. @method loc @@ -8662,21 +8299,18 @@ define("ember-handlebars/helpers/loc", @param {String} str The string to format @see {Ember.String#loc} */ - __exports__["default"] = function locHelper(str) { - return loc(str); - } + __exports__["default"] = loc; }); -define("ember-handlebars/helpers/partial", - ["ember-metal/core","ember-metal/is_none","ember-handlebars/ext","ember-handlebars/helpers/binding","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { +enifed("ember-handlebars/helpers/partial", + ["ember-metal/core","ember-metal/is_none","ember-handlebars/helpers/binding","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { "use strict"; var Ember = __dependency1__["default"]; // Ember.assert // var emberAssert = Ember.assert; - var isNone = __dependency2__.isNone; - var handlebarsGet = __dependency3__.handlebarsGet; - var bind = __dependency4__.bind; + var isNone = __dependency2__["default"]; + var bind = __dependency3__.bind; /** @module ember @@ -8717,16 +8351,6 @@ define("ember-handlebars/helpers/partial", changes, the partial will be re-rendered using the new template name. - ## Setting the partial's context with `with` - - The `partial` helper can be used in conjunction with the `with` - helper to set a context that will be used by the partial: - - ```handlebars - {{#with currentUser}} - {{partial "user_info"}} - {{/with}} - ``` @method partial @for Ember.Handlebars.helpers @@ -8734,18 +8358,19 @@ define("ember-handlebars/helpers/partial", */ __exports__["default"] = function partialHelper(name, options) { + var view = options.data.view; var context = (options.contexts && options.contexts.length) ? options.contexts[0] : this; options.helperName = options.helperName || 'partial'; if (options.types[0] === "ID") { + var partialNameStream = view.getStream(name); // Helper was passed a property path; we need to // create a binding that will re-render whenever // this property changes. options.fn = function(context, fnOptions) { - var partialName = handlebarsGet(context, name, fnOptions); - renderPartial(context, partialName, fnOptions); + renderPartial(context, partialNameStream.value(), fnOptions); }; return bind.call(context, name, options, true, exists); @@ -8773,80 +8398,25 @@ define("ember-handlebars/helpers/partial", template = template || deprecatedTemplate; - template(context, { data: options.data }); + template(context, { + data: options.data + }); } }); -define("ember-handlebars/helpers/shared", - ["ember-handlebars/ext","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var handlebarsGet = __dependency1__.handlebarsGet; - - __exports__["default"] = function resolvePaths(options) { - var ret = [], - contexts = options.contexts, - roots = options.roots, - data = options.data; - - for (var i=0, l=contexts.length; i - {{#with loggedInUser}} - Last Login: {{lastLogin}} - User Info: {{template "user_info"}} - {{/with}} - - ``` - - ```html - - ``` - - ```handlebars - {{#if isUser}} - {{template "user_info"}} - {{else}} - {{template "unlogged_user_info"}} - {{/if}} - ``` - - This helper looks for templates in the global `Ember.TEMPLATES` hash. If you - add `"; - return testEl.firstChild.innerHTML === ''; - })(); - - // IE 8 (and likely earlier) likes to move whitespace preceeding - // a script tag to appear after it. This means that we can - // accidentally remove whitespace when updating a morph. - var movesWhitespace = typeof document !== 'undefined' && (function() { - var testEl = document.createElement('div'); - testEl.innerHTML = "Test: Value"; - return testEl.childNodes[0].nodeValue === 'Test:' && - testEl.childNodes[2].nodeValue === ' Value'; - })(); - - // Use this to find children by ID instead of using jQuery - var findChildById = function(element, id) { - if (element.getAttribute('id') === id) { return element; } - - var len = element.childNodes.length, idx, node, found; - for (idx=0; idx 0) { - var len = matches.length, idx; - for (idx=0; idxTest'); - canSet = el.options.length === 1; - } - - innerHTMLTags[tagName] = canSet; - - return canSet; - }; - - function setInnerHTML(element, html) { - var tagName = element.tagName; - - if (canSetInnerHTML(tagName)) { - setInnerHTMLWithoutFix(element, html); - } else { - // Firefox versions < 11 do not have support for element.outerHTML. - var outerHTML = element.outerHTML || new XMLSerializer().serializeToString(element); - - var startTag = outerHTML.match(new RegExp("<"+tagName+"([^>]*)>", 'i'))[0], - endTag = ''; - - var wrapper = document.createElement('div'); - setInnerHTMLWithoutFix(wrapper, startTag + html + endTag); - element = wrapper.firstChild; - while (element.tagName !== tagName) { - element = element.nextSibling; - } - } - - return element; - } - - __exports__.setInnerHTML = setInnerHTML;function isSimpleClick(event) { - var modifier = event.shiftKey || event.metaKey || event.altKey || event.ctrlKey, - secondaryClick = event.which > 1; // IE9 may return undefined + function isSimpleClick(event) { + var modifier = event.shiftKey || event.metaKey || event.altKey || event.ctrlKey; + var secondaryClick = event.which > 1; // IE9 may return undefined return !modifier && !secondaryClick; } - __exports__.isSimpleClick = isSimpleClick; + __exports__.isSimpleClick = isSimpleClick;/** + @private + @method getViewRange + @param {Ember.View} view + */ + function getViewRange(view) { + var range = document.createRange(); + range.setStartAfter(view._morph.start); + range.setEndBefore(view._morph.end); + return range; + } + + /** + `getViewClientRects` provides information about the position of the border + box edges of a view relative to the viewport. + + It is only intended to be used by development tools like the Ember Inpsector + and may not work on older browsers. + + @private + @method getViewClientRects + @param {Ember.View} view + */ + function getViewClientRects(view) { + var range = getViewRange(view); + return range.getClientRects(); + } + + __exports__.getViewClientRects = getViewClientRects;/** + `getViewBoundingClientRect` provides information about the position of the + bounding border box edges of a view relative to the viewport. + + It is only intended to be used by development tools like the Ember Inpsector + and may not work on older browsers. + + @private + @method getViewBoundingClientRect + @param {Ember.View} view + */ + function getViewBoundingClientRect(view) { + var range = getViewRange(view); + return range.getBoundingClientRect(); + } + + __exports__.getViewBoundingClientRect = getViewBoundingClientRect; }); -define("ember-views/views/collection_view", - ["ember-metal/core","ember-metal/platform","ember-metal/binding","ember-metal/merge","ember-metal/property_get","ember-metal/property_set","ember-runtime/system/string","ember-views/views/container_view","ember-views/views/core_view","ember-views/views/view","ember-metal/mixin","ember-runtime/mixins/array","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __exports__) { +enifed("ember-views/views/collection_view", + ["ember-metal/core","ember-metal/binding","ember-metal/property_get","ember-metal/property_set","ember-runtime/system/string","ember-views/views/container_view","ember-views/views/core_view","ember-views/views/view","ember-metal/mixin","ember-views/streams/read","ember-runtime/mixins/array","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __exports__) { "use strict"; /** @@ -36362,18 +38263,17 @@ define("ember-views/views/collection_view", var Ember = __dependency1__["default"]; // Ember.assert - var create = __dependency2__.create; - var isGlobalPath = __dependency3__.isGlobalPath; - var merge = __dependency4__["default"]; - var get = __dependency5__.get; - var set = __dependency6__.set; - var fmt = __dependency7__.fmt; - var ContainerView = __dependency8__["default"]; - var CoreView = __dependency9__["default"]; - var View = __dependency10__["default"]; - var observer = __dependency11__.observer; - var beforeObserver = __dependency11__.beforeObserver; - var EmberArray = __dependency12__["default"]; + var isGlobalPath = __dependency2__.isGlobalPath; + var get = __dependency3__.get; + var set = __dependency4__.set; + var fmt = __dependency5__.fmt; + var ContainerView = __dependency6__["default"]; + var CoreView = __dependency7__["default"]; + var View = __dependency8__["default"]; + var observer = __dependency9__.observer; + var beforeObserver = __dependency9__.beforeObserver; + var readViewFactory = __dependency10__.readViewFactory; + var EmberArray = __dependency11__["default"]; /** `Ember.CollectionView` is an `Ember.View` descendent responsible for managing @@ -36396,27 +38296,32 @@ define("ember-views/views/collection_view", The view for each item in the collection will have its `content` property set to the item. - ## Specifying itemViewClass + ## Specifying `itemViewClass` By default the view class for each item in the managed collection will be an instance of `Ember.View`. You can supply a different class by setting the `CollectionView`'s `itemViewClass` property. - Given an empty `` and the following code: + Given the following application code: ```javascript - someItemsView = Ember.CollectionView.create({ + var App = Ember.Application.create(); + App.ItemListView = Ember.CollectionView.extend({ classNames: ['a-collection'], content: ['A','B','C'], itemViewClass: Ember.View.extend({ template: Ember.Handlebars.compile("the letter: {{view.content}}") }) }); - - someItemsView.appendTo('body'); ``` - Will result in the following HTML structure + And a simple application template: + + ```handlebars + {{view 'item-list'}} + ``` + + The following HTML will result: ```html
      @@ -36432,21 +38337,26 @@ define("ember-views/views/collection_view", "ul", "ol", "table", "thead", "tbody", "tfoot", "tr", or "select" will result in the item views receiving an appropriately matched `tagName` property. - Given an empty `` and the following code: + Given the following application code: ```javascript - anUnorderedListView = Ember.CollectionView.create({ + var App = Ember.Application.create(); + App.UnorderedListView = Ember.CollectionView.create({ tagName: 'ul', content: ['A','B','C'], itemViewClass: Ember.View.extend({ template: Ember.Handlebars.compile("the letter: {{view.content}}") }) }); - - anUnorderedListView.appendTo('body'); ``` - Will result in the following HTML structure + And a simple application template: + + ```handlebars + {{view 'unordered-list-view'}} + ``` + + The following HTML will result: ```html
        @@ -36457,7 +38367,7 @@ define("ember-views/views/collection_view", ``` Additional `tagName` pairs can be provided by adding to - `Ember.CollectionView.CONTAINER_MAP ` + `Ember.CollectionView.CONTAINER_MAP`. For example: ```javascript Ember.CollectionView.CONTAINER_MAP['article'] = 'section' @@ -36470,7 +38380,7 @@ define("ember-views/views/collection_view", `createChildView` method can be overidden: ```javascript - CustomCollectionView = Ember.CollectionView.extend({ + App.CustomCollectionView = Ember.CollectionView.extend({ createChildView: function(viewClass, attrs) { if (attrs.content.kind == 'album') { viewClass = App.AlbumView; @@ -36490,18 +38400,23 @@ define("ember-views/views/collection_view", will be the `CollectionView`s only child. ```javascript - aListWithNothing = Ember.CollectionView.create({ - classNames: ['nothing'] + var App = Ember.Application.create(); + App.ListWithNothing = Ember.CollectionView.create({ + classNames: ['nothing'], content: null, emptyView: Ember.View.extend({ template: Ember.Handlebars.compile("The collection is empty") }) }); - - aListWithNothing.appendTo('body'); ``` - Will result in the following HTML structure + And a simple application template: + + ```handlebars + {{view 'list-with-nothing'}} + ``` + + The following HTML will result: ```html
        @@ -36658,18 +38573,8 @@ define("ember-views/views/collection_view", // Loop through child views that correspond with the removed items. // Note that we loop from the end of the array to the beginning because // we are mutating it as we go. - var childViews = this._childViews, childView, idx, len; - - len = this._childViews.length; - - var removingAll = removedCount === len; - - if (removingAll) { - this.currentState.empty(this); - this.invokeRecursively(function(view) { - view.removedFromDOM = true; - }, false); - } + var childViews = this._childViews; + var childView, idx; for (idx = start + removedCount - 1; idx >= start; idx--) { childView = childViews[idx]; @@ -36692,26 +38597,24 @@ define("ember-views/views/collection_view", @param {Number} added number of object added to content */ arrayDidChange: function(content, start, removed, added) { - var addedViews = [], view, item, idx, len, itemViewClass, - emptyView; + var addedViews = []; + var view, item, idx, len, itemViewClass, emptyView, itemViewProps; len = content ? get(content, 'length') : 0; if (len) { + itemViewProps = this._itemViewProps || {}; itemViewClass = get(this, 'itemViewClass'); - if ('string' === typeof itemViewClass && isGlobalPath(itemViewClass)) { - itemViewClass = get(itemViewClass) || itemViewClass; - } + itemViewClass = readViewFactory(itemViewClass, this.container); - for (idx = start; idx < start+added; idx++) { item = content.objectAt(idx); - view = this.createChildView(itemViewClass, { - content: item, - contentIndex: idx - }); + itemViewProps.content = item; + itemViewProps.contentIndex = idx; + + view = this.createChildView(itemViewClass, itemViewProps); addedViews.push(view); } @@ -36725,6 +38628,7 @@ define("ember-views/views/collection_view", } emptyView = this.createChildView(emptyView); + addedViews.push(emptyView); set(this, 'emptyView', emptyView); @@ -36788,7 +38692,7 @@ define("ember-views/views/collection_view", __exports__["default"] = CollectionView; }); -define("ember-views/views/component", +enifed("ember-views/views/component", ["ember-metal/core","ember-views/mixins/component_template_deprecation","ember-runtime/mixins/target_action_support","ember-views/views/view","ember-metal/property_get","ember-metal/property_set","ember-metal/is_none","ember-metal/computed","exports"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) { "use strict"; @@ -36801,7 +38705,7 @@ define("ember-views/views/component", var get = __dependency5__.get; var set = __dependency6__.set; - var isNone = __dependency7__.isNone; + var isNone = __dependency7__["default"]; var computed = __dependency8__.computed; @@ -36907,7 +38811,6 @@ define("ember-views/views/component", init: function() { this._super(); - set(this, 'origContext', get(this, 'context')); set(this, 'context', this); set(this, 'controller', this); }, @@ -36938,8 +38841,8 @@ define("ember-views/views/component", template: computed(function(key, value) { if (value !== undefined) { return value; } - var templateName = get(this, 'templateName'), - template = this.templateForName(templateName, 'template'); + var templateName = get(this, 'templateName'); + var template = this.templateForName(templateName, 'template'); return template || get(this, 'defaultTemplate'); @@ -36954,18 +38857,14 @@ define("ember-views/views/component", */ templateName: null, - // during render, isolate keywords - cloneKeywords: function() { - return { - view: this, - controller: this - }; + _setupKeywords: function() { + this._keywords.view.setSource(this); }, _yield: function(context, options) { - var view = options.data.view, - parentView = this._parentView, - template = get(this, 'template'); + var view = options.data.view; + var parentView = this._parentView; + var template = get(this, 'template'); if (template) { @@ -36974,9 +38873,9 @@ define("ember-views/views/component", tagName: '', _contextView: parentView, template: template, - context: options.data.insideGroup ? get(this, 'origContext') : get(parentView, 'context'), + context: get(parentView, 'context'), controller: get(parentView, 'controller'), - templateData: { keywords: parentView.cloneKeywords(), insideGroup: options.data.insideGroup } + templateData: { keywords: {} } }); } }, @@ -37075,8 +38974,8 @@ define("ember-views/views/component", @param [context] {*} a context to send with the action */ sendAction: function(action) { - var actionName, - contexts = a_slice.call(arguments, 1); + var actionName; + var contexts = a_slice.call(arguments, 1); // Send the default action if (action === undefined) { @@ -37092,14 +38991,36 @@ define("ember-views/views/component", action: actionName, actionContext: contexts }); + }, + + send: function(actionName) { + var args = [].slice.call(arguments, 1); + var target; + var hasAction = this._actions && this._actions[actionName]; + + if (hasAction) { + if (this._actions[actionName].apply(this, args) === true) { + // handler returned true, so this action will bubble + } else { + return; + } + } + + if (target = get(this, 'target')) { + target.send.apply(target, arguments); + } else { + if (!hasAction) { + throw new Error(Ember.inspect(this) + ' had no action handler for: ' + actionName); + } + } } }); __exports__["default"] = Component; }); -define("ember-views/views/container_view", - ["ember-metal/core","ember-metal/merge","ember-runtime/mixins/mutable_array","ember-metal/property_get","ember-metal/property_set","ember-views/views/view","ember-views/views/view_collection","ember-views/views/states","ember-metal/error","ember-metal/enumerable_utils","ember-metal/computed","ember-metal/run_loop","ember-metal/properties","ember-views/system/render_buffer","ember-metal/mixin","ember-runtime/system/native_array","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __exports__) { +enifed("ember-views/views/container_view", + ["ember-metal/core","ember-metal/merge","ember-runtime/mixins/mutable_array","ember-metal/property_get","ember-metal/property_set","ember-views/views/view","ember-views/views/states","ember-metal/error","ember-metal/enumerable_utils","ember-metal/computed","ember-metal/run_loop","ember-metal/properties","ember-metal/mixin","ember-runtime/system/native_array","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __exports__) { "use strict"; var Ember = __dependency1__["default"]; // Ember.assert, Ember.K @@ -37110,22 +39031,20 @@ define("ember-views/views/container_view", var set = __dependency5__.set; var View = __dependency6__["default"]; - var ViewCollection = __dependency7__["default"]; - var cloneStates = __dependency8__.cloneStates; - var EmberViewStates = __dependency8__.states; + var cloneStates = __dependency7__.cloneStates; + var EmberViewStates = __dependency7__.states; - var EmberError = __dependency9__["default"]; + var EmberError = __dependency8__["default"]; - var forEach = __dependency10__.forEach; + var forEach = __dependency9__.forEach; - var computed = __dependency11__.computed; - var run = __dependency12__["default"]; - var defineProperty = __dependency13__.defineProperty; - var renderBuffer = __dependency14__["default"]; - var observer = __dependency15__.observer; - var beforeObserver = __dependency15__.beforeObserver; - var emberA = __dependency16__.A; + var computed = __dependency10__.computed; + var run = __dependency11__["default"]; + var defineProperty = __dependency12__.defineProperty; + var observer = __dependency13__.observer; + var beforeObserver = __dependency13__.beforeObserver; + var emberA = __dependency14__.A; /** @module ember @@ -37294,7 +39213,7 @@ define("ember-views/views/container_view", this._super(); var childViews = get(this, 'childViews'); - + // redefine view's childViews property that was obliterated defineProperty(this, 'childViews', View.childViewsProperty); @@ -37358,9 +39277,18 @@ define("ember-views/views/container_view", @param {Ember.RenderBuffer} buffer the buffer to render to */ render: function(buffer) { - this.forEachChildView(function(view) { - view.renderToBuffer(buffer); - }); + var element = buffer.element(); + var dom = buffer.dom; + + if (this.tagName === '') { + element = dom.createDocumentFragment(); + buffer._element = element; + this._childViewsMorph = dom.appendMorph(element, this._morph.contextualElement); + } else { + this._childViewsMorph = dom.createMorph(element, element.lastChild, null); + } + + return element; }, instrumentName: 'container', @@ -37469,7 +39397,9 @@ define("ember-views/views/container_view", merge(states.hasElement, { childViewsWillChange: function(view, views, start, removed) { for (var i=start; i=lengthBefore; i--) { - if (childViews[i]) { childViews[i].destroy(); } - } - }, - /** Iterates over the view's `classNameBindings` array, inserts the value of the specified property into the `classNames` array, then creates an @@ -39274,27 +40968,37 @@ define("ember-views/views/view", @private */ _applyClassNameBindings: function(classBindings) { - var classNames = this.classNames, - elem, newClass, dasherizedClass; + var classNames = this.classNames; + var elem, newClass, dasherizedClass; // Loop through all of the configured bindings. These will be either // property names ('isUrgent') or property paths relative to the view // ('content.isUrgent') forEach(classBindings, function(binding) { - + var parsedPath; + + if (typeof binding === 'string') { + parsedPath = View._parsePropertyPath(binding); + if (parsedPath.path === '') { + parsedPath.stream = new SimpleStream(true); + } else { + parsedPath.stream = this.getStream('_view.' + parsedPath.path); + } + } else { + parsedPath = binding; + } + // Variable in which the old class value is saved. The observer function // closes over this variable, so it knows which string to remove when // the property changes. var oldClass; - // Extract just the property name from bindings like 'foo:bar' - var parsedPath = View._parsePropertyPath(binding); // Set up an observer on the context. If the property changes, toggle the // class name. - var observer = function() { + var observer = this._wrapAsScheduled(function() { // Get the current value of the property - newClass = this._classStringForProperty(binding); + newClass = this._classStringForProperty(parsedPath); elem = this.$(); // If we had previously added a class to the element, remove it. @@ -39313,10 +41017,10 @@ define("ember-views/views/view", } else { oldClass = null; } - }; + }); // Get the class name for the property at its current value - dasherizedClass = this._classStringForProperty(binding); + dasherizedClass = this._classStringForProperty(parsedPath); if (dasherizedClass) { // Ensure that it gets into the classNames array @@ -39329,7 +41033,7 @@ define("ember-views/views/view", oldClass = dasherizedClass; } - this.registerObserver(this, parsedPath.path, observer); + parsedPath.stream.subscribe(observer, this); // Remove className so when the view is rerendered, // the className is added based on binding reevaluation this.one('willClearRender', function() { @@ -39353,14 +41057,15 @@ define("ember-views/views/view", @private */ _applyAttributeBindings: function(buffer, attributeBindings) { - var attributeValue, - unspecifiedAttributeBindings = this._unspecifiedAttributeBindings = this._unspecifiedAttributeBindings || {}; + var attributeValue; + var unspecifiedAttributeBindings = this._unspecifiedAttributeBindings = this._unspecifiedAttributeBindings || {}; forEach(attributeBindings, function(binding) { - var split = binding.split(':'), - property = split[0], - attributeName = split[1] || property; + var split = binding.split(':'); + var property = split[0]; + var attributeName = split[1] || property; + if (property in this) { this._setupAttributeBindingObservation(property, attributeName); @@ -39427,16 +41132,8 @@ define("ember-views/views/view", @param property @private */ - _classStringForProperty: function(property) { - var parsedPath = View._parsePropertyPath(property); - var path = parsedPath.path; - - var val = get(this, path); - if (val === undefined && isGlobalPath(path)) { - val = get(Ember.lookup, path); - } - - return View._classStringForValue(path, val, parsedPath.className, parsedPath.falsyClassName); + _classStringForProperty: function(parsedPath) { + return View._classStringForValue(parsedPath.path, parsedPath.stream.value(), parsedPath.className, parsedPath.falsyClassName); }, // .......................................................... @@ -39449,13 +41146,7 @@ define("ember-views/views/view", @property element @type DOMElement */ - element: computed('_parentView', function(key, value) { - if (value !== undefined) { - return this.currentState.setElement(this, value); - } else { - return this.currentState.getElement(this); - } - }), + element: null, /** Returns a jQuery object for this view's element. If you pass in a selector @@ -39474,9 +41165,9 @@ define("ember-views/views/view", }, mutateChildViews: function(callback) { - var childViews = this._childViews, - idx = childViews.length, - view; + var childViews = this._childViews; + var idx = childViews.length; + var view; while(--idx >= 0) { view = childViews[idx]; @@ -39491,8 +41182,8 @@ define("ember-views/views/view", if (!childViews) { return this; } - var len = childViews.length, - view, idx; + var len = childViews.length; + var view, idx; for (idx = 0; idx < len; idx++) { view = childViews[idx]; @@ -39522,12 +41213,11 @@ define("ember-views/views/view", @param {String|DOMElement|jQuery} A selector, element, HTML string, or jQuery object @return {Ember.View} receiver */ - appendTo: function(target) { - // Schedule the DOM element to be created and appended to the given - // element after bindings have synchronized. - this._insertElementLater(function() { - this.$().appendTo(target); - }); + appendTo: function(selector) { + var target = jQuery(selector); + + + this.constructor.renderer.appendTo(this, target[0]); return this; }, @@ -39545,47 +41235,15 @@ define("ember-views/views/view", @param {String|DOMElement|jQuery} target A selector, element, HTML string, or jQuery object @return {Ember.View} received */ - replaceIn: function(target) { + replaceIn: function(selector) { + var target = jQuery(selector); + - this._insertElementLater(function() { - jQuery(target).empty(); - this.$().appendTo(target); - }); + this.constructor.renderer.replaceIn(this, target[0]); return this; }, - /** - Schedules a DOM operation to occur during the next render phase. This - ensures that all bindings have finished synchronizing before the view is - rendered. - - To use, pass a function that performs a DOM operation. - - Before your function is called, this view and all child views will receive - the `willInsertElement` event. After your function is invoked, this view - and all of its child views will receive the `didInsertElement` event. - - ```javascript - view._insertElementLater(function() { - this.createElement(); - this.$().appendTo('body'); - }); - ``` - - @method _insertElementLater - @param {Function} fn the function that inserts the element into the DOM - @private - */ - _insertElementLater: function(fn) { - this._scheduledInsert = run.scheduleOnce('render', this, '_insertElement', fn); - }, - - _insertElement: function (fn) { - this._scheduledInsert = null; - this.currentState.insertElement(this, fn); - }, - /** Appends the view's element to the document body. If the view does not have an HTML representation yet, `createElement()` will be called @@ -39619,9 +41277,6 @@ define("ember-views/views/view", // In the interim, we will just re-render if that happens. It is more // important than elements get garbage collected. if (!this.removedFromDOM) { this.destroyElement(); } - this.invokeRecursively(function(view) { - if (view.clearRenderedChildren) { view.clearRenderedChildren(); } - }); }, elementId: null, @@ -39643,20 +41298,20 @@ define("ember-views/views/view", }, /** - Creates a DOM representation of the view and all of its - child views by recursively calling the `render()` method. + Creates a DOM representation of the view and all of its child views by + recursively calling the `render()` method. - After the element has been created, `didInsertElement` will + After the element has been inserted into the DOM, `didInsertElement` will be called on this view and all of its child views. @method createElement @return {Ember.View} receiver */ createElement: function() { - if (get(this, 'element')) { return this; } + if (this.element) { return this; } - var buffer = this.renderToBuffer(); - set(this, 'element', buffer.element()); + this._didCreateElementWithoutMorph = true; + this.constructor.renderer.renderTree(this); return this; }, @@ -39673,6 +41328,9 @@ define("ember-views/views/view", or after the view was re-rendered. Override this function to do any set up that requires an element in the document body. + When a view has children, didInsertElement will be called on the + child view(s) first, bubbling upwards through the hierarchy. + @event didInsertElement */ didInsertElement: Ember.K, @@ -39686,65 +41344,6 @@ define("ember-views/views/view", */ willClearRender: Ember.K, - /** - Run this callback on the current view (unless includeSelf is false) and recursively on child views. - - @method invokeRecursively - @param fn {Function} - @param includeSelf {Boolean} Includes itself if true. - @private - */ - invokeRecursively: function(fn, includeSelf) { - var childViews = (includeSelf === false) ? this._childViews : [this]; - var currentViews, view, currentChildViews; - - while (childViews.length) { - currentViews = childViews.slice(); - childViews = []; - - for (var i=0, l=currentViews.length; i=0; i--) { - childViews[i].removedFromDOM = true; - } - // remove from non-virtual parent view if viewName was specified if (viewName && nonVirtualParentView) { nonVirtualParentView.set(viewName, null); } - childLen = childViews.length; - for (i=childLen-1; i>=0; i--) { - childViews[i].destroy(); - } - return this; }, @@ -40187,16 +41743,18 @@ define("ember-views/views/view", _toggleVisibility: function() { var $el = this.$(); - if (!$el) { return; } - var isVisible = get(this, 'isVisible'); if (this._isVisible === isVisible) { return ; } - $el.toggle(isVisible); - + // It's important to keep these in sync, even if we don't yet have + // an element in the DOM to manipulate: this._isVisible = isVisible; + if (!$el) { return; } + + $el.toggle(isVisible); + if (this._isAncestorHidden()) { return; } if (isVisible) { @@ -40240,28 +41798,16 @@ define("ember-views/views/view", return false; }, - - clearBuffer: function() { - this.invokeRecursively(nullViewsBuffer); - }, transitionTo: function(state, children) { this._transitionTo(state, children); }, _transitionTo: function(state, children) { var priorState = this.currentState; var currentState = this.currentState = this._states[state]; - this._state = state; if (priorState && priorState.exit) { priorState.exit(this); } if (currentState.enter) { currentState.enter(this); } - if (state === 'inDOM') { meta(this).cache.element = undefined; } - - if (children !== false) { - this.forEachChildView(function(view) { - view._transitionTo(state); - }); - } }, // ....................................................... @@ -40290,22 +41836,78 @@ define("ember-views/views/view", return; } - var view = this, - stateCheckedObserver = function() { - view.currentState.invokeObserver(this, observer); - }, - scheduledObserver = function() { - run.scheduleOnce('render', this, stateCheckedObserver); - }; + var scheduledObserver = this._wrapAsScheduled(observer); addObserver(root, path, target, scheduledObserver); this.one('willClearRender', function() { removeObserver(root, path, target, scheduledObserver); }); - } + }, + _wrapAsScheduled: function(fn) { + var view = this; + var stateCheckedFn = function() { + view.currentState.invokeObserver(this, fn); + }; + var scheduledFn = function() { + run.scheduleOnce('render', this, stateCheckedFn); + }; + return scheduledFn; + }, + + getStream: function(path) { + return this._getContextStream().get(path); + }, + + _getBindingForStream: function(path) { + if (this._streamBindings === undefined) { + this._streamBindings = create(null); + this.one('willDestroyElement', this, this._destroyStreamBindings); + } + + if (this._streamBindings[path] !== undefined) { + return this._streamBindings[path]; + } else { + var stream = this._getContextStream().get(path); + return this._streamBindings[path] = new StreamBinding(stream); + } + }, + + _destroyStreamBindings: function() { + var streamBindings = this._streamBindings; + for (var path in streamBindings) { + streamBindings[path].destroy(); + } + this._streamBindings = undefined; + }, + + _getContextStream: function() { + if (this._contextStream === undefined) { + this._baseContext = new KeyStream(this, 'context'); + this._contextStream = new ContextStream(this); + this.one('willDestroyElement', this, this._destroyContextStream); + } + + return this._contextStream; + }, + + _destroyContextStream: function() { + this._baseContext.destroy(); + this._baseContext = undefined; + this._contextStream.destroy(); + this._contextStream = undefined; + }, + + _unsubscribeFromStreamBindings: function() { + for (var key in this._streamBindingSubscriptions) { + var streamBinding = this[key + 'Binding']; + var callback = this._streamBindingSubscriptions[key]; + streamBinding.unsubscribe(callback); + } + } }); + deprecateProperty(View.prototype, 'state', '_state'); deprecateProperty(View.prototype, 'states', '_states'); @@ -40336,52 +41938,6 @@ define("ember-views/views/view", // once the view has been inserted into the DOM, legal manipulations // are done on the DOM element. - function notifyMutationListeners() { - run.once(View, 'notifyMutationListeners'); - } - - var DOMManager = { - prepend: function(view, html) { - view.$().prepend(html); - notifyMutationListeners(); - }, - - after: function(view, html) { - view.$().after(html); - notifyMutationListeners(); - }, - - html: function(view, html) { - view.$().html(html); - notifyMutationListeners(); - }, - - replace: function(view) { - var element = get(view, 'element'); - - set(view, 'element', null); - - view._insertElementLater(function() { - jQuery(element).replaceWith(get(view, 'element')); - notifyMutationListeners(); - }); - }, - - remove: function(view) { - view.$().remove(); - notifyMutationListeners(); - }, - - empty: function(view) { - view.$().empty(); - notifyMutationListeners(); - } - }; - - View.reopen({ - domManager: DOMManager - }); - View.reopenClass({ /** @@ -40404,11 +41960,10 @@ define("ember-views/views/view", @private */ _parsePropertyPath: function(path) { - var split = path.split(':'), - propertyPath = split[0], - classNames = "", - className, - falsyClassName; + var split = path.split(':'); + var propertyPath = split[0]; + var classNames = ""; + var className, falsyClassName; // check if the property is defined as prop:class or prop:trueClass:falseClass if (split.length > 1) { @@ -40420,6 +41975,7 @@ define("ember-views/views/view", } return { + stream: undefined, path: propertyPath, classNames: classNames, className: (className === '') ? undefined : className, @@ -40492,7 +42048,7 @@ define("ember-views/views/view", }); var mutation = EmberObject.extend(Evented).create(); - + // TODO MOVE TO RENDERER HOOKS View.addMutationListener = function(callback) { mutation.on('change', callback); }; @@ -40550,82 +42106,11 @@ define("ember-views/views/view", __exports__["default"] = View; }); -define("ember-views/views/view_collection", - ["ember-metal/enumerable_utils","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var forEach = __dependency1__.forEach; - - function ViewCollection(initialViews) { - var views = this.views = initialViews || []; - this.length = views.length; - } - - ViewCollection.prototype = { - length: 0, - - trigger: function(eventName) { - var views = this.views, view; - for (var i = 0, l = views.length; i < l; i++) { - view = views[i]; - if (view.trigger) { view.trigger(eventName); } - } - }, - - triggerRecursively: function(eventName) { - var views = this.views; - for (var i = 0, l = views.length; i < l; i++) { - views[i].triggerRecursively(eventName); - } - }, - - invokeRecursively: function(fn) { - var views = this.views, view; - - for (var i = 0, l = views.length; i < l; i++) { - view = views[i]; - fn(view); - } - }, - - transitionTo: function(state, children) { - var views = this.views; - for (var i = 0, l = views.length; i < l; i++) { - views[i]._transitionTo(state, children); - } - }, - - push: function() { - this.length += arguments.length; - var views = this.views; - return views.push.apply(views, arguments); - }, - - objectAt: function(idx) { - return this.views[idx]; - }, - - forEach: function(callback) { - var views = this.views; - return forEach(views, callback); - }, - - clear: function() { - this.length = 0; - this.views.length = 0; - } - }; - - __exports__["default"] = ViewCollection; - }); -define("ember", +enifed("ember", ["ember-metal","ember-runtime","ember-handlebars","ember-views","ember-routing","ember-routing-handlebars","ember-application","ember-extension-support"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__) { - // Remove "use strict"; from transpiled module until - // https://bugs.webkit.org/show_bug.cgi?id=138038 is fixed - // - // REMOVE_USE_STRICT: true - + "use strict"; + /* global navigator */ // require the main entry points for each of these packages // this is so that the global exports occur properly @@ -40641,521 +42126,830 @@ define("ember", @module ember */ - function throwWithMessage(msg) { - return function() { - throw new Ember.Error(msg); - }; - } - - function generateRemovedClass(className) { - var msg = " has been moved into a plugin: https://github.com/emberjs/ember-states"; - - return { - extend: throwWithMessage(className + msg), - create: throwWithMessage(className + msg) - }; - } - - Ember.StateManager = generateRemovedClass("Ember.StateManager"); - - /** - This was exported to ember-states plugin for v 1.0.0 release. See: https://github.com/emberjs/ember-states - - @class StateManager - @namespace Ember - */ - - Ember.State = generateRemovedClass("Ember.State"); - - /** - This was exported to ember-states plugin for v 1.0.0 release. See: https://github.com/emberjs/ember-states - - @class State - @namespace Ember - */ - }); -define("metamorph", - [], - function() { + }); +enifed("morph", + ["./morph/morph","./morph/dom-helper","exports"], + function(__dependency1__, __dependency2__, __exports__) { "use strict"; - // ========================================================================== - // Project: metamorph - // Copyright: ©2014 Tilde, Inc. All rights reserved. - // ========================================================================== + var Morph = __dependency1__["default"]; + var Morph; + __exports__.Morph = Morph; + var DOMHelper = __dependency2__["default"]; + var DOMHelper; + __exports__.DOMHelper = DOMHelper; + }); +enifed("morph/dom-helper", + ["../morph/morph","./dom-helper/build-html-dom","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var Morph = __dependency1__["default"]; + var buildHTMLDOM = __dependency2__.buildHTMLDOM; + var svgNamespace = __dependency2__.svgNamespace; + var svgHTMLIntegrationPoints = __dependency2__.svgHTMLIntegrationPoints; - var K = function() {}, - guid = 0, - disableRange = (function(){ - if ('undefined' !== typeof MetamorphENV) { - return MetamorphENV.DISABLE_RANGE_API; - } else if ('undefined' !== ENV) { - return ENV.DISABLE_RANGE_API; - } else { - return false; - } - })(), + var deletesBlankTextNodes = (function(){ + var element = document.createElement('div'); + element.appendChild( document.createTextNode('') ); + var clonedElement = element.cloneNode(true); + return clonedElement.childNodes.length === 0; + })(); - // Feature-detect the W3C range API, the extended check is for IE9 which only partially supports ranges - supportsRange = (!disableRange) && typeof document !== 'undefined' && ('createRange' in document) && (typeof Range !== 'undefined') && Range.prototype.createContextualFragment, + var ignoresCheckedAttribute = (function(){ + var element = document.createElement('input'); + element.setAttribute('checked', 'checked'); + var clonedElement = element.cloneNode(false); + return !clonedElement.checked; + })(); - // Internet Explorer prior to 9 does not allow setting innerHTML if the first element - // is a "zero-scope" element. This problem can be worked around by making - // the first node an invisible text node. We, like Modernizr, use ­ - needsShy = typeof document !== 'undefined' && (function() { - var testEl = document.createElement('div'); - testEl.innerHTML = "
        "; - testEl.firstChild.innerHTML = ""; - return testEl.firstChild.innerHTML === ''; - })(), + function isSVG(ns){ + return ns === svgNamespace; + } - - // IE 8 (and likely earlier) likes to move whitespace preceeding - // a script tag to appear after it. This means that we can - // accidentally remove whitespace when updating a morph. - movesWhitespace = document && (function() { - var testEl = document.createElement('div'); - testEl.innerHTML = "Test: Value"; - return testEl.childNodes[0].nodeValue === 'Test:' && - testEl.childNodes[2].nodeValue === ' Value'; - })(); - - // Constructor that supports either Metamorph('foo') or new - // Metamorph('foo'); - // - // Takes a string of HTML as the argument. - - var Metamorph = function(html) { - var self; - - if (this instanceof Metamorph) { - self = this; + // This is not the namespace of the element, but of + // the elements inside that elements. + function interiorNamespace(element){ + if ( + element && + element.namespaceURI === svgNamespace && + !svgHTMLIntegrationPoints[element.tagName] + ) { + return svgNamespace; } else { - self = new K(); + return null; } + } - self.innerHTML = html; - var myGuid = 'metamorph-'+(guid++); - self.start = myGuid + '-start'; - self.end = myGuid + '-end'; + // The HTML spec allows for "omitted start tags". These tags are optional + // when their intended child is the first thing in the parent tag. For + // example, this is a tbody start tag: + // + // + // + // + // + // The tbody may be omitted, and the browser will accept and render: + // + //
        + // + // + // However, the omitted start tag will still be added to the DOM. Here + // we test the string and context to see if the browser is about to + // perform this cleanup. + // + // http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html#optional-tags + // describes which tags are omittable. The spec for tbody and colgroup + // explains this behavior: + // + // http://www.whatwg.org/specs/web-apps/current-work/multipage/tables.html#the-tbody-element + // http://www.whatwg.org/specs/web-apps/current-work/multipage/tables.html#the-colgroup-element + // - return self; + var omittedStartTagChildTest = /<([\w:]+)/; + function detectOmittedStartTag(string, contextualElement){ + // Omitted start tags are only inside table tags. + if (contextualElement.tagName === 'TABLE') { + var omittedStartTagChildMatch = omittedStartTagChildTest.exec(string); + if (omittedStartTagChildMatch) { + var omittedStartTagChild = omittedStartTagChildMatch[1]; + // It is already asserted that the contextual element is a table + // and not the proper start tag. Just see if a tag was omitted. + return omittedStartTagChild === 'tr' || + omittedStartTagChild === 'col'; + } + } + } + + function buildSVGDOM(html, dom){ + var div = dom.document.createElement('div'); + div.innerHTML = ''+html+''; + return div.firstChild.childNodes; + } + + /* + * A class wrapping DOM functions to address environment compatibility, + * namespaces, contextual elements for morph un-escaped content + * insertion. + * + * When entering a template, a DOMHelper should be passed: + * + * template(context, { hooks: hooks, dom: new DOMHelper() }); + * + * TODO: support foreignObject as a passed contextual element. It has + * a namespace (svg) that does not match its internal namespace + * (xhtml). + * + * @class DOMHelper + * @constructor + * @param {HTMLDocument} _document The document DOM methods are proxied to + */ + function DOMHelper(_document){ + this.document = _document || window.document; + this.namespace = null; + } + + var prototype = DOMHelper.prototype; + prototype.constructor = DOMHelper; + + prototype.insertBefore = function(element, childElement, referenceChild) { + return element.insertBefore(childElement, referenceChild); }; - K.prototype = Metamorph.prototype; - - var rangeFor, htmlFunc, removeFunc, outerHTMLFunc, appendToFunc, afterFunc, prependFunc, startTagFunc, endTagFunc; - - outerHTMLFunc = function() { - return this.startTag() + this.innerHTML + this.endTag(); + prototype.appendChild = function(element, childElement) { + return element.appendChild(childElement); }; - startTagFunc = function() { - /* - * We replace chevron by its hex code in order to prevent escaping problems. - * Check this thread for more explaination: - * http://stackoverflow.com/questions/8231048/why-use-x3c-instead-of-when-generating-html-from-javascript - */ - return "
        hi
        "; - * div.firstChild.firstChild.tagName //=> "" - * - * If our script markers are inside such a node, we need to find that - * node and use *it* as the marker. - */ - var realNode = function(start) { - while (start.parentNode.tagName === "") { - start = start.parentNode; - } - - return start; - }; - - /* - * When automatically adding a tbody, Internet Explorer inserts the - * tbody immediately before the first . Other browsers create it - * before the first node, no matter what. - * - * This means the the following code: - * - * div = document.createElement("div"); - * div.innerHTML = "
        hi
        - * - * Generates the following DOM in IE: - * - * + div - * + table - * - script id='first' - * + tbody - * + tr - * + td - * - "hi" - * - script id='last' - * - * Which means that the two script tags, even though they were - * inserted at the same point in the hierarchy in the original - * HTML, now have different parents. - * - * This code reparents the first script tag by making it the tbody's - * first child. - * - */ - var fixParentage = function(start, end) { - if (start.parentNode !== end.parentNode) { - end.parentNode.insertBefore(start, end.parentNode.firstChild); - } - }; - - htmlFunc = function(html, outerToo) { - // get the real starting node. see realNode for details. - var start = realNode(document.getElementById(this.start)); - var end = document.getElementById(this.end); - var parentNode = end.parentNode; - var node, nextSibling, last; - - // make sure that the start and end nodes share the same - // parent. If not, fix it. - fixParentage(start, end); - - // remove all of the nodes after the starting placeholder and - // before the ending placeholder. - node = start.nextSibling; - while (node) { - nextSibling = node.nextSibling; - last = node === end; - - // if this is the last node, and we want to remove it as well, - // set the `end` node to the next sibling. This is because - // for the rest of the function, we insert the new nodes - // before the end (note that insertBefore(node, null) is - // the same as appendChild(node)). - // - // if we do not want to remove it, just break. - if (last) { - if (outerToo) { end = node.nextSibling; } else { break; } - } - - node.parentNode.removeChild(node); - - // if this is the last node and we didn't break before - // (because we wanted to remove the outer nodes), break - // now. - if (last) { break; } - - node = nextSibling; - } - - // get the first node for the HTML string, even in cases like - // tables and lists where a simple innerHTML on a div would - // swallow some of the content. - node = firstNodeFor(start.parentNode, html); - - if (outerToo) { - start.parentNode.removeChild(start); - } - - // copy the nodes for the HTML between the starting and ending - // placeholder. - while (node) { - nextSibling = node.nextSibling; - parentNode.insertBefore(node, end); - node = nextSibling; - } - }; - - // remove the nodes in the DOM representing this metamorph. - // - // this includes the starting and ending placeholders. - removeFunc = function() { - var start = realNode(document.getElementById(this.start)); - var end = document.getElementById(this.end); - - this.html(''); - start.parentNode.removeChild(start); - end.parentNode.removeChild(end); - }; - - appendToFunc = function(parentNode) { - var node = firstNodeFor(parentNode, this.outerHTML()); - var nextSibling; - - while (node) { - nextSibling = node.nextSibling; - parentNode.appendChild(node); - node = nextSibling; - } - }; - - afterFunc = function(html) { - // get the real starting node. see realNode for details. - var end = document.getElementById(this.end); - var insertBefore = end.nextSibling; - var parentNode = end.parentNode; - var nextSibling; - var node; - - // get the first node for the HTML string, even in cases like - // tables and lists where a simple innerHTML on a div would - // swallow some of the content. - node = firstNodeFor(parentNode, html); - - // copy the nodes for the HTML between the starting and ending - // placeholder. - while (node) { - nextSibling = node.nextSibling; - parentNode.insertBefore(node, insertBefore); - node = nextSibling; - } - }; - - prependFunc = function(html) { - var start = document.getElementById(this.start); - var parentNode = start.parentNode; - var nextSibling; - var node; - - node = firstNodeFor(parentNode, html); - var insertBefore = start.nextSibling; - - while (node) { - nextSibling = node.nextSibling; - parentNode.insertBefore(node, insertBefore); - node = nextSibling; - } + prototype.createElement = function(tagName) { + return this.document.createElement(tagName); }; } - Metamorph.prototype.html = function(html) { - this.checkRemoved(); - if (html === undefined) { return this.innerHTML; } - - htmlFunc.call(this, html); - - this.innerHTML = html; + prototype.setNamespace = function(ns) { + this.namespace = ns; }; - Metamorph.prototype.replaceWith = function(html) { - this.checkRemoved(); - htmlFunc.call(this, html, true); + prototype.detectNamespace = function(element) { + this.namespace = interiorNamespace(element); }; - Metamorph.prototype.remove = removeFunc; - Metamorph.prototype.outerHTML = outerHTMLFunc; - Metamorph.prototype.appendTo = appendToFunc; - Metamorph.prototype.after = afterFunc; - Metamorph.prototype.prepend = prependFunc; - Metamorph.prototype.startTag = startTagFunc; - Metamorph.prototype.endTag = endTagFunc; - - Metamorph.prototype.isRemoved = function() { - var before = document.getElementById(this.start); - var after = document.getElementById(this.end); - - return !before || !after; + prototype.createDocumentFragment = function(){ + return this.document.createDocumentFragment(); }; - Metamorph.prototype.checkRemoved = function() { - if (this.isRemoved()) { - throw new Error("Cannot perform operations on a Metamorph that is not in the DOM."); + prototype.createTextNode = function(text){ + return this.document.createTextNode(text); + }; + + prototype.repairClonedNode = function(element, blankChildTextNodes, isChecked){ + if (deletesBlankTextNodes && blankChildTextNodes.length > 0) { + for (var i=0, len=blankChildTextNodes.length;i]*selected/; + detectAutoSelectedOption = function detectAutoSelectedOption(select, option, html) { //jshint ignore:line + return select.selectedIndex === 0 && + !detectAutoSelectedOptionRegex.test(html); + }; + } else { + detectAutoSelectedOption = function detectAutoSelectedOption(select, option, html) { //jshint ignore:line + var selectedAttribute = option.getAttribute('selected'); + return select.selectedIndex === 0 && ( + selectedAttribute === null || + ( selectedAttribute !== '' && selectedAttribute.toLowerCase() !== 'selected' ) + ); + }; + } + + // IE 9 and earlier don't allow us to set innerHTML on col, colgroup, frameset, + // html, style, table, tbody, tfoot, thead, title, tr. Detect this and add + // them to an initial list of corrected tags. + // + // Here we are only dealing with the ones which can have child nodes. + // + var tagNamesRequiringInnerHTMLFix, tableNeedsInnerHTMLFix; + var tableInnerHTMLTestElement = document.createElement('table'); + try { + tableInnerHTMLTestElement.innerHTML = ''; + } catch (e) { + } finally { + tableNeedsInnerHTMLFix = (tableInnerHTMLTestElement.childNodes.length === 0); + } + if (tableNeedsInnerHTMLFix) { + tagNamesRequiringInnerHTMLFix = { + colgroup: ['table'], + table: [], + tbody: ['table'], + tfoot: ['table'], + thead: ['table'], + tr: ['table', 'tbody'] + }; + } + + // IE 8 doesn't allow setting innerHTML on a select tag. Detect this and + // add it to the list of corrected tags. + // + var selectInnerHTMLTestElement = document.createElement('select'); + selectInnerHTMLTestElement.innerHTML = ''; + if (selectInnerHTMLTestElement) { + tagNamesRequiringInnerHTMLFix = tagNamesRequiringInnerHTMLFix || {}; + tagNamesRequiringInnerHTMLFix.select = []; + } + + function scriptSafeInnerHTML(element, html) { + // without a leading text node, IE will drop a leading script tag. + html = '­'+html; + + element.innerHTML = html; + + var nodes = element.childNodes; + + // Look for ­ to remove it. + var shyElement = nodes[0]; + while (shyElement.nodeType === 1 && !shyElement.nodeName) { + shyElement = shyElement.firstChild; + } + // At this point it's the actual unicode character. + if (shyElement.nodeType === 3 && shyElement.nodeValue.charAt(0) === "\u00AD") { + var newValue = shyElement.nodeValue.slice(1); + if (newValue.length) { + shyElement.nodeValue = shyElement.nodeValue.slice(1); + } else { + shyElement.parentNode.removeChild(shyElement); + } + } + + return nodes; + } + + function buildDOMWithFix(html, contextualElement){ + var tagName = contextualElement.tagName; + + // Firefox versions < 11 do not have support for element.outerHTML. + var outerHTML = contextualElement.outerHTML || new XMLSerializer().serializeToString(contextualElement); + if (!outerHTML) { + throw "Can't set innerHTML on "+tagName+" in this browser"; + } + + var wrappingTags = tagNamesRequiringInnerHTMLFix[tagName.toLowerCase()]; + var startTag = outerHTML.match(new RegExp("<"+tagName+"([^>]*)>", 'i'))[0]; + var endTag = ''; + + var wrappedHTML = [startTag, html, endTag]; + + var i = wrappingTags.length; + var wrappedDepth = 1 + i; + while(i--) { + wrappedHTML.unshift('<'+wrappingTags[i]+'>'); + wrappedHTML.push(''); + } + + var wrapper = document.createElement('div'); + scriptSafeInnerHTML(wrapper, wrappedHTML.join('')); + var element = wrapper; + while (wrappedDepth--) { + element = element.firstChild; + while (element && element.nodeType !== 1) { + element = element.nextSibling; + } + } + while (element && element.tagName !== tagName) { + element = element.nextSibling; + } + return element ? element.childNodes : []; + } + + var buildDOM; + if (needsShy) { + buildDOM = function buildDOM(html, contextualElement, dom){ + contextualElement = dom.cloneNode(contextualElement, false); + scriptSafeInnerHTML(contextualElement, html); + return contextualElement.childNodes; + }; + } else { + buildDOM = function buildDOM(html, contextualElement, dom){ + contextualElement = dom.cloneNode(contextualElement, false); + contextualElement.innerHTML = html; + return contextualElement.childNodes; + }; + } + + var buildIESafeDOM; + if (tagNamesRequiringInnerHTMLFix || movesWhitespace) { + buildIESafeDOM = function buildIESafeDOM(html, contextualElement, dom) { + // Make a list of the leading text on script nodes. Include + // script tags without any whitespace for easier processing later. + var spacesBefore = []; + var spacesAfter = []; + html = html.replace(/(\s*)()(\s*)/g, function(match, tag, spaces) { + spacesAfter.push(spaces); + return tag; + }); + + // Fetch nodes + var nodes; + if (tagNamesRequiringInnerHTMLFix[contextualElement.tagName.toLowerCase()]) { + // buildDOMWithFix uses string wrappers for problematic innerHTML. + nodes = buildDOMWithFix(html, contextualElement); + } else { + nodes = buildDOM(html, contextualElement, dom); + } + + // Build a list of script tags, the nodes themselves will be + // mutated as we add test nodes. + var i, j, node, nodeScriptNodes; + var scriptNodes = []; + for (i=0;node=nodes[i];i++) { + if (node.nodeType !== 1) { + continue; + } + if (node.tagName === 'SCRIPT') { + scriptNodes.push(node); + } else { + nodeScriptNodes = node.getElementsByTagName('script'); + for (j=0;j 0) { + textNode = dom.document.createTextNode(spaceBefore); + scriptNode.parentNode.insertBefore(textNode, scriptNode); + } + + spaceAfter = spacesAfter[i]; + if (spaceAfter && spaceAfter.length > 0) { + textNode = dom.document.createTextNode(spaceAfter); + scriptNode.parentNode.insertBefore(textNode, scriptNode.nextSibling); + } + } + + return nodes; + }; + } else { + buildIESafeDOM = buildDOM; + } + + // When parsing innerHTML, the browser may set up DOM with some things + // not desired. For example, with a select element context and option + // innerHTML the first option will be marked selected. + // + // This method cleans up some of that, resetting those values back to + // their defaults. + // + function buildSafeDOM(html, contextualElement, dom) { + var childNodes = buildIESafeDOM(html, contextualElement, dom); + + if (contextualElement.tagName === 'SELECT') { + // Walk child nodes + for (var i = 0; childNodes[i]; i++) { + // Find and process the first option child node + if (childNodes[i].tagName === 'OPTION') { + if (detectAutoSelectedOption(childNodes[i].parentNode, childNodes[i], html)) { + // If the first node is selected but does not have an attribute, + // presume it is not really selected. + childNodes[i].parentNode.selectedIndex = -1; + } + break; + } + } + } + + return childNodes; + } + + var buildHTMLDOM; + if (needsIntegrationPointFix) { + buildHTMLDOM = function buildHTMLDOM(html, contextualElement, dom){ + if (svgHTMLIntegrationPoints[contextualElement.tagName]) { + return buildSafeDOM(html, document.createElement('div'), dom); + } else { + return buildSafeDOM(html, contextualElement, dom); + } + }; + } else { + buildHTMLDOM = buildSafeDOM; + } + + __exports__.buildHTMLDOM = buildHTMLDOM; + }); +enifed("morph/morph", + ["exports"], + function(__exports__) { + "use strict"; + var splice = Array.prototype.splice; + + function ensureStartEnd(start, end) { + if (start === null || end === null) { + throw new Error('a fragment parent must have boundary nodes in order to detect insertion'); + } + } + + function ensureContext(contextualElement) { + if (!contextualElement || contextualElement.nodeType !== 1) { + throw new Error('An element node must be provided for a contextualElement, you provided ' + + (contextualElement ? 'nodeType ' + contextualElement.nodeType : 'nothing')); + } + } + + // TODO: this is an internal API, this should be an assert + function Morph(parent, start, end, domHelper, contextualElement) { + if (parent.nodeType === 11) { + ensureStartEnd(start, end); + this.element = null; + } else { + this.element = parent; + } + this._parent = parent; + this.start = start; + this.end = end; + this.domHelper = domHelper; + ensureContext(contextualElement); + this.contextualElement = contextualElement; + this.reset(); + } + + Morph.prototype.reset = function() { + this.text = null; + this.owner = null; + this.morphs = null; + this.before = null; + this.after = null; + this.escaped = true; + }; + + Morph.prototype.parent = function () { + if (!this.element) { + var parent = this.start.parentNode; + if (this._parent !== parent) { + this.element = this._parent = parent; + } + } + return this._parent; + }; + + Morph.prototype.destroy = function () { + if (this.owner) { + this.owner.removeMorph(this); + } else { + clear(this.element || this.parent(), this.start, this.end); + } + }; + + Morph.prototype.removeMorph = function (morph) { + var morphs = this.morphs; + for (var i=0, l=morphs.length; i 0 ? morphs[index-1] : null; + var after = index < morphs.length ? morphs[index] : null; + var start = before === null ? this.start : (before.end === null ? parent.lastChild : before.end.previousSibling); + var end = after === null ? this.end : (after.start === null ? parent.firstChild : after.start.nextSibling); + var morph = new Morph(parent, start, end, this.domHelper, this.contextualElement); + + morph.owner = this; + morph._update(parent, node); + + if (before !== null) { + morph.before = before; + before.end = start.nextSibling; + before.after = morph; + } + + if (after !== null) { + morph.after = after; + after.before = morph; + after.start = end.previousSibling; + } + + this.morphs.splice(index, 0, morph); + return morph; + }; + + Morph.prototype.replace = function (index, removedLength, addedNodes) { + if (this.morphs === null) this.morphs = []; + var parent = this.element || this.parent(); + var morphs = this.morphs; + var before = index > 0 ? morphs[index-1] : null; + var after = index+removedLength < morphs.length ? morphs[index+removedLength] : null; + var start = before === null ? this.start : (before.end === null ? parent.lastChild : before.end.previousSibling); + var end = after === null ? this.end : (after.start === null ? parent.firstChild : after.start.nextSibling); + var addedLength = addedNodes === undefined ? 0 : addedNodes.length; + var args, i, current; + + if (removedLength > 0) { + clear(parent, start, end); + } + + if (addedLength === 0) { + if (before !== null) { + before.after = after; + before.end = end; + } + if (after !== null) { + after.before = before; + after.start = start; + } + morphs.splice(index, removedLength); + return; + } + + args = new Array(addedLength+2); + if (addedLength > 0) { + for (i=0; ithis.lengthBeforeRender){this.clearRenderedChildren();this._childViews.length=this.lengthBeforeRender}if(i){t=Ember.RenderBuffer();t=this.renderToBuffer(t);h=this._childViews.length>0;if(h){this.invokeRecursively(o,false)}e.innerHTML=t.innerString?t.innerString():s(t);r(this,"element",e);var a=this._transitionTo?this._transitionTo:this.transitionTo;a.call(this,"inDOM");if(h){this.invokeRecursively(l,false)}}else{e.innerHTML=""}}t["default"]=Ember.View.extend(i,{updateContext:function(e){var t=n(this,"context");Ember.instrument("view.updateContext.render",this,function(){if(t!==e){r(this,"context",e);if(e&&e.isController){r(this,"controller",e)}}},this)},rerender:function(){Ember.run.scheduleOnce("render",this,h)},_contextDidChange:Ember.observer(h,"context","controller")})});t("list-view/list_item_view_mixin",["exports"],function(e){"use strict";var t=Ember.get,i=Ember.set;function n(e,t){return e&&t&&e.x===t.x&&e.y===t.y}function r(){var e,i,r;Ember.instrument("view.updateContext.positionElement",this,function(){e=t(this,"element");i=this.position;r=this._position;if(!i||!e){return}if(n(i,r)){return}Ember.run.schedule("render",this,this._parentView.applyTransform,e,i.x,i.y);this._position=i},this)}e["default"]=Ember.Mixin.create({init:function(){this._super();this.one("didInsertElement",r)},classNames:["ember-list-item-view"],_position:null,updatePosition:function(e){this.position=e;this._positionElement()},_positionElement:r})});t("list-view/list_view",["list-view/list_view_helper","list-view/list_view_mixin","exports"],function(e,t,i){"use strict";var n=e["default"];var r=t["default"];var s=Ember.get,o=Ember.set;i["default"]=Ember.ContainerView.extend(r,{css:{position:"relative",overflow:"auto","-webkit-overflow-scrolling":"touch","overflow-scrolling":"touch"},applyTransform:n.applyTransform,_scrollTo:function(e){var t=s(this,"element");if(t){t.scrollTop=e}},didInsertElement:function(){var e=this;var t=s(this,"element");this._updateScrollableHeight();this._scroll=function(t){e.scroll(t)};Ember.$(t).on("scroll",this._scroll)},willDestroyElement:function(){var e;e=s(this,"element");Ember.$(e).off("scroll",this._scroll)},scroll:function(e){this.scrollTo(e.target.scrollTop)},scrollTo:function(e){var t=s(this,"element");this._scrollTo(e);this._scrollContentTo(e)},totalHeightDidChange:Ember.observer(function(){Ember.run.scheduleOnce("afterRender",this,this._updateScrollableHeight)},"totalHeight"),_updateScrollableHeight:function(){var e,t;t=this._state||this.state;if(t==="inDOM"){if(this._isChildEmptyView()){e=""}else{e=s(this,"totalHeight")}this.$(".ember-list-container").css({height:e})}}})});t("list-view/list_view_helper",["exports"],function(e){"use strict";var t=document.createElement("div"),i=t.style;var n=["Webkit","Moz","O","ms"];function r(e){if(e in i)return e;var t=e.charAt(0).toUpperCase()+e.slice(1);for(var r=0;r1}).readOnly(),init:function(){this._super();this._cachedHeights=[0];this.on("didInsertElement",this._syncListContainerWidth);this.columnCountDidChange();this._syncChildViews();this._addContentArrayObserver()},_addContentArrayObserver:Ember.beforeObserver(function(){c.call(this)},"content"),render:function(e){e.push('
        ');this._super(e);e.push("
        ")},willInsertElement:function(){if(!this.get("height")||!this.get("rowHeight")){throw new Error("A ListView must be created with a height and a rowHeight.")}this._super()},style:Ember.computed("height","width",function(){var e,t,i,r;e=n(this,"height");t=n(this,"width");r=n(this,"css");i="";if(e){i+="height:"+e+"px;"}if(t){i+="width:"+t+"px;"}for(var s in r){if(r.hasOwnProperty(s)){i+=s+":"+r[s]+";"}}return i}),scrollTo:function(e){throw new Error("must override to perform the visual scroll and effectively delegate to _scrollContentTo")},_scrollTo:Ember.K,_scrollContentTo:function(e){var t,i,r,l,h,a,c,u,d;u=o(0,e);if(this.scrollTop===u){return}var f=o(0,n(this,"totalHeight")-n(this,"height"));u=s(u,f);d=n(this,"content");c=n(d,"length");t=this._startingIndex(c);Ember.instrument("view._scrollContentTo",{scrollTop:u,content:d,startingIndex:t,endingIndex:s(o(c-1,0),t+this._numChildViewsForViewport())},function(){this.scrollTop=u;h=o(c-1,0);t=this._startingIndex();l=t+this._numChildViewsForViewport();i=s(h,l);if(t===this._lastStartingIndex&&i===this._lastEndingIndex){this.trigger("scrollYChanged",e);return}else{Ember.run(this,function(){this._reuseChildren();this._lastStartingIndex=t;this._lastEndingIndex=i;this.trigger("scrollYChanged",e)})}},this)},totalHeight:Ember.computed("content.length","rowHeight","columnCount","bottomPadding",function(){if(typeof this.heightForIndex==="function"){return this._totalHeightWithHeightForIndex()}else{return this._totalHeightWithStaticRowHeight()}}),_doRowHeightDidChange:function(){this._cachedHeights=[0];this._cachedPos=0;this._syncChildViews()},_rowHeightDidChange:Ember.observer("rowHeight",function(){Ember.run.once(this,this._doRowHeightDidChange)}),_totalHeightWithHeightForIndex:function(){var e=this.get("content.length");return this._cachedHeightLookup(e)},_totalHeightWithStaticRowHeight:function(){var e,t,i,r;e=n(this,"content.length");t=n(this,"rowHeight");i=n(this,"columnCount");r=n(this,"bottomPadding");return h(e/i)*t+r},_prepareChildForReuse:function(e){e.prepareForReuse()},_reuseChildForContentIndex:function(e,t){var i,s,o,l,h,a,c;var u=this.itemViewForIndex(t);if(e.constructor!==u){var d=this._childViews.indexOf(e);e.destroy();e=this.createChildView(u);this.insertAt(d,e)}i=n(this,"content");a=n(this,"enableProfiling");h=this.positionForIndex(t);e.updatePosition(h);r(e,"contentIndex",t);if(a){Ember.instrument("view._reuseChildForContentIndex",h,function(){},this)}o=i.objectAt(t);e.updateContext(o)},positionForIndex:function(e){if(typeof this.heightForIndex!=="function"){return this._singleHeightPosForIndex(e)}else{return this._multiHeightPosForIndex(e)}},_singleHeightPosForIndex:function(e){var t,i,r,s,o,h;t=n(this,"elementWidth")||1;i=n(this,"width")||1;r=n(this,"columnCount");s=n(this,"rowHeight");o=s*l(e/r);h=e%r*t;return{y:o,x:h}},_multiHeightPosForIndex:function(e){var t,i,r,s,o,l;t=n(this,"elementWidth")||1;i=n(this,"width")||1;r=n(this,"columnCount");l=e%r*t;o=this._cachedHeightLookup(e);return{x:l,y:o}},_cachedHeightLookup:function(e){for(var t=this._cachedPos;te){i=l(t/e)}else{i=1}return i}),columnCountDidChange:Ember.observer(function(){var e,t,i,r,o,l,h,a;l=this._lastColumnCount;t=this.scrollTop;h=n(this,"columnCount");r=n(this,"maxScrollTop");a=n(this,"element");this._lastColumnCount=h;if(l){e=l/h;i=t*e;o=s(r,i);this._scrollTo(o);this.scrollTop=o}if(arguments.length>0){Ember.run.schedule("afterRender",this,this._syncListContainerWidth)}},"columnCount"),maxScrollTop:Ember.computed("height","totalHeight",function(){var e,t;e=n(this,"totalHeight");t=n(this,"height");return o(0,e-t)}),_isChildEmptyView:function(){var e=n(this,"emptyView");return e&&e instanceof Ember.View&&this._childViews.length===1&&this._childViews.indexOf(e)===0},_numChildViewsForViewport:function(){if(this.heightForIndex){return this._numChildViewsForViewportWithMultiHeight()}else{return this._numChildViewsForViewportWithoutMultiHeight()}},_numChildViewsForViewportWithoutMultiHeight:function(){var e,t,i,r;e=n(this,"height");t=n(this,"rowHeight");i=n(this,"paddingCount");r=n(this,"columnCount");return h(e/t)*r+i*r},_numChildViewsForViewportWithMultiHeight:function(){var e,t,i;var r=this.scrollTop;var s=this.get("height");var o=this.get("content.length");var l=0;var h=n(this,"paddingCount");var a=this._calculatedStartingIndex();var c=0;var u=this._cachedHeightLookup(a);for(var d=0;ds){break}}return d+h+1},_startingIndex:function(e){var t,i,r,h,a;if(e===undefined){a=n(this,"content.length")}else{a=e}t=this.scrollTop;i=n(this,"rowHeight");r=n(this,"columnCount");if(this.heightForIndex){h=this._calculatedStartingIndex()}else{h=l(t/i)*r}var c=this._numChildViewsForViewport();var u=1*r;var d=o(a-c,0);return s(h,d)},_calculatedStartingIndex:function(){var e,t,i;var r=this.scrollTop;var s=this.get("height");var o=this.get("content.length");var l=0;var h=n(this,"paddingCount");for(var a=0;a=r){break}}return a},contentWillChange:Ember.beforeObserver(function(){var e;e=n(this,"content");if(e){e.removeArrayObserver(this)}},"content"),contentDidChange:Ember.observer(function(){c.call(this);d.call(this)},"content"),needsSyncChildViews:Ember.observer(d,"height","width","columnCount"),_addItemView:function(e){var t,i;t=this.itemViewForIndex(e);i=this.createChildView(t);this.pushObject(i)},itemViewForIndex:function(e){return n(this,"itemViewClass")},heightForIndex:null,_syncChildViews:function(){var e,t,i,r,s,o,l,h,c,d,f;if(n(this,"isDestroyed")||n(this,"isDestroying")){return}h=n(this,"content.length");c=n(this,"emptyView");t=this._childViewCount();e=this.positionOrderedChildViews();if(this._isChildEmptyView()){v.call(this)}o=this._startingIndex();l=o+t;r=t;i=e.length;f=r-i;if(f===0){}else if(f>0){s=this._lastEndingIndex;for(d=0;d=this._lastStartingIndex||t .ember-list-container")[0]},willBeginScroll:function(e,t){this._isScrolling=false;this.trigger("scrollingDidStart");this.scroller.doTouchStart(e,t)},continueScroll:function(e,t){var i,n,r;if(this._isScrolling){this.scroller.doTouchMove(e,t)}else{i=this._scrollerTop;this.scroller.doTouchMove(e,t);n=this._scrollerTop;if(i!==n){r=Ember.$.Event("scrollerstart");Ember.$(e[0].target).trigger(r);this._isScrolling=true}}},endScroll:function(e){this.scroller.doTouchEnd(e)},scrollTo:function(e,t){if(t===undefined){t=true}this.scroller.scrollTo(0,e,t,1)},mouseWheel:function(e){var t,i,n;t=e.webkitDirectionInvertedFromDevice;i=e.wheelDeltaY*(t?.8:-.8);n=this.scroller.__scrollTop+i;if(n>=0&&n<=this.scroller.__maxScrollTop){this.scroller.scrollBy(0,i,true);e.stopPropagation()}return false}})});i("list-view/main")})(this); \ No newline at end of file diff --git a/vendor/assets/javascripts/select2.js b/vendor/assets/javascripts/select2.js index 99f212bd8f..7590b82295 100644 --- a/vendor/assets/javascripts/select2.js +++ b/vendor/assets/javascripts/select2.js @@ -1,7 +1,7 @@ /* Copyright 2012 Igor Vaynberg -Version: @@ver@@ Timestamp: @@timestamp@@ +Version: 3.5.2 Timestamp: Sat Nov 1 14:43:36 EDT 2014 This software is licensed under the Apache License, Version 2.0 (the "Apache License") or the GNU General Public License version 2 (the "GPL License"). You may choose either license to govern your @@ -46,7 +46,7 @@ the specific language governing permissions and limitations under the Apache Lic return; } - var KEY, AbstractSelect2, SingleSelect2, MultiSelect2, nextUid, sizer, + var AbstractSelect2, SingleSelect2, MultiSelect2, nextUid, sizer, lastMousePosition={x:0,y:0}, $document, scrollBarDimensions, KEY = { @@ -98,8 +98,7 @@ the specific language governing permissions and limitations under the Apache Lic }, MEASURE_SCROLLBAR_TEMPLATE = "
        ", - // HACK, removed AT LEAST until https://github.com/mishoo/UglifyJS2/issues/328 lands in uglifyjs2 - DIACRITICS = {}; + DIACRITICS = {"\u24B6":"A","\uFF21":"A","\u00C0":"A","\u00C1":"A","\u00C2":"A","\u1EA6":"A","\u1EA4":"A","\u1EAA":"A","\u1EA8":"A","\u00C3":"A","\u0100":"A","\u0102":"A","\u1EB0":"A","\u1EAE":"A","\u1EB4":"A","\u1EB2":"A","\u0226":"A","\u01E0":"A","\u00C4":"A","\u01DE":"A","\u1EA2":"A","\u00C5":"A","\u01FA":"A","\u01CD":"A","\u0200":"A","\u0202":"A","\u1EA0":"A","\u1EAC":"A","\u1EB6":"A","\u1E00":"A","\u0104":"A","\u023A":"A","\u2C6F":"A","\uA732":"AA","\u00C6":"AE","\u01FC":"AE","\u01E2":"AE","\uA734":"AO","\uA736":"AU","\uA738":"AV","\uA73A":"AV","\uA73C":"AY","\u24B7":"B","\uFF22":"B","\u1E02":"B","\u1E04":"B","\u1E06":"B","\u0243":"B","\u0182":"B","\u0181":"B","\u24B8":"C","\uFF23":"C","\u0106":"C","\u0108":"C","\u010A":"C","\u010C":"C","\u00C7":"C","\u1E08":"C","\u0187":"C","\u023B":"C","\uA73E":"C","\u24B9":"D","\uFF24":"D","\u1E0A":"D","\u010E":"D","\u1E0C":"D","\u1E10":"D","\u1E12":"D","\u1E0E":"D","\u0110":"D","\u018B":"D","\u018A":"D","\u0189":"D","\uA779":"D","\u01F1":"DZ","\u01C4":"DZ","\u01F2":"Dz","\u01C5":"Dz","\u24BA":"E","\uFF25":"E","\u00C8":"E","\u00C9":"E","\u00CA":"E","\u1EC0":"E","\u1EBE":"E","\u1EC4":"E","\u1EC2":"E","\u1EBC":"E","\u0112":"E","\u1E14":"E","\u1E16":"E","\u0114":"E","\u0116":"E","\u00CB":"E","\u1EBA":"E","\u011A":"E","\u0204":"E","\u0206":"E","\u1EB8":"E","\u1EC6":"E","\u0228":"E","\u1E1C":"E","\u0118":"E","\u1E18":"E","\u1E1A":"E","\u0190":"E","\u018E":"E","\u24BB":"F","\uFF26":"F","\u1E1E":"F","\u0191":"F","\uA77B":"F","\u24BC":"G","\uFF27":"G","\u01F4":"G","\u011C":"G","\u1E20":"G","\u011E":"G","\u0120":"G","\u01E6":"G","\u0122":"G","\u01E4":"G","\u0193":"G","\uA7A0":"G","\uA77D":"G","\uA77E":"G","\u24BD":"H","\uFF28":"H","\u0124":"H","\u1E22":"H","\u1E26":"H","\u021E":"H","\u1E24":"H","\u1E28":"H","\u1E2A":"H","\u0126":"H","\u2C67":"H","\u2C75":"H","\uA78D":"H","\u24BE":"I","\uFF29":"I","\u00CC":"I","\u00CD":"I","\u00CE":"I","\u0128":"I","\u012A":"I","\u012C":"I","\u0130":"I","\u00CF":"I","\u1E2E":"I","\u1EC8":"I","\u01CF":"I","\u0208":"I","\u020A":"I","\u1ECA":"I","\u012E":"I","\u1E2C":"I","\u0197":"I","\u24BF":"J","\uFF2A":"J","\u0134":"J","\u0248":"J","\u24C0":"K","\uFF2B":"K","\u1E30":"K","\u01E8":"K","\u1E32":"K","\u0136":"K","\u1E34":"K","\u0198":"K","\u2C69":"K","\uA740":"K","\uA742":"K","\uA744":"K","\uA7A2":"K","\u24C1":"L","\uFF2C":"L","\u013F":"L","\u0139":"L","\u013D":"L","\u1E36":"L","\u1E38":"L","\u013B":"L","\u1E3C":"L","\u1E3A":"L","\u0141":"L","\u023D":"L","\u2C62":"L","\u2C60":"L","\uA748":"L","\uA746":"L","\uA780":"L","\u01C7":"LJ","\u01C8":"Lj","\u24C2":"M","\uFF2D":"M","\u1E3E":"M","\u1E40":"M","\u1E42":"M","\u2C6E":"M","\u019C":"M","\u24C3":"N","\uFF2E":"N","\u01F8":"N","\u0143":"N","\u00D1":"N","\u1E44":"N","\u0147":"N","\u1E46":"N","\u0145":"N","\u1E4A":"N","\u1E48":"N","\u0220":"N","\u019D":"N","\uA790":"N","\uA7A4":"N","\u01CA":"NJ","\u01CB":"Nj","\u24C4":"O","\uFF2F":"O","\u00D2":"O","\u00D3":"O","\u00D4":"O","\u1ED2":"O","\u1ED0":"O","\u1ED6":"O","\u1ED4":"O","\u00D5":"O","\u1E4C":"O","\u022C":"O","\u1E4E":"O","\u014C":"O","\u1E50":"O","\u1E52":"O","\u014E":"O","\u022E":"O","\u0230":"O","\u00D6":"O","\u022A":"O","\u1ECE":"O","\u0150":"O","\u01D1":"O","\u020C":"O","\u020E":"O","\u01A0":"O","\u1EDC":"O","\u1EDA":"O","\u1EE0":"O","\u1EDE":"O","\u1EE2":"O","\u1ECC":"O","\u1ED8":"O","\u01EA":"O","\u01EC":"O","\u00D8":"O","\u01FE":"O","\u0186":"O","\u019F":"O","\uA74A":"O","\uA74C":"O","\u01A2":"OI","\uA74E":"OO","\u0222":"OU","\u24C5":"P","\uFF30":"P","\u1E54":"P","\u1E56":"P","\u01A4":"P","\u2C63":"P","\uA750":"P","\uA752":"P","\uA754":"P","\u24C6":"Q","\uFF31":"Q","\uA756":"Q","\uA758":"Q","\u024A":"Q","\u24C7":"R","\uFF32":"R","\u0154":"R","\u1E58":"R","\u0158":"R","\u0210":"R","\u0212":"R","\u1E5A":"R","\u1E5C":"R","\u0156":"R","\u1E5E":"R","\u024C":"R","\u2C64":"R","\uA75A":"R","\uA7A6":"R","\uA782":"R","\u24C8":"S","\uFF33":"S","\u1E9E":"S","\u015A":"S","\u1E64":"S","\u015C":"S","\u1E60":"S","\u0160":"S","\u1E66":"S","\u1E62":"S","\u1E68":"S","\u0218":"S","\u015E":"S","\u2C7E":"S","\uA7A8":"S","\uA784":"S","\u24C9":"T","\uFF34":"T","\u1E6A":"T","\u0164":"T","\u1E6C":"T","\u021A":"T","\u0162":"T","\u1E70":"T","\u1E6E":"T","\u0166":"T","\u01AC":"T","\u01AE":"T","\u023E":"T","\uA786":"T","\uA728":"TZ","\u24CA":"U","\uFF35":"U","\u00D9":"U","\u00DA":"U","\u00DB":"U","\u0168":"U","\u1E78":"U","\u016A":"U","\u1E7A":"U","\u016C":"U","\u00DC":"U","\u01DB":"U","\u01D7":"U","\u01D5":"U","\u01D9":"U","\u1EE6":"U","\u016E":"U","\u0170":"U","\u01D3":"U","\u0214":"U","\u0216":"U","\u01AF":"U","\u1EEA":"U","\u1EE8":"U","\u1EEE":"U","\u1EEC":"U","\u1EF0":"U","\u1EE4":"U","\u1E72":"U","\u0172":"U","\u1E76":"U","\u1E74":"U","\u0244":"U","\u24CB":"V","\uFF36":"V","\u1E7C":"V","\u1E7E":"V","\u01B2":"V","\uA75E":"V","\u0245":"V","\uA760":"VY","\u24CC":"W","\uFF37":"W","\u1E80":"W","\u1E82":"W","\u0174":"W","\u1E86":"W","\u1E84":"W","\u1E88":"W","\u2C72":"W","\u24CD":"X","\uFF38":"X","\u1E8A":"X","\u1E8C":"X","\u24CE":"Y","\uFF39":"Y","\u1EF2":"Y","\u00DD":"Y","\u0176":"Y","\u1EF8":"Y","\u0232":"Y","\u1E8E":"Y","\u0178":"Y","\u1EF6":"Y","\u1EF4":"Y","\u01B3":"Y","\u024E":"Y","\u1EFE":"Y","\u24CF":"Z","\uFF3A":"Z","\u0179":"Z","\u1E90":"Z","\u017B":"Z","\u017D":"Z","\u1E92":"Z","\u1E94":"Z","\u01B5":"Z","\u0224":"Z","\u2C7F":"Z","\u2C6B":"Z","\uA762":"Z","\u24D0":"a","\uFF41":"a","\u1E9A":"a","\u00E0":"a","\u00E1":"a","\u00E2":"a","\u1EA7":"a","\u1EA5":"a","\u1EAB":"a","\u1EA9":"a","\u00E3":"a","\u0101":"a","\u0103":"a","\u1EB1":"a","\u1EAF":"a","\u1EB5":"a","\u1EB3":"a","\u0227":"a","\u01E1":"a","\u00E4":"a","\u01DF":"a","\u1EA3":"a","\u00E5":"a","\u01FB":"a","\u01CE":"a","\u0201":"a","\u0203":"a","\u1EA1":"a","\u1EAD":"a","\u1EB7":"a","\u1E01":"a","\u0105":"a","\u2C65":"a","\u0250":"a","\uA733":"aa","\u00E6":"ae","\u01FD":"ae","\u01E3":"ae","\uA735":"ao","\uA737":"au","\uA739":"av","\uA73B":"av","\uA73D":"ay","\u24D1":"b","\uFF42":"b","\u1E03":"b","\u1E05":"b","\u1E07":"b","\u0180":"b","\u0183":"b","\u0253":"b","\u24D2":"c","\uFF43":"c","\u0107":"c","\u0109":"c","\u010B":"c","\u010D":"c","\u00E7":"c","\u1E09":"c","\u0188":"c","\u023C":"c","\uA73F":"c","\u2184":"c","\u24D3":"d","\uFF44":"d","\u1E0B":"d","\u010F":"d","\u1E0D":"d","\u1E11":"d","\u1E13":"d","\u1E0F":"d","\u0111":"d","\u018C":"d","\u0256":"d","\u0257":"d","\uA77A":"d","\u01F3":"dz","\u01C6":"dz","\u24D4":"e","\uFF45":"e","\u00E8":"e","\u00E9":"e","\u00EA":"e","\u1EC1":"e","\u1EBF":"e","\u1EC5":"e","\u1EC3":"e","\u1EBD":"e","\u0113":"e","\u1E15":"e","\u1E17":"e","\u0115":"e","\u0117":"e","\u00EB":"e","\u1EBB":"e","\u011B":"e","\u0205":"e","\u0207":"e","\u1EB9":"e","\u1EC7":"e","\u0229":"e","\u1E1D":"e","\u0119":"e","\u1E19":"e","\u1E1B":"e","\u0247":"e","\u025B":"e","\u01DD":"e","\u24D5":"f","\uFF46":"f","\u1E1F":"f","\u0192":"f","\uA77C":"f","\u24D6":"g","\uFF47":"g","\u01F5":"g","\u011D":"g","\u1E21":"g","\u011F":"g","\u0121":"g","\u01E7":"g","\u0123":"g","\u01E5":"g","\u0260":"g","\uA7A1":"g","\u1D79":"g","\uA77F":"g","\u24D7":"h","\uFF48":"h","\u0125":"h","\u1E23":"h","\u1E27":"h","\u021F":"h","\u1E25":"h","\u1E29":"h","\u1E2B":"h","\u1E96":"h","\u0127":"h","\u2C68":"h","\u2C76":"h","\u0265":"h","\u0195":"hv","\u24D8":"i","\uFF49":"i","\u00EC":"i","\u00ED":"i","\u00EE":"i","\u0129":"i","\u012B":"i","\u012D":"i","\u00EF":"i","\u1E2F":"i","\u1EC9":"i","\u01D0":"i","\u0209":"i","\u020B":"i","\u1ECB":"i","\u012F":"i","\u1E2D":"i","\u0268":"i","\u0131":"i","\u24D9":"j","\uFF4A":"j","\u0135":"j","\u01F0":"j","\u0249":"j","\u24DA":"k","\uFF4B":"k","\u1E31":"k","\u01E9":"k","\u1E33":"k","\u0137":"k","\u1E35":"k","\u0199":"k","\u2C6A":"k","\uA741":"k","\uA743":"k","\uA745":"k","\uA7A3":"k","\u24DB":"l","\uFF4C":"l","\u0140":"l","\u013A":"l","\u013E":"l","\u1E37":"l","\u1E39":"l","\u013C":"l","\u1E3D":"l","\u1E3B":"l","\u017F":"l","\u0142":"l","\u019A":"l","\u026B":"l","\u2C61":"l","\uA749":"l","\uA781":"l","\uA747":"l","\u01C9":"lj","\u24DC":"m","\uFF4D":"m","\u1E3F":"m","\u1E41":"m","\u1E43":"m","\u0271":"m","\u026F":"m","\u24DD":"n","\uFF4E":"n","\u01F9":"n","\u0144":"n","\u00F1":"n","\u1E45":"n","\u0148":"n","\u1E47":"n","\u0146":"n","\u1E4B":"n","\u1E49":"n","\u019E":"n","\u0272":"n","\u0149":"n","\uA791":"n","\uA7A5":"n","\u01CC":"nj","\u24DE":"o","\uFF4F":"o","\u00F2":"o","\u00F3":"o","\u00F4":"o","\u1ED3":"o","\u1ED1":"o","\u1ED7":"o","\u1ED5":"o","\u00F5":"o","\u1E4D":"o","\u022D":"o","\u1E4F":"o","\u014D":"o","\u1E51":"o","\u1E53":"o","\u014F":"o","\u022F":"o","\u0231":"o","\u00F6":"o","\u022B":"o","\u1ECF":"o","\u0151":"o","\u01D2":"o","\u020D":"o","\u020F":"o","\u01A1":"o","\u1EDD":"o","\u1EDB":"o","\u1EE1":"o","\u1EDF":"o","\u1EE3":"o","\u1ECD":"o","\u1ED9":"o","\u01EB":"o","\u01ED":"o","\u00F8":"o","\u01FF":"o","\u0254":"o","\uA74B":"o","\uA74D":"o","\u0275":"o","\u01A3":"oi","\u0223":"ou","\uA74F":"oo","\u24DF":"p","\uFF50":"p","\u1E55":"p","\u1E57":"p","\u01A5":"p","\u1D7D":"p","\uA751":"p","\uA753":"p","\uA755":"p","\u24E0":"q","\uFF51":"q","\u024B":"q","\uA757":"q","\uA759":"q","\u24E1":"r","\uFF52":"r","\u0155":"r","\u1E59":"r","\u0159":"r","\u0211":"r","\u0213":"r","\u1E5B":"r","\u1E5D":"r","\u0157":"r","\u1E5F":"r","\u024D":"r","\u027D":"r","\uA75B":"r","\uA7A7":"r","\uA783":"r","\u24E2":"s","\uFF53":"s","\u00DF":"s","\u015B":"s","\u1E65":"s","\u015D":"s","\u1E61":"s","\u0161":"s","\u1E67":"s","\u1E63":"s","\u1E69":"s","\u0219":"s","\u015F":"s","\u023F":"s","\uA7A9":"s","\uA785":"s","\u1E9B":"s","\u24E3":"t","\uFF54":"t","\u1E6B":"t","\u1E97":"t","\u0165":"t","\u1E6D":"t","\u021B":"t","\u0163":"t","\u1E71":"t","\u1E6F":"t","\u0167":"t","\u01AD":"t","\u0288":"t","\u2C66":"t","\uA787":"t","\uA729":"tz","\u24E4":"u","\uFF55":"u","\u00F9":"u","\u00FA":"u","\u00FB":"u","\u0169":"u","\u1E79":"u","\u016B":"u","\u1E7B":"u","\u016D":"u","\u00FC":"u","\u01DC":"u","\u01D8":"u","\u01D6":"u","\u01DA":"u","\u1EE7":"u","\u016F":"u","\u0171":"u","\u01D4":"u","\u0215":"u","\u0217":"u","\u01B0":"u","\u1EEB":"u","\u1EE9":"u","\u1EEF":"u","\u1EED":"u","\u1EF1":"u","\u1EE5":"u","\u1E73":"u","\u0173":"u","\u1E77":"u","\u1E75":"u","\u0289":"u","\u24E5":"v","\uFF56":"v","\u1E7D":"v","\u1E7F":"v","\u028B":"v","\uA75F":"v","\u028C":"v","\uA761":"vy","\u24E6":"w","\uFF57":"w","\u1E81":"w","\u1E83":"w","\u0175":"w","\u1E87":"w","\u1E85":"w","\u1E98":"w","\u1E89":"w","\u2C73":"w","\u24E7":"x","\uFF58":"x","\u1E8B":"x","\u1E8D":"x","\u24E8":"y","\uFF59":"y","\u1EF3":"y","\u00FD":"y","\u0177":"y","\u1EF9":"y","\u0233":"y","\u1E8F":"y","\u00FF":"y","\u1EF7":"y","\u1E99":"y","\u1EF5":"y","\u01B4":"y","\u024F":"y","\u1EFF":"y","\u24E9":"z","\uFF5A":"z","\u017A":"z","\u1E91":"z","\u017C":"z","\u017E":"z","\u1E93":"z","\u1E95":"z","\u01B6":"z","\u0225":"z","\u0240":"z","\u2C6C":"z","\uA763":"z","\u0386":"\u0391","\u0388":"\u0395","\u0389":"\u0397","\u038A":"\u0399","\u03AA":"\u0399","\u038C":"\u039F","\u038E":"\u03A5","\u03AB":"\u03A5","\u038F":"\u03A9","\u03AC":"\u03B1","\u03AD":"\u03B5","\u03AE":"\u03B7","\u03AF":"\u03B9","\u03CA":"\u03B9","\u0390":"\u03B9","\u03CC":"\u03BF","\u03CD":"\u03C5","\u03CB":"\u03C5","\u03B0":"\u03C5","\u03C9":"\u03C9","\u03C2":"\u03C3"}; $document = $(document); @@ -133,7 +132,7 @@ the specific language governing permissions and limitations under the Apache Lic function measureScrollbar () { var $template = $( MEASURE_SCROLLBAR_TEMPLATE ); - $template.appendTo('body'); + $template.appendTo(document.body); var dim = { width: $template.width() - $template[0].clientWidth, @@ -161,16 +160,16 @@ the specific language governing permissions and limitations under the Apache Lic } /** - * Splits the string into an array of values, trimming each value. An empty array is returned for nulls or empty + * Splits the string into an array of values, transforming each value. An empty array is returned for nulls or empty * strings * @param string * @param separator */ - function splitVal(string, separator) { + function splitVal(string, separator, transform) { var val, i, l; if (string === null || string.length < 1) return []; val = string.split(separator); - for (i = 0, l = val.length; i < l; i = i + 1) val[i] = $.trim(val[i]); + for (i = 0, l = val.length; i < l; i = i + 1) val[i] = transform(val[i]); return val; } @@ -194,10 +193,6 @@ the specific language governing permissions and limitations under the Apache Lic }); } - $document.on("mousemove", function (e) { - lastMousePosition.x = e.pageX; - lastMousePosition.y = e.pageY; - }); /** * filters mouse events so an event is fired only if the mouse moved. @@ -316,7 +311,7 @@ the specific language governing permissions and limitations under the Apache Lic whiteSpace: "nowrap" }); sizer.attr("class","select2-sizer"); - $("body").append(sizer); + $(document.body).append(sizer); } sizer.text(e.val()); return sizer.width(); @@ -325,27 +320,34 @@ the specific language governing permissions and limitations under the Apache Lic function syncCssClasses(dest, src, adapter) { var classes, replacements = [], adapted; - classes = dest.attr("class"); + classes = $.trim(dest.attr("class")); + if (classes) { classes = '' + classes; // for IE which returns object - $(classes.split(" ")).each2(function() { + + $(classes.split(/\s+/)).each2(function() { if (this.indexOf("select2-") === 0) { replacements.push(this); } }); } - classes = src.attr("class"); + + classes = $.trim(src.attr("class")); + if (classes) { classes = '' + classes; // for IE which returns object - $(classes.split(" ")).each2(function() { + + $(classes.split(/\s+/)).each2(function() { if (this.indexOf("select2-") !== 0) { adapted = adapter(this); + if (adapted) { replacements.push(adapted); } } }); } + dest.attr("class", replacements.join(" ")); } @@ -392,7 +394,7 @@ the specific language governing permissions and limitations under the Apache Lic * @param options.data a function(searchTerm, pageNumber, context) that should return an object containing query string parameters for the above url. * @param options.dataType request data type: ajax, jsonp, other datatypes supported by jQuery's $.ajax function or the transport function if specified * @param options.quietMillis (optional) milliseconds to wait before making the ajaxRequest, helps debounce the ajax function if invoked too often - * @param options.results a function(remoteData, pageNumber) that converts data returned form the remote request to the format expected by Select2. + * @param options.results a function(remoteData, pageNumber, query) that converts data returned form the remote request to the format expected by Select2. * The expected format is an object containing the following keys: * results array of objects that will be used as choices * more (optional) boolean indicating whether there are more results available @@ -439,7 +441,18 @@ the specific language governing permissions and limitations under the Apache Lic data: data, success: function (data) { // TODO - replace query.page with query so users have access to term, page, etc. - var results = options.results(data, query.page); + // added query as third paramter to keep backwards compatibility + var results = options.results(data, query.page, query); + query.callback(results); + }, + error: function(jqXHR, textStatus, errorThrown){ + var results = { + hasError: true, + jqXHR: jqXHR, + textStatus: textStatus, + errorThrown: errorThrown + }; + query.callback(results); } }); @@ -638,7 +651,7 @@ the specific language governing permissions and limitations under the Apache Lic function cleanupJQueryElements() { var self = this; - Array.prototype.forEach.call(arguments, function (element) { + $.each(arguments, function (i, element) { self[element].remove(); self[element] = null; }); @@ -686,12 +699,15 @@ the specific language governing permissions and limitations under the Apache Lic this.container = this.createContainer(); - this.liveRegion = $("", { - role: "status", - "aria-live": "polite" - }) - .addClass("select2-hidden-accessible") - .appendTo(document.body); + this.liveRegion = $('.select2-hidden-accessible'); + if (this.liveRegion.length == 0) { + this.liveRegion = $("", { + role: "status", + "aria-live": "polite" + }) + .addClass("select2-hidden-accessible") + .appendTo(document.body); + } this.containerId="s2id_"+(opts.element.attr("id") || "autogen"+nextUid()); this.containerEventName= this.containerId @@ -701,7 +717,7 @@ the specific language governing permissions and limitations under the Apache Lic this.container.attr("title", opts.element.attr("title")); - this.body = $("body"); + this.body = $(document.body); syncCssClasses(this.container, this.opts.element, this.opts.adaptContainerCssClass); @@ -833,13 +849,15 @@ the specific language governing permissions and limitations under the Apache Lic // abstract destroy: function () { - var element=this.opts.element, select2 = element.data("select2"); + var element=this.opts.element, select2 = element.data("select2"), self = this; this.close(); - if (element.length && element[0].detachEvent) { + if (element.length && element[0].detachEvent && self._sync) { element.each(function () { - this.detachEvent("onpropertychange", this._sync); + if (self._sync) { + this.detachEvent("onpropertychange", self._sync); + } }); } if (this.propertyObserver) { @@ -853,7 +871,7 @@ the specific language governing permissions and limitations under the Apache Lic select2.liveRegion.remove(); select2.dropdown.remove(); element - .removeClass("select2-offscreen") + .show() .removeData("select2") .off(".select2") .prop("autofocus", this.autofocus || false); @@ -924,6 +942,8 @@ the specific language governing permissions and limitations under the Apache Lic results = opts.sortResults(results, container, query); + // collect the created nodes for bulk append + var nodes = []; for (i = 0, l = results.length; i < l; i = i + 1) { result=results[i]; @@ -963,9 +983,11 @@ the specific language governing permissions and limitations under the Apache Lic } node.data("select2-data", result); - container.append(node); + nodes.push(node[0]); } + // bulk append the created nodes + container.append(nodes); liveRegion.text(opts.formatMatches(results.length)); }; @@ -1020,7 +1042,7 @@ the specific language governing permissions and limitations under the Apache Lic query.callback(data); }); - // this is needed because inside val() we construct choices from options and there id is hardcoded + // this is needed because inside val() we construct choices from options and their id is hardcoded opts.id=function(e) { return e.id; }; } else { if (!("query" in opts)) { @@ -1041,7 +1063,7 @@ the specific language governing permissions and limitations under the Apache Lic if (opts.initSelection === undefined) { opts.initSelection = function (element, callback) { var data = []; - $(splitVal(element.val(), opts.separator)).each(function () { + $(splitVal(element.val(), opts.separator, opts.transformVal)).each(function () { var obj = { id: this, text: this }, tags = opts.tags; if ($.isFunction(tags)) tags=tags(); @@ -1096,11 +1118,15 @@ the specific language governing permissions and limitations under the Apache Lic if (readonly === undefined) readonly = false; this.readonly(readonly); - syncCssClasses(this.container, this.opts.element, this.opts.adaptContainerCssClass); - this.container.addClass(evaluate(this.opts.containerCssClass, this.opts.element)); + if (this.container) { + syncCssClasses(this.container, this.opts.element, this.opts.adaptContainerCssClass); + this.container.addClass(evaluate(this.opts.containerCssClass, this.opts.element)); + } - syncCssClasses(this.dropdown, this.opts.element, this.opts.adaptDropdownCssClass); - this.dropdown.addClass(evaluate(this.opts.dropdownCssClass, this.opts.element)); + if (this.dropdown) { + syncCssClasses(this.dropdown, this.opts.element, this.opts.adaptDropdownCssClass); + this.dropdown.addClass(evaluate(this.opts.dropdownCssClass, this.opts.element)); + } }); @@ -1110,13 +1136,13 @@ the specific language governing permissions and limitations under the Apache Lic this.attachEvent("onpropertychange", self._sync); }); } - + // safari, chrome, firefox, IE11 observer = window.MutationObserver || window.WebKitMutationObserver|| window.MozMutationObserver; if (observer !== undefined) { if (this.propertyObserver) { delete this.propertyObserver; this.propertyObserver = null; } this.propertyObserver = new observer(function (mutations) { - mutations.forEach(self._sync); + $.each(mutations, self._sync); }); this.propertyObserver.observe(el.get(0), { attributes:true, subtree:false }); } @@ -1124,7 +1150,7 @@ the specific language governing permissions and limitations under the Apache Lic // abstract triggerSelect: function(data) { - var evt = $.Event("select2-selecting", { val: this.id(data), object: data }); + var evt = $.Event("select2-selecting", { val: this.id(data), object: data, choice: data }); this.opts.element.trigger(evt); return !evt.isDefaultPrevented(); }, @@ -1199,15 +1225,16 @@ the specific language governing permissions and limitations under the Apache Lic // abstract opened: function () { - return this.container.hasClass("select2-dropdown-open"); + return (this.container) ? this.container.hasClass("select2-dropdown-open") : false; }, // abstract positionDropdown: function() { var $dropdown = this.dropdown, - offset = this.container.offset(), - height = this.container.outerHeight(false), - width = this.container.outerWidth(false), + container = this.container, + offset = container.offset(), + height = container.outerHeight(false), + width = container.outerWidth(false), dropHeight = $dropdown.outerHeight(false), $window = $(window), windowWidth = $window.width(), @@ -1219,7 +1246,12 @@ the specific language governing permissions and limitations under the Apache Lic enoughRoomBelow = dropTop + dropHeight <= viewportBottom, enoughRoomAbove = (offset.top - dropHeight) >= $window.scrollTop(), dropWidth = $dropdown.outerWidth(false), - enoughRoomOnRight = dropLeft + dropWidth <= viewPortRight, + enoughRoomOnRight = function() { + return dropLeft + dropWidth <= viewPortRight; + }, + enoughRoomOnLeft = function() { + return offset.left + viewPortRight + container.outerWidth(false) > dropWidth; + }, aboveNow = $dropdown.hasClass("select2-drop-above"), bodyOffset, above, @@ -1254,7 +1286,6 @@ the specific language governing permissions and limitations under the Apache Lic dropTop = offset.top + height; dropLeft = offset.left; dropWidth = $dropdown.outerWidth(false); - enoughRoomOnRight = dropLeft + dropWidth <= viewPortRight; $dropdown.show(); // fix so the cursor does not move to the left within the search-textbox in IE @@ -1269,7 +1300,6 @@ the specific language governing permissions and limitations under the Apache Lic dropWidth = $dropdown.outerWidth(false) + (resultsListNode.scrollHeight === resultsListNode.clientHeight ? 0 : scrollBarDimensions.width); dropWidth > width ? width = dropWidth : dropWidth = width; dropHeight = $dropdown.outerHeight(false); - enoughRoomOnRight = dropLeft + dropWidth <= viewPortRight; } else { this.container.removeClass('select2-drop-auto-width'); @@ -1285,7 +1315,7 @@ the specific language governing permissions and limitations under the Apache Lic dropLeft -= bodyOffset.left; } - if (!enoughRoomOnRight) { + if (!enoughRoomOnRight() && enoughRoomOnLeft()) { dropLeft = offset.left + this.container.outerWidth(false) - dropWidth; } @@ -1344,6 +1374,12 @@ the specific language governing permissions and limitations under the Apache Lic this.opening(); + // Only bind the document mousemove when the dropdown is visible + $document.on("mousemove.select2Event", function (e) { + lastMousePosition.x = e.pageX; + lastMousePosition.y = e.pageY; + }); + return true; }, @@ -1368,7 +1404,7 @@ the specific language governing permissions and limitations under the Apache Lic // create the dropdown mask if doesn't already exist mask = $("#select2-drop-mask"); - if (mask.length == 0) { + if (mask.length === 0) { mask = $(document.createElement("div")); mask.attr("id","select2-drop-mask").attr("class","select2-drop-mask"); mask.hide(); @@ -1440,6 +1476,8 @@ the specific language governing permissions and limitations under the Apache Lic this.container.removeClass("select2-dropdown-open").removeClass("select2-container-active"); this.results.empty(); + // Now that the dropdown is closed, unbind the global document mousemove event + $document.off("mousemove.select2Event"); this.clearSearch(); this.search.removeClass("select2-active"); @@ -1468,7 +1506,7 @@ the specific language governing permissions and limitations under the Apache Lic // abstract ensureHighlightVisible: function () { - var results = this.results, children, index, child, hb, rb, y, more; + var results = this.results, children, index, child, hb, rb, y, more, topOffset; index = this.highlight(); @@ -1488,7 +1526,9 @@ the specific language governing permissions and limitations under the Apache Lic child = $(children[index]); - hb = child.offset().top + child.outerHeight(true); + topOffset = (child.offset() || {}).top || 0; + + hb = topOffset + child.outerHeight(true); // if this is the last child lets also make sure select2-more-results is visible if (index === children.length - 1) { @@ -1498,11 +1538,11 @@ the specific language governing permissions and limitations under the Apache Lic } } - rb = results.offset().top + results.outerHeight(true); + rb = results.offset().top + results.outerHeight(false); if (hb > rb) { results.scrollTop(results.scrollTop() + (hb - rb)); } - y = child.offset().top - results.offset().top; + y = topOffset - results.offset().top; // make sure the top of the element is visible if (y < 0 && child.css('display') != 'none' ) { @@ -1621,7 +1661,7 @@ the specific language governing permissions and limitations under the Apache Lic self.postprocessResults(data, false, false); if (data.more===true) { - more.detach().appendTo(results).text(evaluate(self.opts.formatLoadMore, self.opts.element, page+1)); + more.detach().appendTo(results).html(self.opts.escapeMarkup(evaluate(self.opts.formatLoadMore, self.opts.element, page+1))); window.setTimeout(function() { self.loadMoreIfNeeded(); }, 10); } else { more.remove(); @@ -1674,7 +1714,7 @@ the specific language governing permissions and limitations under the Apache Lic self.liveRegion.text(results.text()); } else { - self.liveRegion.text(self.opts.formatMatches(results.find('.select2-result-selectable').length)); + self.liveRegion.text(self.opts.formatMatches(results.find('.select2-result-selectable:not(".select2-selected")').length)); } } @@ -1749,6 +1789,12 @@ the specific language governing permissions and limitations under the Apache Lic return; } + // handle ajax error + if(data.hasError !== undefined && checkFormatter(opts.formatAjaxError, "formatAjaxError")) { + render("
      • " + evaluate(opts.formatAjaxError, opts.element, data.jqXHR, data.textStatus, data.errorThrown) + "
      • "); + return; + } + // save context, if any this.context = (data.context===undefined) ? null : data.context; // create a default choice and prepend it to the list @@ -2063,6 +2109,7 @@ the specific language governing permissions and limitations under the Apache Lic this.focusser.attr("id", "s2id_autogen"+idSuffix); elementLabel = $("label[for='" + this.opts.element.attr("id") + "']"); + this.opts.element.focus(this.bind(function () { this.focus(); })); this.focusser.prev() .text(elementLabel.text()) @@ -2084,6 +2131,9 @@ the specific language governing permissions and limitations under the Apache Lic this.search.on("keydown", this.bind(function (e) { if (!this.isInterfaceEnabled()) return; + // filter 229 keyCodes (input method editor is processing key input) + if (229 == e.keyCode) return; + if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) { // prevent the page from scrolling killEvent(e); @@ -2164,11 +2214,17 @@ the specific language governing permissions and limitations under the Apache Lic })); selection.on("mousedown touchstart", "abbr", this.bind(function (e) { - if (!this.isInterfaceEnabled()) return; + if (!this.isInterfaceEnabled()) { + return; + } + this.clear(); killEventImmediately(e); this.close(); - this.selection.focus(); + + if (this.selection) { + this.selection.focus(); + } })); selection.on("mousedown touchstart", this.bind(function (e) { @@ -2217,7 +2273,7 @@ the specific language governing permissions and limitations under the Apache Lic })); this.initContainerWidth(); - this.opts.element.addClass("select2-offscreen"); + this.opts.element.hide(); this.setPlaceholder(); }, @@ -2546,7 +2602,6 @@ the specific language governing permissions and limitations under the Apache Lic self=this; // TODO validate placeholder is a string if specified - if (opts.element.get(0).tagName.toLowerCase() === "select") { // install the selection initializer opts.initSelection = function (element, callback) { @@ -2561,7 +2616,7 @@ the specific language governing permissions and limitations under the Apache Lic } else if ("data" in opts) { // install default initSelection when applied to hidden input and data is local opts.initSelection = opts.initSelection || function (element, callback) { - var ids = splitVal(element.val(), opts.separator); + var ids = splitVal(element.val(), opts.separator, opts.transformVal); //search in data by array of ids, storing matching items in a list var matches = []; opts.query({ @@ -2638,8 +2693,7 @@ the specific language governing permissions and limitations under the Apache Lic this.selection = selection = this.container.find(selector); var _this = this; - this.selection.on("click", ".select2-search-choice:not(.select2-locked)", function (e) { - //killEvent(e); + this.selection.on("click", ".select2-container:not(.select2-container-disabled) .select2-search-choice:not(.select2-locked)", function (e) { _this.search[0].focus(); _this.selectChoice($(this)); }); @@ -2650,6 +2704,7 @@ the specific language governing permissions and limitations under the Apache Lic this.search.prev() .text($("label[for='" + this.opts.element.attr("id") + "']").text()) .attr('for', this.search.attr('id')); + this.opts.element.focus(this.bind(function () { this.focus(); })); this.search.on("input paste", this.bind(function() { if (this.search.attr('placeholder') && this.search.val().length == 0) return; @@ -2801,7 +2856,7 @@ the specific language governing permissions and limitations under the Apache Lic })); this.initContainerWidth(); - this.opts.element.addClass("select2-offscreen"); + this.opts.element.hide(); // set the placeholder if necessary this.clearSearch(); @@ -2936,7 +2991,7 @@ the specific language governing permissions and limitations under the Apache Lic // multi onSelect: function (data, options) { - if (!this.triggerSelect(data)) { return; } + if (!this.triggerSelect(data) || data.text === "") { return; } this.addSelectedChoice(data); @@ -3010,7 +3065,7 @@ the specific language governing permissions and limitations under the Apache Lic formatted=this.opts.formatSelection(data, choice.find("div"), this.opts.escapeMarkup); if (formatted != undefined) { - choice.find("div").replaceWith("
        "+formatted+"
        "); + choice.find("div").replaceWith($("
        ").html(formatted)); } cssClass=this.opts.formatSelectionCssClass(data, choice.find("div")); if (cssClass != undefined) { @@ -3108,7 +3163,7 @@ the specific language governing permissions and limitations under the Apache Lic } }); - if (this.highlight() == -1 && noHighlightUpdate !== false){ + if (this.highlight() == -1 && noHighlightUpdate !== false && this.opts.closeOnSelect === true){ self.highlight(0); } @@ -3165,7 +3220,7 @@ the specific language governing permissions and limitations under the Apache Lic return val === null ? [] : val; } else { val = this.opts.element.val(); - return splitVal(val, this.opts.separator); + return splitVal(val, this.opts.separator, this.opts.transformVal); } }, @@ -3195,7 +3250,7 @@ the specific language governing permissions and limitations under the Apache Lic if (equal(this.opts.id(current[i]), this.opts.id(old[j]))) { current.splice(i, 1); if(i>0){ - i--; + i--; } old.splice(j, 1); j--; @@ -3378,29 +3433,36 @@ the specific language governing permissions and limitations under the Apache Lic dropdownCssClass: "", formatResult: function(result, container, query, escapeMarkup) { var markup=[]; - markMatch(result.text, query.term, markup, escapeMarkup); + markMatch(this.text(result), query.term, markup, escapeMarkup); return markup.join(""); }, + transformVal: function(val) { + return $.trim(val); + }, formatSelection: function (data, container, escapeMarkup) { - return data ? escapeMarkup(data.text) : undefined; + return data ? escapeMarkup(this.text(data)) : undefined; }, sortResults: function (results, container, query) { return results; }, formatResultCssClass: function(data) {return data.css;}, formatSelectionCssClass: function(data, container) {return undefined;}, - formatMatches: function (matches) { return matches + " results are available, use up and down arrow keys to navigate."; }, - formatNoMatches: function () { return "No matches found"; }, - formatInputTooShort: function (input, min) { var n = min - input.length; return "Please enter " + n + " or more character" + (n == 1? "" : "s"); }, - formatInputTooLong: function (input, max) { var n = input.length - max; return "Please delete " + n + " character" + (n == 1? "" : "s"); }, - formatSelectionTooBig: function (limit) { return "You can only select " + limit + " item" + (limit == 1 ? "" : "s"); }, - formatLoadMore: function (pageNumber) { return "Loading more results…"; }, - formatSearching: function () { return "Searching…"; }, minimumResultsForSearch: 0, minimumInputLength: 0, maximumInputLength: null, maximumSelectionSize: 0, id: function (e) { return e == undefined ? null : e.id; }, + text: function (e) { + if (e && this.data && this.data.text) { + if ($.isFunction(this.data.text)) { + return this.data.text(e); + } else { + return e[this.data.text]; + } + } else { + return e.text; + } + }, matcher: function(term, text) { return stripDiacritics(''+text).toUpperCase().indexOf(stripDiacritics(''+term).toUpperCase()) >= 0; }, @@ -3434,6 +3496,21 @@ the specific language governing permissions and limitations under the Apache Lic } }; + $.fn.select2.locales = []; + + $.fn.select2.locales['en'] = { + formatMatches: function (matches) { if (matches === 1) { return "One result is available, press enter to select it."; } return matches + " results are available, use up and down arrow keys to navigate."; }, + formatNoMatches: function () { return "No matches found"; }, + formatAjaxError: function (jqXHR, textStatus, errorThrown) { return "Loading failed"; }, + formatInputTooShort: function (input, min) { var n = min - input.length; return "Please enter " + n + " or more character" + (n == 1 ? "" : "s"); }, + formatInputTooLong: function (input, max) { var n = input.length - max; return "Please delete " + n + " character" + (n == 1 ? "" : "s"); }, + formatSelectionTooBig: function (limit) { return "You can only select " + limit + " item" + (limit == 1 ? "" : "s"); }, + formatLoadMore: function (pageNumber) { return "Loading more results…"; }, + formatSearching: function () { return "Searching…"; } + }; + + $.extend($.fn.select2.defaults, $.fn.select2.locales['en']); + $.fn.select2.ajaxDefaults = { transport: $.ajax, params: { diff --git a/vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.ar.yml b/vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.ar.yml new file mode 100644 index 0000000000..60f365ad68 --- /dev/null +++ b/vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.ar.yml @@ -0,0 +1,12 @@ +# encoding: utf-8 +# +# Never edit this file. It will be overwritten when translations are pulled from Transifex. +# +# To work with us on translations, join this project: +# https://www.transifex.com/projects/p/discourse-org/ + +ar: + site_settings: + enable_imgur: "الرجاء تمكين حسابك في imgur. لا تقم بتحميل الملفات محلياً" + imgur_client_id: "حسابك لدى imgur.com مطلوب لتحميل الصور" + imgur_client_secret: "حسابك في imgur.com سرّي , غير مطلوب الآن لرفع الصور, لكن ربما سيكون مطلوباً في وقت لاحق" diff --git a/vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.ko.yml b/vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.ko.yml index ecf6fffaf1..c0c2107c64 100644 --- a/vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.ko.yml +++ b/vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.ko.yml @@ -5,4 +5,8 @@ # To work with us on translations, join this project: # https://www.transifex.com/projects/p/discourse-org/ -ko_KR: {} +ko: + site_settings: + enable_imgur: "로컬에 이미지를 저장하지 않고 imgur api를 통해 이미지 업로드 활성화. " + imgur_client_id: "이미지 업로드를 위해 imgur.com의 client ID 가 필요합니다." + imgur_client_secret: "imgur.com의 client secret 키. 현재 이미지 업로드를 위해 꼭 필요하지는 않지만 언젠가를 위해." diff --git a/vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.pt.yml b/vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.pt.yml index 6d85dde87a..a1dfb05666 100644 --- a/vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.pt.yml +++ b/vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.pt.yml @@ -8,5 +8,5 @@ pt: site_settings: enable_imgur: "Ativar a API do imgur para carregamento, não armazenar ficheiros localmente" - imgur_client_id: "O seu ID de cliente do imgur.com, necessário para activar o carregamento de imagens" + imgur_client_id: "O seu ID de cliente do imgur.com, necessário para ativar o carregamento de imagens" imgur_client_secret: "A sua chave privada de cliente do imgur.com. Não é necessária de momento para o carregamento de imagens, mas pode vir a ser no futuro." diff --git a/vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.te.yml b/vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.te.yml new file mode 100644 index 0000000000..9f65aef693 --- /dev/null +++ b/vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.te.yml @@ -0,0 +1,12 @@ +# encoding: utf-8 +# +# Never edit this file. It will be overwritten when translations are pulled from Transifex. +# +# To work with us on translations, join this project: +# https://www.transifex.com/projects/p/discourse-org/ + +te: + site_settings: + enable_imgur: "దస్త్రాలు స్థానికంగా ఉంచవద్దు. imgur ను చేతనం చేయి" + imgur_client_id: "మీ imgur.com క్లైంట్ ఐడీ. బొమ్మ ఎగుమతి ప్రమేయానికి కావాలి." + imgur_client_secret: "మీ imgur.com క్లైంట్ రహస్యం. ప్రస్తుతం బొమ్మ ఎగుమతించు ప్రమేయానికి అవసరం లేదు, కానీ భవిష్యత్తులో అవసరం కావచ్చు." diff --git a/vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.tr_TR.yml b/vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.tr_TR.yml new file mode 100644 index 0000000000..a9c90a45cc --- /dev/null +++ b/vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.tr_TR.yml @@ -0,0 +1,12 @@ +# encoding: utf-8 +# +# Never edit this file. It will be overwritten when translations are pulled from Transifex. +# +# To work with us on translations, join this project: +# https://www.transifex.com/projects/p/discourse-org/ + +tr_TR: + site_settings: + enable_imgur: "Yükleme için imgur api'sini etkinleştirin, dosyaları yerel olarak barındırmayın" + imgur_client_id: "Imgur.com kullanıcı ID'niz, fotoğraf yükleme işlevi için gerekli" + imgur_client_secret: "Imgur.com kullanıcı anahtarınız. Fotoğraf yükleme işlevi için şu an gerekli değil, ama ileride bazı durumlarda gerekli olabilir." diff --git a/vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.zh_CN.yml b/vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.zh_CN.yml index 45e75b7b01..19094bf434 100644 --- a/vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.zh_CN.yml +++ b/vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.zh_CN.yml @@ -8,5 +8,5 @@ zh_CN: site_settings: enable_imgur: "上传文件到 imgur,不在本地存储。" - imgur_client_id: "您的 imgur.com client ID,如果启用上传则必填" - imgur_client_secret: "您的 imgur.com 客户密钥。目前不需要此密钥即可使用上传图片功能,但可能在以后会用到。" + imgur_client_id: "你的 imgur.com client ID,如果启用上传则必填" + imgur_client_secret: "你的 imgur.com 客户密钥。目前不需要此密钥即可使用上传图片功能,但可能在以后会用到。" diff --git a/vendor/gems/rails_multisite/lib/rails_multisite/connection_management.rb b/vendor/gems/rails_multisite/lib/rails_multisite/connection_management.rb index 5cb58140c7..e5ef70c1b0 100644 --- a/vendor/gems/rails_multisite/lib/rails_multisite/connection_management.rb +++ b/vendor/gems/rails_multisite/lib/rails_multisite/connection_management.rb @@ -158,15 +158,20 @@ module RailsMultisite @app = app end - def call(env) + def self.host(env) request = Rack::Request.new(env) + request['__ws'] || request.host + end + + def call(env) + host = self.class.host(env) begin #TODO: add a callback so users can simply go to a domain to register it, or something - return [404, {}, ["not found"]] unless @@host_spec_cache[request.host] + return [404, {}, ["not found"]] unless @@host_spec_cache[host] ActiveRecord::Base.connection_handler.clear_active_connections! - self.class.establish_connection(:host => request['__ws'] || request.host) + self.class.establish_connection(:host => host) @app.call(env) ensure ActiveRecord::Base.connection_handler.clear_active_connections!