diff --git a/.eslintignore b/.eslintignore index a520c62c91..644318acf6 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,11 +1,9 @@ app/assets/javascripts/env.js app/assets/javascripts/main_include.js app/assets/javascripts/main_include_admin.js -app/assets/javascripts/preload_store.js app/assets/javascripts/pagedown_custom.js app/assets/javascripts/vendor.js app/assets/javascripts/locales/i18n.js -app/assets/javascripts/defer/html-sanitizer-bundle.js app/assets/javascripts/ember-addons/ app/assets/javascripts/discourse/lib/autosize.js.es6 lib/javascripts/locale/ diff --git a/.eslintrc b/.eslintrc index 2b3ed3ba7d..4148a259ba 100644 --- a/.eslintrc +++ b/.eslintrc @@ -14,7 +14,6 @@ "RSVP":true, "Discourse":true, "Em":true, - "PreloadStore":true, "Handlebars":true, "I18n":true, "bootbox":true, @@ -77,6 +76,7 @@ "no-eval": 2, "no-extend-native": 2, "no-extra-parens": 0, + "no-inner-declarations": 2, "no-irregular-whitespace": 2, "no-iterator": 2, "no-loop-func": 2, diff --git a/.gitignore b/.gitignore index d88f2ca164..9dfa377bcf 100644 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,7 @@ log/ !/plugins/lazyYT/ !/plugins/poll/ !/plugins/discourse-details/ +!/plugins/discourse-nginx-performance-report /plugins/*/auto_generated/ /spec/fixtures/plugins/my_plugin/auto_generated diff --git a/Gemfile.lock b/Gemfile.lock index 29e84d147c..bd46133d2f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,38 +1,38 @@ GEM remote: https://rubygems.org/ specs: - actionmailer (4.2.6) - actionpack (= 4.2.6) - actionview (= 4.2.6) - activejob (= 4.2.6) + actionmailer (4.2.7) + actionpack (= 4.2.7) + actionview (= 4.2.7) + activejob (= 4.2.7) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 1.0, >= 1.0.5) - actionpack (4.2.6) - actionview (= 4.2.6) - activesupport (= 4.2.6) + actionpack (4.2.7) + actionview (= 4.2.7) + activesupport (= 4.2.7) rack (~> 1.6) rack-test (~> 0.6.2) rails-dom-testing (~> 1.0, >= 1.0.5) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (4.2.6) - activesupport (= 4.2.6) + actionview (4.2.7) + activesupport (= 4.2.7) builder (~> 3.1) erubis (~> 2.7.0) rails-dom-testing (~> 1.0, >= 1.0.5) rails-html-sanitizer (~> 1.0, >= 1.0.2) active_model_serializers (0.8.3) activemodel (>= 3.0) - activejob (4.2.6) - activesupport (= 4.2.6) + activejob (4.2.7) + activesupport (= 4.2.7) globalid (>= 0.3.0) - activemodel (4.2.6) - activesupport (= 4.2.6) + activemodel (4.2.7) + activesupport (= 4.2.7) builder (~> 3.1) - activerecord (4.2.6) - activemodel (= 4.2.6) - activesupport (= 4.2.6) + activerecord (4.2.7) + activemodel (= 4.2.7) + activesupport (= 4.2.7) arel (~> 6.0) - activesupport (4.2.6) + activesupport (4.2.7) i18n (~> 0.7) json (~> 1.7, >= 1.7.7) minitest (~> 5.1) @@ -42,12 +42,12 @@ GEM activerecord (>= 3.2, < 6.0) rake (>= 10.4, < 12.0) arel (6.0.3) - aws-sdk (2.3.7) - aws-sdk-resources (= 2.3.7) - aws-sdk-core (2.3.7) + aws-sdk (2.3.22) + aws-sdk-resources (= 2.3.22) + aws-sdk-core (2.3.22) jmespath (~> 1.0) - aws-sdk-resources (2.3.7) - aws-sdk-core (= 2.3.7) + aws-sdk-resources (2.3.22) + aws-sdk-core (= 2.3.22) babel-source (5.8.34) babel-transpiler (0.7.0) babel-source (>= 4.0, < 6) @@ -136,14 +136,12 @@ GEM progress (~> 3.0, >= 3.0.1) image_size (1.4.1) in_threads (1.3.1) - jmespath (1.2.4) - json_pure (>= 1.8.1) + jmespath (1.3.0) jquery-rails (4.0.5) rails-dom-testing (~> 1.0) railties (>= 4.2.0) thor (>= 0.14, < 2.0) json (1.8.3) - json_pure (1.8.3) jwt (1.5.2) kgio (2.10.0) librarian (0.1.2) @@ -162,7 +160,7 @@ GEM rack (>= 1.1.3) metaclass (0.0.4) method_source (0.8.2) - mime-types (2.99.1) + mime-types (2.99.2) mini_portile2 (2.1.0) mini_racer (0.1.3) libv8 (~> 5.0) @@ -218,7 +216,7 @@ GEM omniauth-twitter (1.2.1) json (~> 1.3) omniauth-oauth (~> 1.1) - onebox (1.5.42) + onebox (1.5.43) htmlentities (~> 4.3.4) moneta (~> 0.8) multi_json (~> 1.11) @@ -250,16 +248,16 @@ GEM rack rack-test (0.6.3) rack (>= 1.0) - rails (4.2.6) - actionmailer (= 4.2.6) - actionpack (= 4.2.6) - actionview (= 4.2.6) - activejob (= 4.2.6) - activemodel (= 4.2.6) - activerecord (= 4.2.6) - activesupport (= 4.2.6) + rails (4.2.7) + actionmailer (= 4.2.7) + actionpack (= 4.2.7) + actionview (= 4.2.7) + activejob (= 4.2.7) + activemodel (= 4.2.7) + activerecord (= 4.2.7) + activesupport (= 4.2.7) bundler (>= 1.3.0, < 2.0) - railties (= 4.2.6) + railties (= 4.2.7) sprockets-rails rails-deprecated_sanitizer (1.0.3) activesupport (>= 4.2.0.alpha) @@ -272,13 +270,13 @@ GEM rails-observers (0.1.2) activemodel (~> 4.0) rails_multisite (1.0.4) - railties (4.2.6) - actionpack (= 4.2.6) - activesupport (= 4.2.6) + railties (4.2.7) + actionpack (= 4.2.7) + activesupport (= 4.2.7) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) raindrops (0.16.0) - rake (11.1.2) + rake (11.2.2) rake-compiler (0.9.9) rake rb-fsevent (0.9.7) @@ -371,10 +369,10 @@ GEM spork-rails (4.0.0) rails (>= 3.0.0, < 5) spork (>= 1.0rc0) - sprockets (3.6.0) + sprockets (3.6.3) concurrent-ruby (~> 1.0) rack (> 1, < 3) - sprockets-rails (3.0.4) + sprockets-rails (3.1.1) actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) diff --git a/app/assets/javascripts/admin/components/ace-editor.js.es6 b/app/assets/javascripts/admin/components/ace-editor.js.es6 index 755dc574b2..f9be641640 100644 --- a/app/assets/javascripts/admin/components/ace-editor.js.es6 +++ b/app/assets/javascripts/admin/components/ace-editor.js.es6 @@ -1,5 +1,6 @@ /* global ace:true */ import loadScript from 'discourse/lib/load-script'; +import { escapeExpression } from 'discourse/lib/utilities'; export default Ember.Component.extend({ mode: 'css', @@ -16,7 +17,7 @@ export default Ember.Component.extend({ render(buffer) { buffer.push("
"); if (this.get('content')) { - buffer.push(Discourse.Utilities.escapeExpression(this.get('content'))); + buffer.push(escapeExpression(this.get('content'))); } buffer.push("
"); }, diff --git a/app/assets/javascripts/admin/components/ip-lookup.js.es6 b/app/assets/javascripts/admin/components/ip-lookup.js.es6 index 63f61dcc98..0c9b73d54a 100644 --- a/app/assets/javascripts/admin/components/ip-lookup.js.es6 +++ b/app/assets/javascripts/admin/components/ip-lookup.js.es6 @@ -1,3 +1,4 @@ +import { ajax } from 'discourse/lib/ajax'; export default Ember.Component.extend({ classNames: ["ip-lookup"], @@ -23,7 +24,7 @@ export default Ember.Component.extend({ this.set("show", true); if (!this.get("location")) { - Discourse.ajax("/admin/users/ip-info", { + ajax("/admin/users/ip-info", { data: { ip: this.get("ip") } }).then(function (location) { self.set("location", Em.Object.create(location)); @@ -39,7 +40,7 @@ export default Ember.Component.extend({ "order": "trust_level DESC" }; - Discourse.ajax("/admin/users/total-others-with-same-ip", { data }).then(function (result) { + ajax("/admin/users/total-others-with-same-ip", { data }).then(function (result) { self.set("totalOthersWithSameIP", result.total); }); @@ -67,7 +68,7 @@ export default Ember.Component.extend({ totalOthersWithSameIP: null }); - Discourse.ajax("/admin/users/delete-others-with-same-ip.json", { + ajax("/admin/users/delete-others-with-same-ip.json", { type: "DELETE", data: { "ip": self.get("ip"), diff --git a/app/assets/javascripts/admin/controllers/admin-backups-index.js.es6 b/app/assets/javascripts/admin/controllers/admin-backups-index.js.es6 index 1617e6133a..98f76405a2 100644 --- a/app/assets/javascripts/admin/controllers/admin-backups-index.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-backups-index.js.es6 @@ -1,3 +1,4 @@ +import { ajax } from 'discourse/lib/ajax'; export default Ember.ArrayController.extend({ needs: ["adminBackups"], status: Ember.computed.alias("controllers.adminBackups"), @@ -39,7 +40,7 @@ export default Ember.ArrayController.extend({ _toggleReadOnlyMode(enable) { var site = this.site; - Discourse.ajax("/admin/backups/readonly", { + ajax("/admin/backups/readonly", { type: "PUT", data: { enable: enable } }).then(function() { 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 b4006391b2..fce0cb891e 100644 --- a/app/assets/javascripts/admin/controllers/admin-email-index.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-email-index.js.es6 @@ -1,3 +1,4 @@ +import { ajax } from 'discourse/lib/ajax'; export default Ember.Controller.extend({ /** @@ -29,7 +30,7 @@ export default Ember.Controller.extend({ }); var self = this; - Discourse.ajax("/admin/email/test", { + ajax("/admin/email/test", { type: 'POST', data: { email_address: this.get('testEmailAddress') } }).then(function () { diff --git a/app/assets/javascripts/admin/controllers/admin-emojis.js.es6 b/app/assets/javascripts/admin/controllers/admin-emojis.js.es6 index 5277f2feb2..b111a5952b 100644 --- a/app/assets/javascripts/admin/controllers/admin-emojis.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-emojis.js.es6 @@ -1,3 +1,4 @@ +import { ajax } from 'discourse/lib/ajax'; export default Ember.ArrayController.extend({ sortProperties: ["name"], @@ -15,7 +16,7 @@ export default Ember.ArrayController.extend({ I18n.t("yes_value"), function(destroy) { if (destroy) { - return Discourse.ajax("/admin/customize/emojis/" + emoji.get("name"), { type: "DELETE" }).then(function() { + return ajax("/admin/customize/emojis/" + emoji.get("name"), { type: "DELETE" }).then(function() { self.removeObject(emoji); }); } diff --git a/app/assets/javascripts/admin/controllers/admin-groups-bulk.js.es6 b/app/assets/javascripts/admin/controllers/admin-groups-bulk.js.es6 index 6628a6fa72..bf60519c5b 100644 --- a/app/assets/javascripts/admin/controllers/admin-groups-bulk.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-groups-bulk.js.es6 @@ -1,3 +1,4 @@ +import { ajax } from 'discourse/lib/ajax'; import computed from 'ember-addons/ember-computed-decorators'; import { popupAjaxError } from 'discourse/lib/ajax-error'; @@ -20,7 +21,7 @@ export default Ember.Controller.extend({ .reject(x => x.length === 0); this.set('saving', true); - Discourse.ajax('/admin/groups/bulk', { + ajax('/admin/groups/bulk', { data: { users, group_id: this.get('groupId') }, method: 'PUT' }).then(() => { diff --git a/app/assets/javascripts/admin/controllers/admin-groups-type.js.es6 b/app/assets/javascripts/admin/controllers/admin-groups-type.js.es6 index eba51de4ee..9a5962cccf 100644 --- a/app/assets/javascripts/admin/controllers/admin-groups-type.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-groups-type.js.es6 @@ -1,3 +1,4 @@ +import { ajax } from 'discourse/lib/ajax'; export default Ember.ArrayController.extend({ sortProperties: ['name'], refreshingAutoGroups: false, @@ -9,7 +10,7 @@ export default Ember.ArrayController.extend({ refreshAutoGroups: function(){ var self = this; this.set('refreshingAutoGroups', true); - Discourse.ajax('/admin/groups/refresh_automatic_groups', {type: 'POST'}).then(function() { + 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-user-index.js.es6 b/app/assets/javascripts/admin/controllers/admin-user-index.js.es6 index a5e34fc257..0c1cb7abb3 100644 --- a/app/assets/javascripts/admin/controllers/admin-user-index.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-user-index.js.es6 @@ -1,3 +1,4 @@ +import { ajax } from 'discourse/lib/ajax'; import CanCheckEmails from 'discourse/mixins/can-check-emails'; import { propertyNotEqual, setting } from 'discourse/lib/computed'; @@ -38,7 +39,7 @@ export default Ember.Controller.extend(CanCheckEmails, { saveTitle() { const self = this; - return Discourse.ajax("/users/" + this.get('model.username').toLowerCase(), { + return ajax("/users/" + this.get('model.username').toLowerCase(), { data: {title: this.get('userTitleValue')}, type: 'PUT' }).catch(function(e) { @@ -68,7 +69,7 @@ export default Ember.Controller.extend(CanCheckEmails, { savePrimaryGroup() { const self = this; - return Discourse.ajax("/admin/users/" + this.get('model.id') + "/primary_group", { + return ajax("/admin/users/" + this.get('model.id') + "/primary_group", { type: 'PUT', data: {primary_group_id: this.get('model.primary_group_id')} }).then(function () { diff --git a/app/assets/javascripts/admin/controllers/modals/admin-badge-preview.js.es6 b/app/assets/javascripts/admin/controllers/modals/admin-badge-preview.js.es6 index d26585f348..8f93884c60 100644 --- a/app/assets/javascripts/admin/controllers/modals/admin-badge-preview.js.es6 +++ b/app/assets/javascripts/admin/controllers/modals/admin-badge-preview.js.es6 @@ -1,3 +1,5 @@ +import { escapeExpression } from 'discourse/lib/utilities'; + export default Ember.Controller.extend({ needs: ['modal'], @@ -22,7 +24,7 @@ export default Ember.Controller.extend({ returned = "
";
 
     _.each(raw, function(linehash) {
-      returned += Discourse.Utilities.escapeExpression(linehash["QUERY PLAN"]);
+      returned += escapeExpression(linehash["QUERY PLAN"]);
       returned += "
"; }); @@ -32,7 +34,7 @@ export default Ember.Controller.extend({ processed_sample: Ember.computed.map('model.sample', function(grant) { var i18nKey = 'admin.badges.preview.grant.with', - i18nParams = { username: Discourse.Utilities.escapeExpression(grant.username) }; + i18nParams = { username: escapeExpression(grant.username) }; if (grant.post_id) { i18nKey += "_post"; @@ -41,7 +43,7 @@ export default Ember.Controller.extend({ if (grant.granted_at) { i18nKey += "_time"; - i18nParams.time = Discourse.Utilities.escapeExpression(moment(grant.granted_at).format(I18n.t('dates.long_with_year'))); + i18nParams.time = escapeExpression(moment(grant.granted_at).format(I18n.t('dates.long_with_year'))); } return I18n.t(i18nKey, i18nParams); diff --git a/app/assets/javascripts/admin/controllers/modals/admin-edit-badge-groupings.js.es6 b/app/assets/javascripts/admin/controllers/modals/admin-edit-badge-groupings.js.es6 index 6c0f6b9ccd..086080c173 100644 --- a/app/assets/javascripts/admin/controllers/modals/admin-edit-badge-groupings.js.es6 +++ b/app/assets/javascripts/admin/controllers/modals/admin-edit-badge-groupings.js.es6 @@ -1,3 +1,4 @@ +import { ajax } from 'discourse/lib/ajax'; export default Ember.Controller.extend({ needs: ['modal'], @@ -57,7 +58,7 @@ export default Ember.Controller.extend({ const groupIds = items.map(function(i){return i.get("id") || -1;}); const names = items.map(function(i){return i.get("name");}); - Discourse.ajax('/admin/badges/badge_groupings',{ + ajax('/admin/badges/badge_groupings',{ data: {ids: groupIds, names: names}, method: 'POST' }).then(function(data){ diff --git a/app/assets/javascripts/admin/helpers/preserve-newlines.js.es6 b/app/assets/javascripts/admin/helpers/preserve-newlines.js.es6 index 5f70a2acc8..73bac43379 100644 --- a/app/assets/javascripts/admin/helpers/preserve-newlines.js.es6 +++ b/app/assets/javascripts/admin/helpers/preserve-newlines.js.es6 @@ -1,3 +1,4 @@ import { htmlHelper } from 'discourse/lib/helpers'; +import { escapeExpression } from 'discourse/lib/utilities'; -export default htmlHelper(str => Discourse.Utilities.escapeExpression(str).replace(/\n/g, "
")); +export default htmlHelper(str => escapeExpression(str).replace(/\n/g, "
")); diff --git a/app/assets/javascripts/admin/models/admin-dashboard.js.es6 b/app/assets/javascripts/admin/models/admin-dashboard.js.es6 index 866012b952..ac44a7677f 100644 --- a/app/assets/javascripts/admin/models/admin-dashboard.js.es6 +++ b/app/assets/javascripts/admin/models/admin-dashboard.js.es6 @@ -1,3 +1,4 @@ +import { ajax } from 'discourse/lib/ajax'; const AdminDashboard = Discourse.Model.extend({}); @@ -11,7 +12,7 @@ AdminDashboard.reopenClass({ @return {jqXHR} a jQuery Promise object **/ find: function() { - return Discourse.ajax("/admin/dashboard.json").then(function(json) { + return ajax("/admin/dashboard.json").then(function(json) { var model = AdminDashboard.create(json); model.set('loaded', true); return model; @@ -26,7 +27,7 @@ AdminDashboard.reopenClass({ @return {jqXHR} a jQuery Promise object **/ fetchProblems: function() { - return Discourse.ajax("/admin/dashboard/problems.json", { + return ajax("/admin/dashboard/problems.json", { type: 'GET', dataType: 'json' }).then(function(json) { diff --git a/app/assets/javascripts/admin/models/admin-user.js.es6 b/app/assets/javascripts/admin/models/admin-user.js.es6 index c97f5ea910..d804894770 100644 --- a/app/assets/javascripts/admin/models/admin-user.js.es6 +++ b/app/assets/javascripts/admin/models/admin-user.js.es6 @@ -1,3 +1,4 @@ +import { ajax } from 'discourse/lib/ajax'; import computed from 'ember-addons/ember-computed-decorators'; import { propertyNotEqual } from 'discourse/lib/computed'; import { popupAjaxError } from 'discourse/lib/ajax-error'; @@ -40,7 +41,7 @@ const AdminUser = Discourse.User.extend({ canResetBounceScore: Ember.computed.gt("bounce_score", 0), resetBounceScore() { - return Discourse.ajax(`/admin/users/${this.get("id")}/reset_bounce_score`, { + return ajax(`/admin/users/${this.get("id")}/reset_bounce_score`, { type: 'POST' }).then(() => this.setProperties({ "bounce_score": 0, @@ -50,7 +51,7 @@ const AdminUser = Discourse.User.extend({ generateApiKey() { const self = this; - return Discourse.ajax("/admin/users/" + this.get('id') + "/generate_api_key", { + return ajax("/admin/users/" + this.get('id') + "/generate_api_key", { type: 'POST' }).then(function (result) { const apiKey = ApiKey.create(result.api_key); @@ -60,20 +61,20 @@ const AdminUser = Discourse.User.extend({ }, groupAdded(added) { - return Discourse.ajax("/admin/users/" + this.get('id') + "/groups", { + return ajax("/admin/users/" + this.get('id') + "/groups", { type: 'POST', data: { group_id: added.id } }).then(() => this.get('groups').pushObject(added)); }, groupRemoved(groupId) { - return Discourse.ajax("/admin/users/" + this.get('id') + "/groups/" + groupId, { + return ajax("/admin/users/" + this.get('id') + "/groups/" + groupId, { type: 'DELETE' }).then(() => this.set('groups.[]', this.get('groups').rejectBy("id", groupId))); }, revokeApiKey() { - return Discourse.ajax("/admin/users/" + this.get('id') + "/revoke_api_key", { + return ajax("/admin/users/" + this.get('id') + "/revoke_api_key", { type: 'DELETE' }).then(() => this.set('api_key', null)); }, @@ -104,7 +105,7 @@ const AdminUser = Discourse.User.extend({ "label": ' ' + I18n.t("admin.user.delete_all_posts"), "class": "btn btn-danger", "callback": function() { - Discourse.ajax("/admin/users/" + user.get('id') + "/delete_all_posts", { + ajax("/admin/users/" + user.get('id') + "/delete_all_posts", { type: 'PUT' }).then(() => user.set('post_count', 0)); } @@ -114,7 +115,7 @@ const AdminUser = Discourse.User.extend({ revokeAdmin() { const self = this; - return Discourse.ajax("/admin/users/" + this.get('id') + "/revoke_admin", { + return ajax("/admin/users/" + this.get('id') + "/revoke_admin", { type: 'PUT' }).then(function() { self.setProperties({ @@ -127,7 +128,7 @@ const AdminUser = Discourse.User.extend({ grantAdmin() { const self = this; - return Discourse.ajax("/admin/users/" + this.get('id') + "/grant_admin", { + return ajax("/admin/users/" + this.get('id') + "/grant_admin", { type: 'PUT' }).then(function() { self.setProperties({ @@ -140,7 +141,7 @@ const AdminUser = Discourse.User.extend({ revokeModeration() { const self = this; - return Discourse.ajax("/admin/users/" + this.get('id') + "/revoke_moderation", { + return ajax("/admin/users/" + this.get('id') + "/revoke_moderation", { type: 'PUT' }).then(function() { self.setProperties({ @@ -153,7 +154,7 @@ const AdminUser = Discourse.User.extend({ grantModeration() { const self = this; - return Discourse.ajax("/admin/users/" + this.get('id') + "/grant_moderation", { + return ajax("/admin/users/" + this.get('id') + "/grant_moderation", { type: 'PUT' }).then(function() { self.setProperties({ @@ -165,14 +166,14 @@ const AdminUser = Discourse.User.extend({ }, refreshBrowsers() { - return Discourse.ajax("/admin/users/" + this.get('id') + "/refresh_browsers", { + return ajax("/admin/users/" + this.get('id') + "/refresh_browsers", { type: 'POST' }).finally(() => bootbox.alert(I18n.t("admin.user.refresh_browsers_message"))); }, approve() { const self = this; - return Discourse.ajax("/admin/users/" + this.get('id') + "/approve", { + return ajax("/admin/users/" + this.get('id') + "/approve", { type: 'PUT' }).then(function() { self.setProperties({ @@ -190,7 +191,7 @@ const AdminUser = Discourse.User.extend({ dirty: propertyNotEqual('originalTrustLevel', 'trustLevel.id'), saveTrustLevel() { - return Discourse.ajax("/admin/users/" + this.id + "/trust_level", { + return ajax("/admin/users/" + this.id + "/trust_level", { type: 'PUT', data: { level: this.get('trustLevel.id') } }).then(function() { @@ -210,7 +211,7 @@ const AdminUser = Discourse.User.extend({ }, lockTrustLevel(locked) { - return Discourse.ajax("/admin/users/" + this.id + "/trust_level_lock", { + return ajax("/admin/users/" + this.id + "/trust_level_lock", { type: 'PUT', data: { locked: !!locked } }).then(function() { @@ -239,14 +240,14 @@ const AdminUser = Discourse.User.extend({ }.property('suspended_till', 'suspended_at'), suspend(duration, reason) { - return Discourse.ajax("/admin/users/" + this.id + "/suspend", { + return ajax("/admin/users/" + this.id + "/suspend", { type: 'PUT', data: { duration: duration, reason: reason } }); }, unsuspend() { - return Discourse.ajax("/admin/users/" + this.id + "/unsuspend", { + return ajax("/admin/users/" + this.id + "/unsuspend", { type: 'PUT' }).then(function() { window.location.reload(); @@ -257,7 +258,7 @@ const AdminUser = Discourse.User.extend({ }, log_out() { - return Discourse.ajax("/admin/users/" + this.id + "/log_out", { + return ajax("/admin/users/" + this.id + "/log_out", { type: 'POST', data: { username_or_email: this.get('username') } }).then(function() { @@ -266,7 +267,7 @@ const AdminUser = Discourse.User.extend({ }, impersonate() { - return Discourse.ajax("/admin/impersonate", { + return ajax("/admin/impersonate", { type: 'POST', data: { username_or_email: this.get('username') } }).then(function() { @@ -281,7 +282,7 @@ const AdminUser = Discourse.User.extend({ }, activate() { - return Discourse.ajax('/admin/users/' + this.id + '/activate', { + return ajax('/admin/users/' + this.id + '/activate', { type: 'PUT' }).then(function() { window.location.reload(); @@ -292,7 +293,7 @@ const AdminUser = Discourse.User.extend({ }, deactivate() { - return Discourse.ajax('/admin/users/' + this.id + '/deactivate', { + return ajax('/admin/users/' + this.id + '/deactivate', { type: 'PUT' }).then(function() { window.location.reload(); @@ -304,7 +305,7 @@ const AdminUser = Discourse.User.extend({ unblock() { this.set('blockingUser', true); - return Discourse.ajax('/admin/users/' + this.id + '/unblock', { + return ajax('/admin/users/' + this.id + '/unblock', { type: 'PUT' }).then(function() { window.location.reload(); @@ -320,7 +321,7 @@ const AdminUser = Discourse.User.extend({ const performBlock = function() { user.set('blockingUser', true); - return Discourse.ajax('/admin/users/' + user.id + '/block', { + return ajax('/admin/users/' + user.id + '/block', { type: 'PUT' }).then(function() { window.location.reload(); @@ -345,7 +346,7 @@ const AdminUser = Discourse.User.extend({ }, sendActivationEmail() { - return Discourse.ajax('/users/action/send_activation_email', { + return ajax('/users/action/send_activation_email', { type: 'POST', data: { username: this.get('username') } }).then(function() { @@ -360,7 +361,7 @@ const AdminUser = Discourse.User.extend({ message = I18n.t("admin.user.anonymize_confirm"); const performAnonymize = function() { - return Discourse.ajax("/admin/users/" + user.get('id') + '/anonymize.json', { + return ajax("/admin/users/" + user.get('id') + '/anonymize.json', { type: 'PUT' }).then(function(data) { if (data.success) { @@ -422,7 +423,7 @@ const AdminUser = Discourse.User.extend({ if (opts && opts.deletePosts) { formData["delete_posts"] = true; } - return Discourse.ajax("/admin/users/" + user.get('id') + '.json', { + return ajax("/admin/users/" + user.get('id') + '.json', { type: 'DELETE', data: formData }).then(function(data) { @@ -481,7 +482,7 @@ const AdminUser = Discourse.User.extend({ "label": ' ' + I18n.t("flagging.yes_delete_spammer"), "class": "btn btn-danger", "callback": function() { - return Discourse.ajax("/admin/users/" + user.get('id') + '.json', { + return ajax("/admin/users/" + user.get('id') + '.json', { type: 'DELETE', data: { delete_posts: true, @@ -549,7 +550,7 @@ AdminUser.reopenClass({ }); }); - return Discourse.ajax("/admin/users/approve-bulk", { + return ajax("/admin/users/approve-bulk", { type: 'PUT', data: { users: users.map((u) => u.id) } }).finally(() => bootbox.alert(I18n.t("admin.user.approve_bulk_success"))); @@ -561,7 +562,7 @@ AdminUser.reopenClass({ user.set('selected', false); }); - return Discourse.ajax("/admin/users/reject-bulk", { + return ajax("/admin/users/reject-bulk", { type: 'DELETE', data: { users: users.map((u) => u.id), @@ -571,14 +572,14 @@ AdminUser.reopenClass({ }, find(user_id) { - return Discourse.ajax("/admin/users/" + user_id + ".json").then(result => { + return ajax("/admin/users/" + user_id + ".json").then(result => { result.loadedDetails = true; return AdminUser.create(result); }); }, findAll(query, filter) { - return Discourse.ajax("/admin/users/list/" + query + ".json", { + return ajax("/admin/users/list/" + query + ".json", { data: filter }).then(function(users) { return users.map((u) => AdminUser.create(u)); diff --git a/app/assets/javascripts/admin/models/api-key.js.es6 b/app/assets/javascripts/admin/models/api-key.js.es6 index 7ef7719543..aa05ce8341 100644 --- a/app/assets/javascripts/admin/models/api-key.js.es6 +++ b/app/assets/javascripts/admin/models/api-key.js.es6 @@ -1,3 +1,4 @@ +import { ajax } from 'discourse/lib/ajax'; const ApiKey = Discourse.Model.extend({ /** @@ -8,7 +9,7 @@ const ApiKey = Discourse.Model.extend({ **/ regenerate: function() { var self = this; - return Discourse.ajax('/admin/api/key', {type: 'PUT', data: {id: this.get('id')}}).then(function (result) { + return ajax('/admin/api/key', {type: 'PUT', data: {id: this.get('id')}}).then(function (result) { self.set('key', result.api_key.key); return self; }); @@ -21,7 +22,7 @@ const ApiKey = Discourse.Model.extend({ @returns {Promise} a promise that resolves when the key has been revoked **/ revoke: function() { - return Discourse.ajax('/admin/api/key', {type: 'DELETE', data: {id: this.get('id')}}); + return ajax('/admin/api/key', {type: 'DELETE', data: {id: this.get('id')}}); } }); @@ -51,7 +52,7 @@ ApiKey.reopenClass({ @returns {Promise} a promise that resolves to the array of `ApiKey` instances **/ find: function() { - return Discourse.ajax("/admin/api").then(function(keys) { + return ajax("/admin/api").then(function(keys) { return keys.map(function (key) { return ApiKey.create(key); }); @@ -65,7 +66,7 @@ ApiKey.reopenClass({ @returns {Promise} a promise that resolves to a master `ApiKey` **/ generateMasterKey: function() { - return Discourse.ajax("/admin/api/key", {type: 'POST'}).then(function (result) { + return ajax("/admin/api/key", {type: 'POST'}).then(function (result) { return ApiKey.create(result.api_key); }); } diff --git a/app/assets/javascripts/admin/models/backup.js.es6 b/app/assets/javascripts/admin/models/backup.js.es6 index 8b4991b728..d7baceebac 100644 --- a/app/assets/javascripts/admin/models/backup.js.es6 +++ b/app/assets/javascripts/admin/models/backup.js.es6 @@ -1,11 +1,14 @@ +import { ajax } from 'discourse/lib/ajax'; +import PreloadStore from 'preload-store'; + const Backup = Discourse.Model.extend({ destroy() { - return Discourse.ajax("/admin/backups/" + this.get("filename"), { type: "DELETE" }); + return ajax("/admin/backups/" + this.get("filename"), { type: "DELETE" }); }, restore() { - return Discourse.ajax("/admin/backups/" + this.get("filename") + "/restore", { + return ajax("/admin/backups/" + this.get("filename") + "/restore", { type: "POST", data: { client_id: window.MessageBus.clientId } }); @@ -16,13 +19,13 @@ const Backup = Discourse.Model.extend({ Backup.reopenClass({ find() { - return PreloadStore.getAndRemove("backups", () => Discourse.ajax("/admin/backups.json")) + return PreloadStore.getAndRemove("backups", () => ajax("/admin/backups.json")) .then(backups => backups.map(backup => Backup.create(backup))); }, start(withUploads) { if (withUploads === undefined) { withUploads = true; } - return Discourse.ajax("/admin/backups", { + return ajax("/admin/backups", { type: "POST", data: { with_uploads: withUploads, @@ -34,14 +37,14 @@ Backup.reopenClass({ }, cancel() { - return Discourse.ajax("/admin/backups/cancel.json") + return ajax("/admin/backups/cancel.json") .then(result => { if (!result.success) { bootbox.alert(result.message); } }); }, rollback() { - return Discourse.ajax("/admin/backups/rollback.json") + return ajax("/admin/backups/rollback.json") .then(result => { if (!result.success) { bootbox.alert(result.message); diff --git a/app/assets/javascripts/admin/models/color-scheme.js.es6 b/app/assets/javascripts/admin/models/color-scheme.js.es6 index 512672230d..743c779d6c 100644 --- a/app/assets/javascripts/admin/models/color-scheme.js.es6 +++ b/app/assets/javascripts/admin/models/color-scheme.js.es6 @@ -1,3 +1,4 @@ +import { ajax } from 'discourse/lib/ajax'; import ColorSchemeColor from 'admin/models/color-scheme-color'; const ColorScheme = Discourse.Model.extend(Ember.Copyable, { @@ -65,7 +66,7 @@ const ColorScheme = Discourse.Model.extend(Ember.Copyable, { }); } - return Discourse.ajax("/admin/color_schemes" + (this.id ? '/' + this.id : '') + '.json', { + return ajax("/admin/color_schemes" + (this.id ? '/' + this.id : '') + '.json', { data: JSON.stringify({"color_scheme": data}), type: this.id ? 'PUT' : 'POST', dataType: 'json', @@ -88,7 +89,7 @@ const ColorScheme = Discourse.Model.extend(Ember.Copyable, { destroy: function() { if (this.id) { - return Discourse.ajax("/admin/color_schemes/" + this.id, { type: 'DELETE' }); + return ajax("/admin/color_schemes/" + this.id, { type: 'DELETE' }); } } @@ -106,7 +107,7 @@ var ColorSchemes = Ember.ArrayProxy.extend({ ColorScheme.reopenClass({ findAll: function() { var colorSchemes = ColorSchemes.create({ content: [], loading: true }); - Discourse.ajax('/admin/color_schemes').then(function(all) { + ajax('/admin/color_schemes').then(function(all) { _.each(all, function(colorScheme){ colorSchemes.pushObject(ColorScheme.create({ id: colorScheme.id, diff --git a/app/assets/javascripts/admin/models/email-log.js.es6 b/app/assets/javascripts/admin/models/email-log.js.es6 index 2b19eeff4f..dd6948bd41 100644 --- a/app/assets/javascripts/admin/models/email-log.js.es6 +++ b/app/assets/javascripts/admin/models/email-log.js.es6 @@ -1,3 +1,4 @@ +import { ajax } from 'discourse/lib/ajax'; import AdminUser from 'admin/models/admin-user'; const EmailLog = Discourse.Model.extend({}); @@ -21,7 +22,7 @@ EmailLog.reopenClass({ const status = filter.status || "sent"; filter = _.omit(filter, "status"); - return Discourse.ajax(`/admin/email/${status}.json?offset=${offset}`, { data: filter }) + return ajax(`/admin/email/${status}.json?offset=${offset}`, { data: filter }) .then(logs => _.map(logs, log => EmailLog.create(log))); } }); diff --git a/app/assets/javascripts/admin/models/email-preview.js.es6 b/app/assets/javascripts/admin/models/email-preview.js.es6 index 12826f98ed..f992bf250d 100644 --- a/app/assets/javascripts/admin/models/email-preview.js.es6 +++ b/app/assets/javascripts/admin/models/email-preview.js.es6 @@ -1,3 +1,4 @@ +import { ajax } from 'discourse/lib/ajax'; const EmailPreview = Discourse.Model.extend({}); EmailPreview.reopenClass({ @@ -11,7 +12,7 @@ EmailPreview.reopenClass({ username = Discourse.User.current().username; } - return Discourse.ajax("/admin/email/preview-digest.json", { + return ajax("/admin/email/preview-digest.json", { data: { last_seen_at: lastSeenAt, username: username } }).then(function (result) { return EmailPreview.create(result); diff --git a/app/assets/javascripts/admin/models/email-settings.js.es6 b/app/assets/javascripts/admin/models/email-settings.js.es6 index 1b8f791f26..ed5f000d51 100644 --- a/app/assets/javascripts/admin/models/email-settings.js.es6 +++ b/app/assets/javascripts/admin/models/email-settings.js.es6 @@ -1,8 +1,9 @@ +import { ajax } from 'discourse/lib/ajax'; const EmailSettings = Discourse.Model.extend({}); EmailSettings.reopenClass({ find: function() { - return Discourse.ajax("/admin/email.json").then(function (settings) { + return ajax("/admin/email.json").then(function (settings) { return EmailSettings.create(settings); }); } diff --git a/app/assets/javascripts/admin/models/email-template.js.es6 b/app/assets/javascripts/admin/models/email-template.js.es6 index 7e8e6579ac..81d15c06c9 100644 --- a/app/assets/javascripts/admin/models/email-template.js.es6 +++ b/app/assets/javascripts/admin/models/email-template.js.es6 @@ -1,9 +1,10 @@ +import { ajax } from 'discourse/lib/ajax'; import RestModel from 'discourse/models/rest'; const { getProperties } = Ember; export default RestModel.extend({ revert() { - return Discourse.ajax(`/admin/customize/email_templates/${this.get('id')}`, { + return ajax(`/admin/customize/email_templates/${this.get('id')}`, { method: 'DELETE' }).then(result => getProperties(result.email_template, 'subject', 'body', 'can_revert')); } diff --git a/app/assets/javascripts/admin/models/flagged-post.js.es6 b/app/assets/javascripts/admin/models/flagged-post.js.es6 index 8492c4f8b7..c7a654608f 100644 --- a/app/assets/javascripts/admin/models/flagged-post.js.es6 +++ b/app/assets/javascripts/admin/models/flagged-post.js.es6 @@ -1,3 +1,4 @@ +import { ajax } from 'discourse/lib/ajax'; import AdminUser from 'admin/models/admin-user'; import Topic from 'discourse/models/topic'; import Post from 'discourse/models/post'; @@ -106,22 +107,22 @@ const FlaggedPost = Post.extend({ deletePost: function() { if (this.get('post_number') === 1) { - return Discourse.ajax('/t/' + this.topic_id, { type: 'DELETE', cache: false }); + return ajax('/t/' + this.topic_id, { type: 'DELETE', cache: false }); } else { - return Discourse.ajax('/posts/' + this.id, { type: 'DELETE', cache: false }); + return ajax('/posts/' + this.id, { type: 'DELETE', cache: false }); } }, disagreeFlags: function () { - return Discourse.ajax('/admin/flags/disagree/' + this.id, { type: 'POST', cache: false }); + return ajax('/admin/flags/disagree/' + this.id, { type: 'POST', cache: false }); }, deferFlags: function (deletePost) { - return Discourse.ajax('/admin/flags/defer/' + this.id, { type: 'POST', cache: false, data: { delete_post: deletePost } }); + return ajax('/admin/flags/defer/' + this.id, { type: 'POST', cache: false, data: { delete_post: deletePost } }); }, agreeFlags: function (actionOnPost) { - return Discourse.ajax('/admin/flags/agree/' + this.id, { type: 'POST', cache: false, data: { action_on_post: actionOnPost } }); + return ajax('/admin/flags/agree/' + this.id, { type: 'POST', cache: false, data: { action_on_post: actionOnPost } }); }, postHidden: Em.computed.alias('hidden'), @@ -144,7 +145,7 @@ FlaggedPost.reopenClass({ var result = Em.A(); result.set('loading', true); - return Discourse.ajax('/admin/flags/' + filter + '.json?offset=' + offset).then(function (data) { + return ajax('/admin/flags/' + filter + '.json?offset=' + offset).then(function (data) { // users var userLookup = {}; _.each(data.users, function (user) { diff --git a/app/assets/javascripts/admin/models/incoming-email.js.es6 b/app/assets/javascripts/admin/models/incoming-email.js.es6 index 82534c5c43..d0386b2bc4 100644 --- a/app/assets/javascripts/admin/models/incoming-email.js.es6 +++ b/app/assets/javascripts/admin/models/incoming-email.js.es6 @@ -1,3 +1,4 @@ +import { ajax } from 'discourse/lib/ajax'; import AdminUser from 'admin/models/admin-user'; const IncomingEmail = Discourse.Model.extend({}); @@ -15,7 +16,7 @@ IncomingEmail.reopenClass({ }, find(id) { - return Discourse.ajax(`/admin/email/incoming/${id}.json`); + return ajax(`/admin/email/incoming/${id}.json`); }, findAll(filter, offset) { @@ -25,12 +26,12 @@ IncomingEmail.reopenClass({ const status = filter.status || "received"; filter = _.omit(filter, "status"); - return Discourse.ajax(`/admin/email/${status}.json?offset=${offset}`, { data: filter }) + return ajax(`/admin/email/${status}.json?offset=${offset}`, { data: filter }) .then(incomings => _.map(incomings, incoming => IncomingEmail.create(incoming))); }, loadRawEmail(id) { - return Discourse.ajax(`/admin/email/incoming/${id}/raw.json`); + return ajax(`/admin/email/incoming/${id}/raw.json`); } }); diff --git a/app/assets/javascripts/admin/models/permalink.js.es6 b/app/assets/javascripts/admin/models/permalink.js.es6 index eb867adb31..966e3f10e7 100644 --- a/app/assets/javascripts/admin/models/permalink.js.es6 +++ b/app/assets/javascripts/admin/models/permalink.js.es6 @@ -1,19 +1,20 @@ +import { ajax } from 'discourse/lib/ajax'; const Permalink = Discourse.Model.extend({ save: function() { - return Discourse.ajax("/admin/permalinks.json", { + return ajax("/admin/permalinks.json", { type: 'POST', data: {url: this.get('url'), permalink_type: this.get('permalink_type'), permalink_type_value: this.get('permalink_type_value')} }); }, destroy: function() { - return Discourse.ajax("/admin/permalinks/" + this.get('id') + ".json", {type: 'DELETE'}); + return ajax("/admin/permalinks/" + this.get('id') + ".json", {type: 'DELETE'}); } }); Permalink.reopenClass({ findAll: function(filter) { - return Discourse.ajax("/admin/permalinks.json", { data: { filter: filter } }).then(function(permalinks) { + return ajax("/admin/permalinks.json", { data: { filter: filter } }).then(function(permalinks) { return permalinks.map(p => Permalink.create(p)); }); } diff --git a/app/assets/javascripts/admin/models/report.js.es6 b/app/assets/javascripts/admin/models/report.js.es6 index 1891984b47..3342323b46 100644 --- a/app/assets/javascripts/admin/models/report.js.es6 +++ b/app/assets/javascripts/admin/models/report.js.es6 @@ -1,3 +1,4 @@ +import { ajax } from 'discourse/lib/ajax'; import round from "discourse/lib/round"; import { fmt } from 'discourse/lib/computed'; @@ -132,7 +133,7 @@ const Report = Discourse.Model.extend({ Report.reopenClass({ find(type, startDate, endDate, categoryId, groupId) { - return Discourse.ajax("/admin/reports/" + type, { + return ajax("/admin/reports/" + type, { data: { start_date: startDate, end_date: endDate, diff --git a/app/assets/javascripts/admin/models/screened-email.js.es6 b/app/assets/javascripts/admin/models/screened-email.js.es6 index 71c74d0ad0..a07b9a14b5 100644 --- a/app/assets/javascripts/admin/models/screened-email.js.es6 +++ b/app/assets/javascripts/admin/models/screened-email.js.es6 @@ -1,16 +1,17 @@ +import { ajax } from 'discourse/lib/ajax'; const ScreenedEmail = Discourse.Model.extend({ actionName: function() { return I18n.t("admin.logs.screened_actions." + this.get('action')); }.property('action'), clearBlock: function() { - return Discourse.ajax('/admin/logs/screened_emails/' + this.get('id'), {method: 'DELETE'}); + return ajax('/admin/logs/screened_emails/' + this.get('id'), {method: 'DELETE'}); } }); ScreenedEmail.reopenClass({ findAll: function() { - return Discourse.ajax("/admin/logs/screened_emails.json").then(function(screened_emails) { + return ajax("/admin/logs/screened_emails.json").then(function(screened_emails) { return screened_emails.map(function(b) { return ScreenedEmail.create(b); }); diff --git a/app/assets/javascripts/admin/models/screened-ip-address.js.es6 b/app/assets/javascripts/admin/models/screened-ip-address.js.es6 index 635fddf026..4bde08707b 100644 --- a/app/assets/javascripts/admin/models/screened-ip-address.js.es6 +++ b/app/assets/javascripts/admin/models/screened-ip-address.js.es6 @@ -1,3 +1,4 @@ +import { ajax } from 'discourse/lib/ajax'; import computed from 'ember-addons/ember-computed-decorators'; const ScreenedIpAddress = Discourse.Model.extend({ @@ -14,25 +15,25 @@ const ScreenedIpAddress = Discourse.Model.extend({ }, save() { - return Discourse.ajax("/admin/logs/screened_ip_addresses" + (this.id ? '/' + this.id : '') + ".json", { + return ajax("/admin/logs/screened_ip_addresses" + (this.id ? '/' + this.id : '') + ".json", { type: this.id ? 'PUT' : 'POST', data: {ip_address: this.get('ip_address'), action_name: this.get('action_name')} }); }, destroy() { - return Discourse.ajax("/admin/logs/screened_ip_addresses/" + this.get('id') + ".json", {type: 'DELETE'}); + return ajax("/admin/logs/screened_ip_addresses/" + this.get('id') + ".json", {type: 'DELETE'}); } }); ScreenedIpAddress.reopenClass({ findAll(filter) { - return Discourse.ajax("/admin/logs/screened_ip_addresses.json", { data: { filter: filter } }) + return ajax("/admin/logs/screened_ip_addresses.json", { data: { filter: filter } }) .then(screened_ips => screened_ips.map(b => ScreenedIpAddress.create(b))); }, rollUp() { - return Discourse.ajax("/admin/logs/screened_ip_addresses/roll_up", { type: "POST" }); + return ajax("/admin/logs/screened_ip_addresses/roll_up", { type: "POST" }); } }); diff --git a/app/assets/javascripts/admin/models/screened-url.js.es6 b/app/assets/javascripts/admin/models/screened-url.js.es6 index 9b16c7faec..282bc2fde4 100644 --- a/app/assets/javascripts/admin/models/screened-url.js.es6 +++ b/app/assets/javascripts/admin/models/screened-url.js.es6 @@ -1,3 +1,4 @@ +import { ajax } from 'discourse/lib/ajax'; const ScreenedUrl = Discourse.Model.extend({ actionName: function() { return I18n.t("admin.logs.screened_actions." + this.get('action')); @@ -6,7 +7,7 @@ const ScreenedUrl = Discourse.Model.extend({ ScreenedUrl.reopenClass({ findAll: function() { - return Discourse.ajax("/admin/logs/screened_urls.json").then(function(screened_urls) { + return ajax("/admin/logs/screened_urls.json").then(function(screened_urls) { return screened_urls.map(function(b) { return ScreenedUrl.create(b); }); diff --git a/app/assets/javascripts/admin/models/site-setting.js.es6 b/app/assets/javascripts/admin/models/site-setting.js.es6 index 98b3ab896e..5103762663 100644 --- a/app/assets/javascripts/admin/models/site-setting.js.es6 +++ b/app/assets/javascripts/admin/models/site-setting.js.es6 @@ -1,3 +1,4 @@ +import { ajax } from 'discourse/lib/ajax'; const SiteSetting = Discourse.Model.extend({ overridden: function() { let val = this.get('value'), @@ -28,7 +29,7 @@ const SiteSetting = Discourse.Model.extend({ SiteSetting.reopenClass({ findAll() { - return Discourse.ajax("/admin/site_settings").then(function (settings) { + return ajax("/admin/site_settings").then(function (settings) { // Group the results by category const categories = {}; settings.site_settings.forEach(function(s) { @@ -47,7 +48,7 @@ SiteSetting.reopenClass({ update(key, value) { const data = {}; data[key] = value; - return Discourse.ajax("/admin/site_settings/" + key, { type: 'PUT', data }); + return ajax("/admin/site_settings/" + key, { type: 'PUT', data }); } }); diff --git a/app/assets/javascripts/admin/models/site-text.js.es6 b/app/assets/javascripts/admin/models/site-text.js.es6 index 0d18ad7ea8..8e187dede0 100644 --- a/app/assets/javascripts/admin/models/site-text.js.es6 +++ b/app/assets/javascripts/admin/models/site-text.js.es6 @@ -1,9 +1,10 @@ +import { ajax } from 'discourse/lib/ajax'; import RestModel from 'discourse/models/rest'; const { getProperties } = Ember; export default RestModel.extend({ revert() { - return Discourse.ajax(`/admin/customize/site_texts/${this.get('id')}`, { + return ajax(`/admin/customize/site_texts/${this.get('id')}`, { method: 'DELETE' }).then(result => getProperties(result.site_text, 'value', 'can_revert')); } diff --git a/app/assets/javascripts/admin/models/staff-action-log.js.es6 b/app/assets/javascripts/admin/models/staff-action-log.js.es6 index 4f9e5cc9ae..24ec4fbcc0 100644 --- a/app/assets/javascripts/admin/models/staff-action-log.js.es6 +++ b/app/assets/javascripts/admin/models/staff-action-log.js.es6 @@ -1,4 +1,6 @@ +import { ajax } from 'discourse/lib/ajax'; import AdminUser from 'admin/models/admin-user'; +import { escapeExpression } from 'discourse/lib/utilities'; const StaffActionLog = Discourse.Model.extend({ showFullDetails: false, @@ -19,14 +21,14 @@ const StaffActionLog = Discourse.Model.extend({ formatted += this.format('admin.logs.staff_actions.previous_value', 'previous_value'); } if (!this.get('useModalForDetails')) { - if (this.get('details')) formatted += Discourse.Utilities.escapeExpression(this.get('details')) + '
'; + if (this.get('details')) formatted += escapeExpression(this.get('details')) + '
'; } return formatted; }.property('ip_address', 'email', 'topic_id', 'post_id', 'category_id'), format: function(label, propertyName) { if (this.get(propertyName)) { - return ('' + I18n.t(label) + ': ' + Discourse.Utilities.escapeExpression(this.get(propertyName)) + '
'); + return ('' + I18n.t(label) + ': ' + escapeExpression(this.get(propertyName)) + '
'); } else { return ''; } @@ -55,7 +57,7 @@ StaffActionLog.reopenClass({ }, findAll: function(filters) { - return Discourse.ajax("/admin/logs/staff_action_logs.json", { data: filters }).then(function(staff_actions) { + return ajax("/admin/logs/staff_action_logs.json", { data: filters }).then(function(staff_actions) { return staff_actions.map(function(s) { return StaffActionLog.create(s); }); diff --git a/app/assets/javascripts/admin/models/version-check.js.es6 b/app/assets/javascripts/admin/models/version-check.js.es6 index 4f9b2c4c66..fc8c1bf5e2 100644 --- a/app/assets/javascripts/admin/models/version-check.js.es6 +++ b/app/assets/javascripts/admin/models/version-check.js.es6 @@ -1,3 +1,4 @@ +import { ajax } from 'discourse/lib/ajax'; const VersionCheck = Discourse.Model.extend({ noCheckPerformed: function() { @@ -33,7 +34,7 @@ const VersionCheck = Discourse.Model.extend({ VersionCheck.reopenClass({ find: function() { - return Discourse.ajax('/admin/version_check').then(function(json) { + return ajax('/admin/version_check').then(function(json) { return VersionCheck.create(json); }); } diff --git a/app/assets/javascripts/admin/routes/admin-backups-logs.js.es6 b/app/assets/javascripts/admin/routes/admin-backups-logs.js.es6 index 8a6d32fddf..cd01295f5d 100644 --- a/app/assets/javascripts/admin/routes/admin-backups-logs.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-backups-logs.js.es6 @@ -1,3 +1,5 @@ +import PreloadStore from 'preload-store'; + export default Ember.Route.extend({ // since the logs are pushed via the message bus diff --git a/app/assets/javascripts/admin/routes/admin-backups.js.es6 b/app/assets/javascripts/admin/routes/admin-backups.js.es6 index f97d86f931..d590019431 100644 --- a/app/assets/javascripts/admin/routes/admin-backups.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-backups.js.es6 @@ -1,6 +1,8 @@ +import { ajax } from 'discourse/lib/ajax'; import showModal from 'discourse/lib/show-modal'; import BackupStatus from 'admin/models/backup-status'; import Backup from 'admin/models/backup'; +import PreloadStore from 'preload-store'; const LOG_CHANNEL = "/admin/backups/logs"; @@ -31,7 +33,7 @@ export default Discourse.Route.extend({ model() { return PreloadStore.getAndRemove("operations_status", function() { - return Discourse.ajax("/admin/backups/status.json"); + return ajax("/admin/backups/status.json"); }).then(status => { return BackupStatus.create({ isOperationRunning: status.is_operation_running, diff --git a/app/assets/javascripts/admin/routes/admin-badges-show.js.es6 b/app/assets/javascripts/admin/routes/admin-badges-show.js.es6 index c283ff737b..03d091d228 100644 --- a/app/assets/javascripts/admin/routes/admin-badges-show.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-badges-show.js.es6 @@ -1,3 +1,4 @@ +import { ajax } from 'discourse/lib/ajax'; import Badge from 'discourse/models/badge'; import showModal from 'discourse/lib/show-modal'; @@ -31,7 +32,7 @@ export default Ember.Route.extend({ preview(badge, explain) { badge.set('preview_loading', true); - Discourse.ajax('/admin/badges/preview.json', { + ajax('/admin/badges/preview.json', { method: 'post', data: { sql: badge.get('query'), diff --git a/app/assets/javascripts/admin/routes/admin-badges.js.es6 b/app/assets/javascripts/admin/routes/admin-badges.js.es6 index 5efa86491f..68da5edb6b 100644 --- a/app/assets/javascripts/admin/routes/admin-badges.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-badges.js.es6 @@ -1,3 +1,4 @@ +import { ajax } from 'discourse/lib/ajax'; import Badge from 'discourse/models/badge'; import BadgeGrouping from 'discourse/models/badge-grouping'; @@ -6,7 +7,7 @@ export default Discourse.Route.extend({ model: function() { var self = this; - return Discourse.ajax('/admin/badges.json').then(function(json) { + return ajax('/admin/badges.json').then(function(json) { self._json = json; return Badge.createFromJson(json); }); diff --git a/app/assets/javascripts/admin/routes/admin-emojis.js.es6 b/app/assets/javascripts/admin/routes/admin-emojis.js.es6 index a33ade0111..520f7a0d50 100644 --- a/app/assets/javascripts/admin/routes/admin-emojis.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-emojis.js.es6 @@ -1,6 +1,7 @@ +import { ajax } from 'discourse/lib/ajax'; export default Discourse.Route.extend({ model: function() { - return Discourse.ajax("/admin/customize/emojis.json").then(function(emojis) { + return 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 index 1555c4200a..298bffaa43 100644 --- a/app/assets/javascripts/admin/routes/admin-group.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-group.js.es6 @@ -2,26 +2,23 @@ import Group from 'discourse/models/group'; export default Discourse.Route.extend({ - model: function(params) { - var groups = this.modelFor('adminGroupsType'); + model(params) { if (params.name === 'new') { - return Group.create({ - automatic: false, - visible: true - }); + return Group.create({ automatic: false, visible: true }); } - var group = groups.findProperty('name', params.name); + const group = this.modelFor('adminGroupsType') + .findProperty('name', params.name); if (!group) { return this.transitionTo('adminGroups.index'); } return group; }, - setupController: function(controller, model) { + setupController(controller, model) { controller.set("model", model); controller.set("model.usernames", null); - controller.set("savingStatus", ''); + controller.set("savingStatus", ""); model.findMembers(); } diff --git a/app/assets/javascripts/admin/templates/group.hbs b/app/assets/javascripts/admin/templates/group.hbs index 49db722d6f..2c2a1929a3 100644 --- a/app/assets/javascripts/admin/templates/group.hbs +++ b/app/assets/javascripts/admin/templates/group.hbs @@ -94,10 +94,10 @@ {{#if siteSettings.email_in}} -
+ {{#plugin-outlet "group-email-in"}} {{text-field name="incoming_email" value=model.incoming_email placeholderKey="admin.groups.incoming_email_placeholder"}} -
+ {{/plugin-outlet}} {{/if}} {{/unless}} 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 cb838a48fc..1929abf5a2 100644 --- a/app/assets/javascripts/admin/views/admin-backups-logs.js.es6 +++ b/app/assets/javascripts/admin/views/admin-backups-logs.js.es6 @@ -1,5 +1,6 @@ import debounce from 'discourse/lib/debounce'; import { renderSpinner } from 'discourse/helpers/loading-spinner'; +import { escapeExpression } from 'discourse/lib/utilities'; export default Ember.View.extend({ classNames: ["admin-backups-logs"], @@ -19,7 +20,7 @@ export default Ember.View.extend({ let formattedLogs = this.get("formattedLogs"); for (let i = this.get("index"), length = logs.length; i < length; i++) { const date = logs[i].get("timestamp"), - message = Discourse.Utilities.escapeExpression(logs[i].get("message")); + message = escapeExpression(logs[i].get("message")); formattedLogs += "[" + date + "] " + message + "\n"; } // update the formatted logs & cache index diff --git a/app/assets/javascripts/defer/html-sanitizer-bundle.js b/app/assets/javascripts/defer/html-sanitizer-bundle.js deleted file mode 100644 index 7529153a85..0000000000 --- a/app/assets/javascripts/defer/html-sanitizer-bundle.js +++ /dev/null @@ -1,2233 +0,0 @@ -// Copyright (C) 2010 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/** - * @fileoverview - * Implements RFC 3986 for parsing/formatting URIs. - * - * @author mikesamuel@gmail.com - * \@provides URI - * \@overrides window - */ - -var URI = (function () { - -/** - * creates a uri from the string form. The parser is relaxed, so special - * characters that aren't escaped but don't cause ambiguities will not cause - * parse failures. - * - * @return {URI|null} - */ -function parse(uriStr) { - var m = ('' + uriStr).match(URI_RE_); - if (!m) { return null; } - return new URI( - nullIfAbsent(m[1]), - nullIfAbsent(m[2]), - nullIfAbsent(m[3]), - nullIfAbsent(m[4]), - nullIfAbsent(m[5]), - nullIfAbsent(m[6]), - nullIfAbsent(m[7])); -} - - -/** - * creates a uri from the given parts. - * - * @param scheme {string} an unencoded scheme such as "http" or null - * @param credentials {string} unencoded user credentials or null - * @param domain {string} an unencoded domain name or null - * @param port {number} a port number in [1, 32768]. - * -1 indicates no port, as does null. - * @param path {string} an unencoded path - * @param query {Array.|string|null} a list of unencoded cgi - * parameters where even values are keys and odds the corresponding values - * or an unencoded query. - * @param fragment {string} an unencoded fragment without the "#" or null. - * @return {URI} - */ -function create(scheme, credentials, domain, port, path, query, fragment) { - var uri = new URI( - encodeIfExists2(scheme, URI_DISALLOWED_IN_SCHEME_OR_CREDENTIALS_), - encodeIfExists2( - credentials, URI_DISALLOWED_IN_SCHEME_OR_CREDENTIALS_), - encodeIfExists(domain), - port > 0 ? port.toString() : null, - encodeIfExists2(path, URI_DISALLOWED_IN_PATH_), - null, - encodeIfExists(fragment)); - if (query) { - if ('string' === typeof query) { - uri.setRawQuery(query.replace(/[^?&=0-9A-Za-z_\-~.%]/g, encodeOne)); - } else { - uri.setAllParameters(query); - } - } - return uri; -} -function encodeIfExists(unescapedPart) { - if ('string' == typeof unescapedPart) { - return encodeURIComponent(unescapedPart); - } - return null; -}; -/** - * if unescapedPart is non null, then escapes any characters in it that aren't - * valid characters in a url and also escapes any special characters that - * appear in extra. - * - * @param unescapedPart {string} - * @param extra {RegExp} a character set of characters in [\01-\177]. - * @return {string|null} null iff unescapedPart == null. - */ -function encodeIfExists2(unescapedPart, extra) { - if ('string' == typeof unescapedPart) { - return encodeURI(unescapedPart).replace(extra, encodeOne); - } - return null; -}; -/** converts a character in [\01-\177] to its url encoded equivalent. */ -function encodeOne(ch) { - var n = ch.charCodeAt(0); - return '%' + '0123456789ABCDEF'.charAt((n >> 4) & 0xf) + - '0123456789ABCDEF'.charAt(n & 0xf); -} - -/** - * {@updoc - * $ normPath('foo/./bar') - * # 'foo/bar' - * $ normPath('./foo') - * # 'foo' - * $ normPath('foo/.') - * # 'foo' - * $ normPath('foo//bar') - * # 'foo/bar' - * } - */ -function normPath(path) { - return path.replace(/(^|\/)\.(?:\/|$)/g, '$1').replace(/\/{2,}/g, '/'); -} - -var PARENT_DIRECTORY_HANDLER = new RegExp( - '' - // A path break - + '(/|^)' - // followed by a non .. path element - // (cannot be . because normPath is used prior to this RegExp) - + '(?:[^./][^/]*|\\.{2,}(?:[^./][^/]*)|\\.{3,}[^/]*)' - // followed by .. followed by a path break. - + '/\\.\\.(?:/|$)'); - -var PARENT_DIRECTORY_HANDLER_RE = new RegExp(PARENT_DIRECTORY_HANDLER); - -var EXTRA_PARENT_PATHS_RE = /^(?:\.\.\/)*(?:\.\.$)?/; - -/** - * Normalizes its input path and collapses all . and .. sequences except for - * .. sequences that would take it above the root of the current parent - * directory. - * {@updoc - * $ collapse_dots('foo/../bar') - * # 'bar' - * $ collapse_dots('foo/./bar') - * # 'foo/bar' - * $ collapse_dots('foo/../bar/./../../baz') - * # 'baz' - * $ collapse_dots('../foo') - * # '../foo' - * $ collapse_dots('../foo').replace(EXTRA_PARENT_PATHS_RE, '') - * # 'foo' - * } - */ -function collapse_dots(path) { - if (path === null) { return null; } - var p = normPath(path); - // Only /../ left to flatten - var r = PARENT_DIRECTORY_HANDLER_RE; - // We replace with $1 which matches a / before the .. because this - // guarantees that: - // (1) we have at most 1 / between the adjacent place, - // (2) always have a slash if there is a preceding path section, and - // (3) we never turn a relative path into an absolute path. - for (var q; (q = p.replace(r, '$1')) != p; p = q) {}; - return p; -} - -/** - * resolves a relative url string to a base uri. - * @return {URI} - */ -function resolve(baseUri, relativeUri) { - // there are several kinds of relative urls: - // 1. //foo - replaces everything from the domain on. foo is a domain name - // 2. foo - replaces the last part of the path, the whole query and fragment - // 3. /foo - replaces the the path, the query and fragment - // 4. ?foo - replace the query and fragment - // 5. #foo - replace the fragment only - - var absoluteUri = baseUri.clone(); - // we satisfy these conditions by looking for the first part of relativeUri - // that is not blank and applying defaults to the rest - - var overridden = relativeUri.hasScheme(); - - if (overridden) { - absoluteUri.setRawScheme(relativeUri.getRawScheme()); - } else { - overridden = relativeUri.hasCredentials(); - } - - if (overridden) { - absoluteUri.setRawCredentials(relativeUri.getRawCredentials()); - } else { - overridden = relativeUri.hasDomain(); - } - - if (overridden) { - absoluteUri.setRawDomain(relativeUri.getRawDomain()); - } else { - overridden = relativeUri.hasPort(); - } - - var rawPath = relativeUri.getRawPath(); - var simplifiedPath = collapse_dots(rawPath); - if (overridden) { - absoluteUri.setPort(relativeUri.getPort()); - simplifiedPath = simplifiedPath - && simplifiedPath.replace(EXTRA_PARENT_PATHS_RE, ''); - } else { - overridden = !!rawPath; - if (overridden) { - // resolve path properly - if (simplifiedPath.charCodeAt(0) !== 0x2f /* / */) { // path is relative - var absRawPath = collapse_dots(absoluteUri.getRawPath() || '') - .replace(EXTRA_PARENT_PATHS_RE, ''); - var slash = absRawPath.lastIndexOf('/') + 1; - simplifiedPath = collapse_dots( - (slash ? absRawPath.substring(0, slash) : '') - + collapse_dots(rawPath)) - .replace(EXTRA_PARENT_PATHS_RE, ''); - } - } else { - simplifiedPath = simplifiedPath - && simplifiedPath.replace(EXTRA_PARENT_PATHS_RE, ''); - if (simplifiedPath !== rawPath) { - absoluteUri.setRawPath(simplifiedPath); - } - } - } - - if (overridden) { - absoluteUri.setRawPath(simplifiedPath); - } else { - overridden = relativeUri.hasQuery(); - } - - if (overridden) { - absoluteUri.setRawQuery(relativeUri.getRawQuery()); - } else { - overridden = relativeUri.hasFragment(); - } - - if (overridden) { - absoluteUri.setRawFragment(relativeUri.getRawFragment()); - } - - return absoluteUri; -} - -/** - * a mutable URI. - * - * This class contains setters and getters for the parts of the URI. - * The getXYZ/setXYZ methods return the decoded part -- so - * uri.parse('/foo%20bar').getPath() will return the decoded path, - * /foo bar. - * - *

The raw versions of fields are available too. - * uri.parse('/foo%20bar').getRawPath() will return the raw path, - * /foo%20bar. Use the raw setters with care, since - * URI::toString is not guaranteed to return a valid url if a - * raw setter was used. - * - *

All setters return this and so may be chained, a la - * uri.parse('/foo').setFragment('part').toString(). - * - *

You should not use this constructor directly -- please prefer the factory - * functions {@link uri.parse}, {@link uri.create}, {@link uri.resolve} - * instead.

- * - *

The parameters are all raw (assumed to be properly escaped) parts, and - * any (but not all) may be null. Undefined is not allowed.

- * - * @constructor - */ -function URI( - rawScheme, - rawCredentials, rawDomain, port, - rawPath, rawQuery, rawFragment) { - this.scheme_ = rawScheme; - this.credentials_ = rawCredentials; - this.domain_ = rawDomain; - this.port_ = port; - this.path_ = rawPath; - this.query_ = rawQuery; - this.fragment_ = rawFragment; - /** - * @type {Array|null} - */ - this.paramCache_ = null; -} - -/** returns the string form of the url. */ -URI.prototype.toString = function () { - var out = []; - if (null !== this.scheme_) { out.push(this.scheme_, ':'); } - if (null !== this.domain_) { - out.push('//'); - if (null !== this.credentials_) { out.push(this.credentials_, '@'); } - out.push(this.domain_); - if (null !== this.port_) { out.push(':', this.port_.toString()); } - } - if (null !== this.path_) { out.push(this.path_); } - if (null !== this.query_) { out.push('?', this.query_); } - if (null !== this.fragment_) { out.push('#', this.fragment_); } - return out.join(''); -}; - -URI.prototype.clone = function () { - return new URI(this.scheme_, this.credentials_, this.domain_, this.port_, - this.path_, this.query_, this.fragment_); -}; - -URI.prototype.getScheme = function () { - // HTML5 spec does not require the scheme to be lowercased but - // all common browsers except Safari lowercase the scheme. - return this.scheme_ && decodeURIComponent(this.scheme_).toLowerCase(); -}; -URI.prototype.getRawScheme = function () { - return this.scheme_; -}; -URI.prototype.setScheme = function (newScheme) { - this.scheme_ = encodeIfExists2( - newScheme, URI_DISALLOWED_IN_SCHEME_OR_CREDENTIALS_); - return this; -}; -URI.prototype.setRawScheme = function (newScheme) { - this.scheme_ = newScheme ? newScheme : null; - return this; -}; -URI.prototype.hasScheme = function () { - return null !== this.scheme_; -}; - - -URI.prototype.getCredentials = function () { - return this.credentials_ && decodeURIComponent(this.credentials_); -}; -URI.prototype.getRawCredentials = function () { - return this.credentials_; -}; -URI.prototype.setCredentials = function (newCredentials) { - this.credentials_ = encodeIfExists2( - newCredentials, URI_DISALLOWED_IN_SCHEME_OR_CREDENTIALS_); - - return this; -}; -URI.prototype.setRawCredentials = function (newCredentials) { - this.credentials_ = newCredentials ? newCredentials : null; - return this; -}; -URI.prototype.hasCredentials = function () { - return null !== this.credentials_; -}; - - -URI.prototype.getDomain = function () { - return this.domain_ && decodeURIComponent(this.domain_); -}; -URI.prototype.getRawDomain = function () { - return this.domain_; -}; -URI.prototype.setDomain = function (newDomain) { - return this.setRawDomain(newDomain && encodeURIComponent(newDomain)); -}; -URI.prototype.setRawDomain = function (newDomain) { - this.domain_ = newDomain ? newDomain : null; - // Maintain the invariant that paths must start with a slash when the URI - // is not path-relative. - return this.setRawPath(this.path_); -}; -URI.prototype.hasDomain = function () { - return null !== this.domain_; -}; - - -URI.prototype.getPort = function () { - return this.port_ && decodeURIComponent(this.port_); -}; -URI.prototype.setPort = function (newPort) { - if (newPort) { - newPort = Number(newPort); - if (newPort !== (newPort & 0xffff)) { - throw new Error('Bad port number ' + newPort); - } - this.port_ = '' + newPort; - } else { - this.port_ = null; - } - return this; -}; -URI.prototype.hasPort = function () { - return null !== this.port_; -}; - - -URI.prototype.getPath = function () { - return this.path_ && decodeURIComponent(this.path_); -}; -URI.prototype.getRawPath = function () { - return this.path_; -}; -URI.prototype.setPath = function (newPath) { - return this.setRawPath(encodeIfExists2(newPath, URI_DISALLOWED_IN_PATH_)); -}; -URI.prototype.setRawPath = function (newPath) { - if (newPath) { - newPath = String(newPath); - this.path_ = - // Paths must start with '/' unless this is a path-relative URL. - (!this.domain_ || /^\//.test(newPath)) ? newPath : '/' + newPath; - } else { - this.path_ = null; - } - return this; -}; -URI.prototype.hasPath = function () { - return null !== this.path_; -}; - - -URI.prototype.getQuery = function () { - // From http://www.w3.org/Addressing/URL/4_URI_Recommentations.html - // Within the query string, the plus sign is reserved as shorthand notation - // for a space. - return this.query_ && decodeURIComponent(this.query_).replace(/\+/g, ' '); -}; -URI.prototype.getRawQuery = function () { - return this.query_; -}; -URI.prototype.setQuery = function (newQuery) { - this.paramCache_ = null; - this.query_ = encodeIfExists(newQuery); - return this; -}; -URI.prototype.setRawQuery = function (newQuery) { - this.paramCache_ = null; - this.query_ = newQuery ? newQuery : null; - return this; -}; -URI.prototype.hasQuery = function () { - return null !== this.query_; -}; - -/** - * sets the query given a list of strings of the form - * [ key0, value0, key1, value1, ... ]. - * - *

uri.setAllParameters(['a', 'b', 'c', 'd']).getQuery() - * will yield 'a=b&c=d'. - */ -URI.prototype.setAllParameters = function (params) { - if (typeof params === 'object') { - if (!(params instanceof Array) - && (params instanceof Object - || Object.prototype.toString.call(params) !== '[object Array]')) { - var newParams = []; - var i = -1; - for (var k in params) { - var v = params[k]; - if ('string' === typeof v) { - newParams[++i] = k; - newParams[++i] = v; - } - } - params = newParams; - } - } - this.paramCache_ = null; - var queryBuf = []; - var separator = ''; - for (var j = 0; j < params.length;) { - var k = params[j++]; - var v = params[j++]; - queryBuf.push(separator, encodeURIComponent(k.toString())); - separator = '&'; - if (v) { - queryBuf.push('=', encodeURIComponent(v.toString())); - } - } - this.query_ = queryBuf.join(''); - return this; -}; -URI.prototype.checkParameterCache_ = function () { - if (!this.paramCache_) { - var q = this.query_; - if (!q) { - this.paramCache_ = []; - } else { - var cgiParams = q.split(/[&\?]/); - var out = []; - var k = -1; - for (var i = 0; i < cgiParams.length; ++i) { - var m = cgiParams[i].match(/^([^=]*)(?:=(.*))?$/); - // From http://www.w3.org/Addressing/URL/4_URI_Recommentations.html - // Within the query string, the plus sign is reserved as shorthand - // notation for a space. - out[++k] = decodeURIComponent(m[1]).replace(/\+/g, ' '); - out[++k] = decodeURIComponent(m[2] || '').replace(/\+/g, ' '); - } - this.paramCache_ = out; - } - } -}; -/** - * sets the values of the named cgi parameters. - * - *

So, uri.parse('foo?a=b&c=d&e=f').setParameterValues('c', ['new']) - * yields foo?a=b&c=new&e=f.

- * - * @param key {string} - * @param values {Array.} the new values. If values is a single string - * then it will be treated as the sole value. - */ -URI.prototype.setParameterValues = function (key, values) { - // be nice and avoid subtle bugs where [] operator on string performs charAt - // on some browsers and crashes on IE - if (typeof values === 'string') { - values = [ values ]; - } - - this.checkParameterCache_(); - var newValueIndex = 0; - var pc = this.paramCache_; - var params = []; - for (var i = 0, k = 0; i < pc.length; i += 2) { - if (key === pc[i]) { - if (newValueIndex < values.length) { - params.push(key, values[newValueIndex++]); - } - } else { - params.push(pc[i], pc[i + 1]); - } - } - while (newValueIndex < values.length) { - params.push(key, values[newValueIndex++]); - } - this.setAllParameters(params); - return this; -}; -URI.prototype.removeParameter = function (key) { - return this.setParameterValues(key, []); -}; -/** - * returns the parameters specified in the query part of the uri as a list of - * keys and values like [ key0, value0, key1, value1, ... ]. - * - * @return {Array.} - */ -URI.prototype.getAllParameters = function () { - this.checkParameterCache_(); - return this.paramCache_.slice(0, this.paramCache_.length); -}; -/** - * returns the values for a given cgi parameter as a list of decoded - * query parameter values. - * @return {Array.} - */ -URI.prototype.getParameterValues = function (paramNameUnescaped) { - this.checkParameterCache_(); - var values = []; - for (var i = 0; i < this.paramCache_.length; i += 2) { - if (paramNameUnescaped === this.paramCache_[i]) { - values.push(this.paramCache_[i + 1]); - } - } - return values; -}; -/** - * returns a map of cgi parameter names to (non-empty) lists of values. - * @return {Object.>} - */ -URI.prototype.getParameterMap = function (paramNameUnescaped) { - this.checkParameterCache_(); - var paramMap = {}; - for (var i = 0; i < this.paramCache_.length; i += 2) { - var key = this.paramCache_[i++], - value = this.paramCache_[i++]; - if (!(key in paramMap)) { - paramMap[key] = [value]; - } else { - paramMap[key].push(value); - } - } - return paramMap; -}; -/** - * returns the first value for a given cgi parameter or null if the given - * parameter name does not appear in the query string. - * If the given parameter name does appear, but has no '=' following - * it, then the empty string will be returned. - * @return {string|null} - */ -URI.prototype.getParameterValue = function (paramNameUnescaped) { - this.checkParameterCache_(); - for (var i = 0; i < this.paramCache_.length; i += 2) { - if (paramNameUnescaped === this.paramCache_[i]) { - return this.paramCache_[i + 1]; - } - } - return null; -}; - -URI.prototype.getFragment = function () { - return this.fragment_ && decodeURIComponent(this.fragment_); -}; -URI.prototype.getRawFragment = function () { - return this.fragment_; -}; -URI.prototype.setFragment = function (newFragment) { - this.fragment_ = newFragment ? encodeURIComponent(newFragment) : null; - return this; -}; -URI.prototype.setRawFragment = function (newFragment) { - this.fragment_ = newFragment ? newFragment : null; - return this; -}; -URI.prototype.hasFragment = function () { - return null !== this.fragment_; -}; - -function nullIfAbsent(matchPart) { - return ('string' == typeof matchPart) && (matchPart.length > 0) - ? matchPart - : null; -} - - - - -/** - * a regular expression for breaking a URI into its component parts. - * - *

http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#RFC2234 says - * As the "first-match-wins" algorithm is identical to the "greedy" - * disambiguation method used by POSIX regular expressions, it is natural and - * commonplace to use a regular expression for parsing the potential five - * components of a URI reference. - * - *

The following line is the regular expression for breaking-down a - * well-formed URI reference into its components. - * - *

- * ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
- *  12            3  4          5       6  7        8 9
- * 
- * - *

The numbers in the second line above are only to assist readability; they - * indicate the reference points for each subexpression (i.e., each paired - * parenthesis). We refer to the value matched for subexpression as $. - * For example, matching the above expression to - *

- *     http://www.ics.uci.edu/pub/ietf/uri/#Related
- * 
- * results in the following subexpression matches: - *
- *    $1 = http:
- *    $2 = http
- *    $3 = //www.ics.uci.edu
- *    $4 = www.ics.uci.edu
- *    $5 = /pub/ietf/uri/
- *    $6 = 
- *    $7 = 
- *    $8 = #Related
- *    $9 = Related
- * 
- * where indicates that the component is not present, as is the - * case for the query component in the above example. Therefore, we can - * determine the value of the five components as - *
- *    scheme    = $2
- *    authority = $4
- *    path      = $5
- *    query     = $7
- *    fragment  = $9
- * 
- * - *

msamuel: I have modified the regular expression slightly to expose the - * credentials, domain, and port separately from the authority. - * The modified version yields - *

- *    $1 = http              scheme
- *    $2 =        credentials -\
- *    $3 = www.ics.uci.edu   domain       | authority
- *    $4 =        port        -/
- *    $5 = /pub/ietf/uri/    path
- *    $6 =        query without ?
- *    $7 = Related           fragment without #
- * 
- */ -var URI_RE_ = new RegExp( - "^" + - "(?:" + - "([^:/?#]+)" + // scheme - ":)?" + - "(?://" + - "(?:([^/?#]*)@)?" + // credentials - "([^/?#:@]*)" + // domain - "(?::([0-9]+))?" + // port - ")?" + - "([^?#]+)?" + // path - "(?:\\?([^#]*))?" + // query - "(?:#(.*))?" + // fragment - "$" - ); - -var URI_DISALLOWED_IN_SCHEME_OR_CREDENTIALS_ = /[#\/\?@]/g; -var URI_DISALLOWED_IN_PATH_ = /[\#\?]/g; - -URI.parse = parse; -URI.create = create; -URI.resolve = resolve; -URI.collapse_dots = collapse_dots; // Visible for testing. - -// lightweight string-based api for loadModuleMaker -URI.utils = { - mimeTypeOf: function (uri) { - var uriObj = parse(uri); - if (/\.html$/.test(uriObj.getPath())) { - return 'text/html'; - } else { - return 'application/javascript'; - } - }, - resolve: function (base, uri) { - if (base) { - return resolve(parse(base), parse(uri)).toString(); - } else { - return '' + uri; - } - } -}; - - -return URI; -})(); - -// Exports for closure compiler. -if (typeof window !== 'undefined') { - window['URI'] = URI; -} -; -// Copyright Google Inc. -// Licensed under the Apache Licence Version 2.0 -// Autogenerated at Mon Oct 21 13:30:08 EDT 2013 -// @overrides window -// @provides html4 -var html4 = {}; -html4.atype = { - 'NONE': 0, - 'URI': 1, - 'URI_FRAGMENT': 11, - 'SCRIPT': 2, - 'STYLE': 3, - 'HTML': 12, - 'ID': 4, - 'IDREF': 5, - 'IDREFS': 6, - 'GLOBAL_NAME': 7, - 'LOCAL_NAME': 8, - 'CLASSES': 9, - 'FRAME_TARGET': 10, - 'MEDIA_QUERY': 13 -}; -html4[ 'atype' ] = html4.atype; -html4.ATTRIBS = { - '*::class': 9, - '*::dir': 0, - '*::draggable': 0, - '*::hidden': 0, - '*::id': 4, - '*::inert': 0, - '*::itemprop': 0, - '*::itemref': 6, - '*::itemscope': 0, - '*::lang': 0, - '*::onblur': 2, - '*::onchange': 2, - '*::onclick': 2, - '*::ondblclick': 2, - '*::onerror': 2, - '*::onfocus': 2, - '*::onkeydown': 2, - '*::onkeypress': 2, - '*::onkeyup': 2, - '*::onload': 2, - '*::onmousedown': 2, - '*::onmousemove': 2, - '*::onmouseout': 2, - '*::onmouseover': 2, - '*::onmouseup': 2, - '*::onreset': 2, - '*::onscroll': 2, - '*::onselect': 2, - '*::onsubmit': 2, - '*::onunload': 2, - '*::spellcheck': 0, - '*::style': 3, - '*::title': 0, - '*::translate': 0, - 'a::accesskey': 0, - 'a::coords': 0, - 'a::href': 1, - 'a::hreflang': 0, - 'a::name': 7, - 'a::onblur': 2, - 'a::onfocus': 2, - 'a::shape': 0, - 'a::tabindex': 0, - 'a::target': 10, - 'a::type': 0, - 'bdo::dir': 0, - 'blockquote::cite': 1, - 'br::clear': 0, - 'caption::align': 0, - 'col::align': 0, - 'col::char': 0, - 'col::charoff': 0, - 'col::span': 0, - 'col::valign': 0, - 'col::width': 0, - 'colgroup::align': 0, - 'colgroup::char': 0, - 'colgroup::charoff': 0, - 'colgroup::span': 0, - 'colgroup::valign': 0, - 'colgroup::width': 0, - 'data::value': 0, - 'del::cite': 1, - 'del::datetime': 0, - 'details::open': 0, - 'dir::compact': 0, - 'div::align': 0, - 'dl::compact': 0, - 'h1::align': 0, - 'h2::align': 0, - 'h3::align': 0, - 'h4::align': 0, - 'h5::align': 0, - 'h6::align': 0, - 'hr::align': 0, - 'hr::noshade': 0, - 'hr::size': 0, - 'hr::width': 0, - 'iframe::align': 0, - 'iframe::frameborder': 0, - 'iframe::height': 0, - 'iframe::marginheight': 0, - 'iframe::marginwidth': 0, - 'iframe::width': 0, - 'iframe::src': 1, - 'img::alt': 0, - 'img::height': 0, - 'img::name': 7, - 'img::src': 1, - 'img::width': 0, - 'ins::cite': 1, - 'ins::datetime': 0, - 'label::accesskey': 0, - 'label::for': 5, - 'label::onblur': 2, - 'label::onfocus': 2, - 'legend::accesskey': 0, - 'legend::align': 0, - 'li::type': 0, - 'li::value': 0, - 'ol::compact': 0, - 'ol::reversed': 0, - 'ol::start': 0, - 'ol::type': 0, - 'p::align': 0, - 'pre::width': 0, - 'q::cite': 1, - 'source::type': 0, - 'ul::compact': 0, - 'ul::type': 0, -}; -html4[ 'ATTRIBS' ] = html4.ATTRIBS; -html4.eflags = { - 'OPTIONAL_ENDTAG': 1, - 'EMPTY': 2, - 'CDATA': 4, - 'RCDATA': 8, - 'UNSAFE': 16, - 'FOLDABLE': 32, - 'SCRIPT': 64, - 'STYLE': 128, - 'VIRTUALIZED': 256 -}; -html4[ 'eflags' ] = html4.eflags; -html4.ELEMENTS = { - 'a': 0, - 'abbr': 0, - 'acronym': 0, - 'address': 0, - 'article': 0, - 'aside': 0, - 'b': 0, - 'base': 274, - 'bdi': 0, - 'bdo': 0, - 'big': 0, - 'blockquote': 0, - 'body': 305, - 'br': 2, - 'caption': 0, - 'cite': 0, - 'code': 0, - 'col': 2, - 'colgroup': 1, - 'data': 0, - 'dd': 1, - 'del': 0, - 'details': 0, - 'dfn': 0, - 'dialog': 272, - 'dir': 0, - 'div': 0, - 'dl': 0, - 'dt': 1, - 'em': 0, - 'figcaption': 0, - 'figure': 0, - 'frame': 274, - 'frameset': 272, - 'h1': 0, - 'h2': 0, - 'h3': 0, - 'h4': 0, - 'h5': 0, - 'h6': 0, - 'head': 305, - 'header': 0, - 'hgroup': 0, - 'hr': 2, - 'html': 305, - 'i': 0, - 'iframe': 4, - 'img': 2, - 'ins': 0, - 'isindex': 274, - 'kbd': 0, - 'keygen': 274, - 'label': 0, - 'legend': 0, - 'li': 1, - 'link': 274, - 'nav': 0, - 'nobr': 0, - 'noembed': 276, - 'noframes': 276, - 'noscript': 276, - 'object': 272, - 'ol': 0, - 'p': 1, - 'param': 274, - 'pre': 0, - 'q': 0, - 's': 0, - 'samp': 0, - 'script': 84, - 'section': 0, - 'small': 0, - 'span': 0, - 'strike': 0, - 'strong': 0, - 'style': 148, - 'sub': 0, - 'summary': 0, - 'sup': 0, - 'table': 272, - 'tbody': 273, - 'td': 273, - 'tfoot': 1, - 'th': 273, - 'thead': 273, - 'time': 0, - 'title': 280, - 'tr': 273, - 'tt': 0, - 'u': 0, - 'ul': 0, - 'var': 0, - 'wbr': 2 -}; -html4[ 'ELEMENTS' ] = html4.ELEMENTS; -html4.ELEMENT_DOM_INTERFACES = { - 'a': 'HTMLAnchorElement', - 'abbr': 'HTMLElement', - 'acronym': 'HTMLElement', - 'address': 'HTMLElement', - 'applet': 'HTMLAppletElement', - 'area': 'HTMLAreaElement', - 'article': 'HTMLElement', - 'aside': 'HTMLElement', - 'audio': 'HTMLAudioElement', - 'b': 'HTMLElement', - 'base': 'HTMLBaseElement', - 'basefont': 'HTMLBaseFontElement', - 'bdi': 'HTMLElement', - 'bdo': 'HTMLElement', - 'big': 'HTMLElement', - 'blockquote': 'HTMLQuoteElement', - 'body': 'HTMLBodyElement', - 'br': 'HTMLBRElement', - 'caption': 'HTMLTableCaptionElement', - 'cite': 'HTMLElement', - 'code': 'HTMLElement', - 'col': 'HTMLTableColElement', - 'colgroup': 'HTMLTableColElement', - 'command': 'HTMLCommandElement', - 'data': 'HTMLElement', - 'datalist': 'HTMLDataListElement', - 'dd': 'HTMLElement', - 'del': 'HTMLModElement', - 'details': 'HTMLDetailsElement', - 'dfn': 'HTMLElement', - 'dialog': 'HTMLDialogElement', - 'dir': 'HTMLDirectoryElement', - 'div': 'HTMLDivElement', - 'dl': 'HTMLDListElement', - 'dt': 'HTMLElement', - 'em': 'HTMLElement', - 'fieldset': 'HTMLFieldSetElement', - 'figcaption': 'HTMLElement', - 'figure': 'HTMLElement', - 'footer': 'HTMLElement', - 'form': 'HTMLFormElement', - 'frame': 'HTMLFrameElement', - 'frameset': 'HTMLFrameSetElement', - 'h1': 'HTMLHeadingElement', - 'h2': 'HTMLHeadingElement', - 'h3': 'HTMLHeadingElement', - 'h4': 'HTMLHeadingElement', - 'h5': 'HTMLHeadingElement', - 'h6': 'HTMLHeadingElement', - 'head': 'HTMLHeadElement', - 'header': 'HTMLElement', - 'hgroup': 'HTMLElement', - 'hr': 'HTMLHRElement', - 'html': 'HTMLHtmlElement', - 'i': 'HTMLElement', - 'iframe': 'HTMLIFrameElement', - 'img': 'HTMLImageElement', - 'input': 'HTMLInputElement', - 'ins': 'HTMLModElement', - 'isindex': 'HTMLUnknownElement', - 'kbd': 'HTMLElement', - 'keygen': 'HTMLKeygenElement', - 'label': 'HTMLLabelElement', - 'legend': 'HTMLLegendElement', - 'li': 'HTMLLIElement', - 'link': 'HTMLLinkElement', - 'map': 'HTMLMapElement', - 'menu': 'HTMLMenuElement', - 'meta': 'HTMLMetaElement', - 'nav': 'HTMLElement', - 'nobr': 'HTMLElement', - 'noembed': 'HTMLElement', - 'noframes': 'HTMLElement', - 'noscript': 'HTMLElement', - 'object': 'HTMLObjectElement', - 'ol': 'HTMLOListElement', - 'optgroup': 'HTMLOptGroupElement', - 'option': 'HTMLOptionElement', - 'output': 'HTMLOutputElement', - 'p': 'HTMLParagraphElement', - 'param': 'HTMLParamElement', - 'pre': 'HTMLPreElement', - 'q': 'HTMLQuoteElement', - 's': 'HTMLElement', - 'samp': 'HTMLElement', - 'script': 'HTMLScriptElement', - 'section': 'HTMLElement', - 'select': 'HTMLSelectElement', - 'small': 'HTMLElement', - 'source': 'HTMLSourceElement', - 'span': 'HTMLSpanElement', - 'strike': 'HTMLElement', - 'strong': 'HTMLElement', - 'style': 'HTMLStyleElement', - 'sub': 'HTMLElement', - 'summary': 'HTMLElement', - 'sup': 'HTMLElement', - 'table': 'HTMLTableElement', - 'tbody': 'HTMLTableSectionElement', - 'td': 'HTMLTableDataCellElement', - 'tfoot': 'HTMLTableSectionElement', - 'th': 'HTMLTableHeaderCellElement', - 'thead': 'HTMLTableSectionElement', - 'time': 'HTMLTimeElement', - 'title': 'HTMLTitleElement', - 'tr': 'HTMLTableRowElement', - 'tt': 'HTMLElement', - 'u': 'HTMLElement', - 'ul': 'HTMLUListElement', - 'var': 'HTMLElement', - 'video': 'HTMLVideoElement', - 'wbr': 'HTMLElement' -}; -html4[ 'ELEMENT_DOM_INTERFACES' ] = html4.ELEMENT_DOM_INTERFACES; -html4.ueffects = { - 'NOT_LOADED': 0, - 'SAME_DOCUMENT': 1, - 'NEW_DOCUMENT': 2 -}; -html4[ 'ueffects' ] = html4.ueffects; -html4.URIEFFECTS = { - 'a::href': 2, - 'area::href': 2, - 'audio::src': 1, - 'blockquote::cite': 0, - 'command::icon': 1, - 'del::cite': 0, - 'form::action': 2, - 'iframe::src': 1, - 'img::src': 1, - 'input::src': 1, - 'ins::cite': 0, - 'q::cite': 0, - 'video::poster': 1, - 'video::src': 1 -}; -html4[ 'URIEFFECTS' ] = html4.URIEFFECTS; -html4.ltypes = { - 'UNSANDBOXED': 2, - 'SANDBOXED': 1, - 'DATA': 0 -}; -html4[ 'ltypes' ] = html4.ltypes; -html4.LOADERTYPES = { - 'a::href': 2, - 'area::href': 2, - 'audio::src': 2, - 'blockquote::cite': 2, - 'command::icon': 1, - 'del::cite': 2, - 'form::action': 2, - 'iframe::src': 2, - 'img::src': 1, - 'input::src': 1, - 'ins::cite': 2, - 'q::cite': 2, - 'video::poster': 1, - 'video::src': 2 -}; -html4[ 'LOADERTYPES' ] = html4.LOADERTYPES; -// NOTE: currently focused only on URI-type attributes -html4.REQUIREDATTRIBUTES = { - "audio" : ["src"], - "form" : ["action"], - "iframe" : ["src"], - "image" : ["src"], - "video" : ["src"] -}; -html4[ 'REQUIREDATTRIBUTES' ] = html4.REQUIREDATTRIBUTES; -// export for Closure Compiler -if (typeof window !== 'undefined') { - window['html4'] = html4; -} -; -// Copyright (C) 2006 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/** - * @fileoverview - * An HTML sanitizer that can satisfy a variety of security policies. - * - *

- * The HTML sanitizer is built around a SAX parser and HTML element and - * attributes schemas. - * - * If the cssparser is loaded, inline styles are sanitized using the - * css property and value schemas. Else they are remove during - * sanitization. - * - * If it exists, uses parseCssDeclarations, sanitizeCssProperty, cssSchema - * - * @author mikesamuel@gmail.com - * @author jasvir@gmail.com - * \@requires html4, URI - * \@overrides window - * \@provides html, html_sanitize - */ - -// The Turkish i seems to be a non-issue, but abort in case it is. -if ('I'.toLowerCase() !== 'i') { throw 'I/i problem'; } - -/** - * \@namespace - */ -var html = (function(html4) { - - // For closure compiler - var parseCssDeclarations, sanitizeCssProperty, cssSchema; - if ('undefined' !== typeof window) { - parseCssDeclarations = window['parseCssDeclarations']; - sanitizeCssProperty = window['sanitizeCssProperty']; - cssSchema = window['cssSchema']; - } - - // The keys of this object must be 'quoted' or JSCompiler will mangle them! - // This is a partial list -- lookupEntity() uses the host browser's parser - // (when available) to implement full entity lookup. - // Note that entities are in general case-sensitive; the uppercase ones are - // explicitly defined by HTML5 (presumably as compatibility). - var ENTITIES = { - 'lt': '<', - 'LT': '<', - 'gt': '>', - 'GT': '>', - 'amp': '&', - 'AMP': '&', - 'quot': '"', - 'apos': '\'', - 'nbsp': '\240' - }; - - // Patterns for types of entity/character reference names. - var decimalEscapeRe = /^#(\d+)$/; - var hexEscapeRe = /^#x([0-9A-Fa-f]+)$/; - // contains every entity per http://www.w3.org/TR/2011/WD-html5-20110113/named-character-references.html - var safeEntityNameRe = /^[A-Za-z][A-za-z0-9]+$/; - // Used as a hook to invoke the browser's entity parsing. "), "hullo"); - equal(sanitize(""), "press me!"); - equal(sanitize("draw me!"), "draw me!"); - equal(sanitize("hello"), "hello"); - equal(sanitize("highlight"), "highlight"); - - cooked("[the answer](javascript:alert(42))", "

the answer

", "it prevents XSS"); - - cooked("\n", "


", "it doesn't circumvent XSS with comments"); - - cooked("a", "

a

", "it sanitizes spans"); - cooked("a", "

a

", "it sanitizes spans"); - cooked("a", "

a

", "it sanitizes spans"); -}); - test("URLs in BBCode tags", function() { cooked("[img]http://eviltrout.com/eviltrout.png[/img][img]http://samsaffron.com/samsaffron.png[/img]", @@ -531,23 +500,6 @@ test("URLs in BBCode tags", function() { }); -test("urlAllowed", function() { - var urlAllowed = Discourse.Markdown.urlAllowed; - - var allowed = function(url, msg) { - equal(urlAllowed(url), url, msg); - }; - - allowed("/foo/bar.html", "allows relative urls"); - allowed("http://eviltrout.com/evil/trout", "allows full urls"); - allowed("https://eviltrout.com/evil/trout", "allows https urls"); - allowed("//eviltrout.com/evil/trout", "allows protocol relative urls"); - - equal(urlAllowed("http://google.com/test'onmouseover=alert('XSS!');//.swf"), - "http://google.com/test%27onmouseover=alert(%27XSS!%27);//.swf", - "escape single quotes"); -}); - test("images", function() { cooked("[![folksy logo](http://folksy.com/images/folksy-colour.png)](http://folksy.com/)", "

\"folksy

", @@ -559,7 +511,6 @@ test("images", function() { }); test("censoring", function() { - Discourse.SiteSettings.censored_words = "shucks|whiz|whizzer"; cooked("aw shucks, golly gee whiz.", "

aw ■■■■■■, golly gee ■■■■.

", "it censors words in the Site Settings"); @@ -583,3 +534,166 @@ test("code blocks/spans hoisting", function() { "

$&

", "it works even when hoisting special replacement patterns"); }); + +test('basic bbcode', function() { + cookedPara("[b]strong[/b]", "strong", "bolds text"); + cookedPara("[i]emphasis[/i]", "emphasis", "italics text"); + cookedPara("[u]underlined[/u]", "underlined", "underlines text"); + cookedPara("[s]strikethrough[/s]", "strikethrough", "strikes-through text"); + cookedPara("[img]http://eviltrout.com/eviltrout.png[/img]", "", "links images"); + cookedPara("[email]eviltrout@mailinator.com[/email]", "eviltrout@mailinator.com", "supports [email] without a title"); + cookedPara("[b]evil [i]trout[/i][/b]", + "evil trout", + "allows embedding of tags"); + cookedPara("[EMAIL]eviltrout@mailinator.com[/EMAIL]", "eviltrout@mailinator.com", "supports upper case bbcode"); + cookedPara("[b]strong [b]stronger[/b][/b]", "strong stronger", "accepts nested bbcode tags"); +}); + +test('urls', function() { + cookedPara("[url]not a url[/url]", "not a url", "supports [url] that isn't a url"); + cookedPara("[url]abc.com[/url]", "abc.com", "no error when a url has no protocol and begins with a"); + cookedPara("[url]http://bettercallsaul.com[/url]", "http://bettercallsaul.com", "supports [url] without parameter"); + cookedPara("[url=http://example.com]example[/url]", "example", "supports [url] with given href"); + cookedPara("[url=http://www.example.com][img]http://example.com/logo.png[/img][/url]", + "", + "supports [url] with an embedded [img]"); +}); +test('invalid bbcode', function() { + const result = new PrettyText({ lookupAvatar: false }).cook("[code]I am not closed\n\nThis text exists."); + equal(result, "

[code]I am not closed

\n\n

This text exists.

", "does not raise an error with an open bbcode tag."); +}); + +test('code', function() { + cookedPara("[code]\nx++\n[/code]", "
x++
", "makes code into pre"); + cookedPara("[code]\nx++\ny++\nz++\n[/code]", "
x++\ny++\nz++
", "makes code into pre"); + cookedPara("[code]abc\n#def\n[/code]", '
abc\n#def
', 'it handles headings in a [code] block'); + cookedPara("[code]\n s[/code]", + "
   s
", + "it doesn't trim leading whitespace"); +}); + +test('lists', function() { + cookedPara("[ul][li]option one[/li][/ul]", "
  • option one
", "creates an ul"); + cookedPara("[ol][li]option one[/li][/ol]", "
  1. option one
", "creates an ol"); + cookedPara("[ul]\n[li]option one[/li]\n[li]option two[/li]\n[/ul]", "
  • option one
  • option two
", "suppresses empty lines in lists"); +}); + +test('tags with arguments', function() { + cookedPara("[url=http://bettercallsaul.com]better call![/url]", "better call!", "supports [url] with a title"); + cookedPara("[email=eviltrout@mailinator.com]evil trout[/email]", "evil trout", "supports [email] with a title"); + cookedPara("[u][i]abc[/i][/u]", "abc", "can nest tags"); + cookedPara("[b]first[/b] [b]second[/b]", "first second", "can bold two things on the same line"); +}); + + +test("quotes", function() { + const post = Post.create({ + cooked: "

lorem ipsum

", + username: "eviltrout", + post_number: 1, + topic_id: 2 + }); + + function formatQuote(val, expected, text) { + equal(Quote.build(post, val), expected, text); + }; + + formatQuote(undefined, "", "empty string for undefined content"); + formatQuote(null, "", "empty string for null content"); + formatQuote("", "", "empty string for empty string content"); + + formatQuote("lorem", "[quote=\"eviltrout, post:1, topic:2\"]\nlorem\n[/quote]\n\n", "correctly formats quotes"); + + formatQuote(" lorem \t ", + "[quote=\"eviltrout, post:1, topic:2\"]\nlorem\n[/quote]\n\n", + "trims white spaces before & after the quoted contents"); + + formatQuote("lorem ipsum", + "[quote=\"eviltrout, post:1, topic:2, full:true\"]\nlorem ipsum\n[/quote]\n\n", + "marks quotes as full when the quote is the full message"); + + formatQuote("**lorem** ipsum", + "[quote=\"eviltrout, post:1, topic:2, full:true\"]\n**lorem** ipsum\n[/quote]\n\n", + "keeps BBCode formatting"); + + formatQuote("this is a bug", + "[quote=\"eviltrout, post:1, topic:2\"]\nthis is <not> a bug\n[/quote]\n\n", + "it escapes the contents of the quote"); + + cookedPara("[quote]test[/quote]", + "", + "it supports quotes without params"); + + cookedPara("[quote]\n*test*\n[/quote]", + "", + "it doesn't insert a new line for italics"); + + cookedPara("[quote=,script='a'>"), "
"); + equal(pt.sanitize("

hello

"), "

hello

"); + equal(pt.sanitize("<3 <3"), "<3 <3"); + equal(pt.sanitize("<_<"), "<_<"); + cooked("hello", "

hello

", "it sanitizes while cooking"); + + cooked("disney reddit", + "

disney reddit

", + "we can embed proper links"); + + cooked("
hello
", "

hello

", "it does not allow centering"); + cooked("
hello
\nafter", "

after

", "it does not allow tables"); + cooked("
a\n
\n", "
a\n\n
\n\n
", "it does not double sanitize"); + + cooked("", "", "it does not allow most iframes"); + + cooked("", + "", + "it allows iframe to google maps"); + + cooked("", + "", + "it allows iframe to OpenStreetMap"); + + equal(pt.sanitize(""), "hullo"); + equal(pt.sanitize(""), "press me!"); + equal(pt.sanitize("draw me!"), "draw me!"); + equal(pt.sanitize("hello"), "hello"); + equal(pt.sanitize("highlight"), "highlight"); + + cooked("[the answer](javascript:alert(42))", "

the answer

", "it prevents XSS"); + + cooked("\n", "


", "it doesn't circumvent XSS with comments"); + + cooked("a", "

a

", "it sanitizes spans"); + cooked("a", "

a

", "it sanitizes spans"); + cooked("a", "

a

", "it sanitizes spans"); + + cooked("Ctrl+C", "

Ctrl+C

"); + cooked("it has been 1 day 0 days since our last test failure", "

it has been 1 day 0 days since our last test failure

"); + cooked(`it has been 1 day 0 days since our last test failure`, `

it has been 1 day 0 days since our last test failure

`); + + cooked(`
hello
`, `
hello
`); + + cooked(`1 + 1 is 3 2`, `

1 + 1 is 3 2

`); + cooked(`JS`, `

JS

`); + cooked(`
Forum
Software
`, `
Forum
Software
`); + cooked(`high low`, `

high low

`); +}); + +test("ids on headings", () => { + const pt = new PrettyText(buildOptions({ siteSettings: {} })); + equal(pt.sanitize("

Test Heading

"), "

Test Heading

"); + equal(pt.sanitize(`

Test Heading

`), `

Test Heading

`); + equal(pt.sanitize(`

Test Heading

`), `

Test Heading

`); + equal(pt.sanitize(`

Test Heading

`), `

Test Heading

`); + equal(pt.sanitize(`

Test Heading

`), `

Test Heading

`); + equal(pt.sanitize(`
Test Heading
`), `
Test Heading
`); + equal(pt.sanitize(`
Test Heading
`), `
Test Heading
`); +}); + +test("urlAllowed", () => { + const allowed = (url, msg) => equal(hrefAllowed(url), url, msg); + + allowed("/foo/bar.html", "allows relative urls"); + allowed("http://eviltrout.com/evil/trout", "allows full urls"); + allowed("https://eviltrout.com/evil/trout", "allows https urls"); + allowed("//eviltrout.com/evil/trout", "allows protocol relative urls"); + + equal(hrefAllowed("http://google.com/test'onmouseover=alert('XSS!');//.swf"), + "http://google.com/test%27onmouseover=alert(%27XSS!%27);//.swf", + "escape single quotes"); +}); + diff --git a/test/javascripts/lib/utilities-test.js.es6 b/test/javascripts/lib/utilities-test.js.es6 index c9f35a948b..75b8cc194d 100644 --- a/test/javascripts/lib/utilities-test.js.es6 +++ b/test/javascripts/lib/utilities-test.js.es6 @@ -1,16 +1,27 @@ /* global Int8Array:true */ import { blank } from 'helpers/qunit-helpers'; +import { + emailValid, + isAnImage, + avatarUrl, + allowsAttachments, + getRawSize, + avatarImg, + defaultHomepage, + validateUploadedFiles, + getUploadMarkdown, + caretRowCol, + setCaretPosition +} from 'discourse/lib/utilities'; -module("Discourse.Utilities"); - -var utils = Discourse.Utilities; +module("lib:utilities"); test("emailValid", function() { - ok(utils.emailValid('Bob@example.com'), "allows upper case in the first part of emails"); - ok(utils.emailValid('bob@EXAMPLE.com'), "allows upper case in the email domain"); + ok(emailValid('Bob@example.com'), "allows upper case in the first part of emails"); + ok(emailValid('bob@EXAMPLE.com'), "allows upper case in the email domain"); }); -var validUpload = utils.validateUploadedFiles; +var validUpload = validateUploadedFiles; test("validateUploadedFiles", function() { not(validUpload(null), "no files are invalid"); @@ -80,8 +91,8 @@ test("allows valid uploads to go through", function() { not(bootbox.alert.calledOnce); }); -var getUploadMarkdown = function(filename) { - return utils.getUploadMarkdown({ +var testUploadMarkdown = function(filename) { + return getUploadMarkdown({ original_filename: filename, filesize: 42, width: 100, @@ -91,26 +102,26 @@ var getUploadMarkdown = function(filename) { }; test("getUploadMarkdown", function() { - ok(getUploadMarkdown("lolcat.gif") === ''); - ok(getUploadMarkdown("important.txt") === 'important.txt (42 Bytes)\n'); + ok(testUploadMarkdown("lolcat.gif") === ''); + ok(testUploadMarkdown("important.txt") === 'important.txt (42 Bytes)\n'); }); test("isAnImage", function() { _.each(["png", "jpg", "jpeg", "bmp", "gif", "tif", "tiff", "ico"], function(extension) { var image = "image." + extension; - ok(utils.isAnImage(image), image + " is recognized as an image"); - ok(utils.isAnImage("http://foo.bar/path/to/" + image), image + " is recognized as an image"); + ok(isAnImage(image), image + " is recognized as an image"); + ok(isAnImage("http://foo.bar/path/to/" + image), image + " is recognized as an image"); }); - not(utils.isAnImage("file.txt")); - not(utils.isAnImage("http://foo.bar/path/to/file.txt")); - not(utils.isAnImage("")); + not(isAnImage("file.txt")); + not(isAnImage("http://foo.bar/path/to/file.txt")); + not(isAnImage("")); }); test("avatarUrl", function() { - var rawSize = utils.getRawSize; - blank(utils.avatarUrl('', 'tiny'), "no template returns blank"); - equal(utils.avatarUrl('/fake/template/{size}.png', 'tiny'), "/fake/template/" + rawSize(20) + ".png", "simple avatar url"); - equal(utils.avatarUrl('/fake/template/{size}.png', 'large'), "/fake/template/" + rawSize(45) + ".png", "different size"); + var rawSize = getRawSize; + blank(avatarUrl('', 'tiny'), "no template returns blank"); + equal(avatarUrl('/fake/template/{size}.png', 'tiny'), "/fake/template/" + rawSize(20) + ".png", "simple avatar url"); + equal(avatarUrl('/fake/template/{size}.png', 'large'), "/fake/template/" + rawSize(45) + ".png", "different size"); }); var setDevicePixelRatio = function(value) { @@ -126,19 +137,19 @@ test("avatarImg", function() { setDevicePixelRatio(2); var avatarTemplate = "/path/to/avatar/{size}.png"; - equal(utils.avatarImg({avatarTemplate: avatarTemplate, size: 'tiny'}), + equal(avatarImg({avatarTemplate: avatarTemplate, size: 'tiny'}), "", "it returns the avatar html"); - equal(utils.avatarImg({avatarTemplate: avatarTemplate, size: 'tiny', title: 'evilest trout'}), + equal(avatarImg({avatarTemplate: avatarTemplate, size: 'tiny', title: 'evilest trout'}), "", "it adds a title if supplied"); - equal(utils.avatarImg({avatarTemplate: avatarTemplate, size: 'tiny', extraClasses: 'evil fish'}), + equal(avatarImg({avatarTemplate: avatarTemplate, size: 'tiny', extraClasses: 'evil fish'}), "", "it adds extra classes if supplied"); - blank(utils.avatarImg({avatarTemplate: "", size: 'tiny'}), + blank(avatarImg({avatarTemplate: "", size: 'tiny'}), "it doesn't render avatars for invalid avatar template"); setDevicePixelRatio(oldRatio); @@ -146,18 +157,18 @@ test("avatarImg", function() { test("allowsAttachments", function() { Discourse.SiteSettings.authorized_extensions = "jpg|jpeg|gif"; - not(utils.allowsAttachments(), "no attachments allowed by default"); + not(allowsAttachments(), "no attachments allowed by default"); Discourse.SiteSettings.authorized_extensions = "jpg|jpeg|gif|*"; - ok(utils.allowsAttachments(), "attachments are allowed when all extensions are allowed"); + ok(allowsAttachments(), "attachments are allowed when all extensions are allowed"); Discourse.SiteSettings.authorized_extensions = "jpg|jpeg|gif|pdf"; - ok(utils.allowsAttachments(), "attachments are allowed when at least one extension is not an image extension"); + ok(allowsAttachments(), "attachments are allowed when at least one extension is not an image extension"); }); test("defaultHomepage", function() { Discourse.SiteSettings.top_menu = "latest|top|hot"; - equal(utils.defaultHomepage(), "latest", "default homepage is the first item in the top_menu site setting"); + equal(defaultHomepage(), "latest", "default homepage is the first item in the top_menu site setting"); }); test("caretRowCol", () => { @@ -167,9 +178,9 @@ test("caretRowCol", () => { document.body.appendChild(textarea); const assertResult = (setCaretPos, expectedRowNum, expectedColNum) => { - Discourse.Utilities.setCaretPosition(textarea, setCaretPos); + setCaretPosition(textarea, setCaretPos); - const result = Discourse.Utilities.caretRowCol(textarea); + const result = caretRowCol(textarea); equal(result.rowNum, expectedRowNum, "returns the right row of the caret"); equal(result.colNum, expectedColNum, "returns the right col of the caret"); }; diff --git a/test/javascripts/mdtest/fixtures/Markdown Documentation - Syntax.xhtml b/test/javascripts/mdtest/fixtures/Markdown Documentation - Syntax.xhtml index 2a83541bdc..41c48857bb 100644 --- a/test/javascripts/mdtest/fixtures/Markdown Documentation - Syntax.xhtml +++ b/test/javascripts/mdtest/fixtures/Markdown Documentation - Syntax.xhtml @@ -43,9 +43,9 @@ can see the source for it by adding '.t
-

Overview

+

Overview

-

Philosophy

+

Philosophy

Markdown is intended to be as easy-to-read and easy-to-write as is feasible.

@@ -64,7 +64,7 @@ look like *emphasis*. Markdown lists look like, well, lists. Even blockquotes look like quoted passages of text, assuming you've ever used email.

-

Inline HTML

+

Inline HTML

Markdown's syntax is intended for one purpose: to be used as a format for writing for the web.

@@ -115,7 +115,7 @@ link or image syntax, go right ahead.

Unlike block-level HTML tags, Markdown syntax is processed within span-level tags.

-

Automatic Escaping for Special Characters

+

Automatic Escaping for Special Characters

In HTML, there are two characters that demand special treatment: < and &. Left angle brackets are used to start tags; ampersands are @@ -179,9 +179,9 @@ and & in your example code needs to be escaped.)


-

Block Elements

+

Block Elements

-

Paragraphs and Line Breaks

+

Paragraphs and Line Breaks

A paragraph is simply one or more consecutive lines of text, separated by one or more blank lines. (A blank line is any line that looks like a @@ -202,7 +202,7 @@ end a line with two or more spaces, then type return.

Markdown's email-style
blockquoting and multi-paragraph list items work best -- and look better -- when you format them with hard breaks.

-

Headers

+

Markdown supports two styles of headers, Setext and atx.

@@ -241,7 +241,7 @@ determines the header level.) :

### This is an H3 ######
-

Blockquotes

+

Blockquotes

Markdown uses email-style > characters for blockquoting. If you're familiar with quoting passages of text in an email message, then you @@ -296,7 +296,7 @@ and code blocks:

example, with BBEdit, you can make a selection and choose Increase Quote Level from the Text menu.

-

Lists

+

Lists

Markdown supports ordered (numbered) and unordered (bulleted) lists.

@@ -472,7 +472,7 @@ line. To avoid this, you can backslash-escape the period:

1986\. What a great season.
 
-

Code Blocks

+

Code Blocks

Pre-formatted code blocks are used for writing about programming or markup source code. Rather than forming normal paragraphs, the lines @@ -541,7 +541,7 @@ ampersands and angle brackets. For example, this:

asterisks are just literal asterisks within a code block. This means it's also easy to use Markdown to write about Markdown's own syntax.

-

Horizontal Rules

+

Horizontal Rules

You can produce a horizontal rule tag (<hr />) by placing three or more hyphens, asterisks, or underscores on a line by themselves. If you @@ -563,9 +563,9 @@ _ _ _


-

Span Elements

+

Span Elements

-

Links

+

Markdown supports two style of links: inline and reference.

@@ -727,7 +727,7 @@ allowing you to move the markup-related metadata out of the paragraph, you can add links without interrupting the narrative flow of your prose.

-

Emphasis

+

Emphasis

Markdown treats asterisks (*) and underscores (_) as indicators of emphasis. Text wrapped with one * or _ will be wrapped with an @@ -772,7 +772,7 @@ escape it:

\*this text is surrounded by literal asterisks\*
 
-

Code

+

Code

To indicate a span of code, wrap it with backtick quotes (`). Unlike a pre-formatted code block, a code span indicates code within a @@ -836,7 +836,7 @@ tags. Markdown will turn this:

equivalent of <code>&amp;mdash;</code>.</p> -

Images

+

Images

Admittedly, it's fairly difficult to devise a "natural" syntax for placing images into a plain text document format.

@@ -879,9 +879,9 @@ use regular HTML <img> tags.


-

Miscellaneous

+

Miscellaneous

-

Automatic Links

+

Markdown supports a shortcut style for creating "automatic" links for URLs and email addresses: simply surround the URL or email address with angle brackets. What this means is that if you want to show the actual text of a URL or email address, and also have it be a clickable link, you can do this:

@@ -916,7 +916,7 @@ most, address-harvesting bots, but it definitely won't fool all of them. It's better than nothing, but an address published in this way will probably eventually start receiving spam.)

-

Backslash Escapes

+

Backslash Escapes

Markdown allows you to use backslash escapes to generate literal characters which would otherwise have special meaning in Markdown's diff --git a/test/javascripts/mdtest/mdtest.js.erb b/test/javascripts/mdtest/mdtest.js.es6.erb similarity index 55% rename from test/javascripts/mdtest/mdtest.js.erb rename to test/javascripts/mdtest/mdtest.js.es6.erb index f1c9a71381..a17d564513 100644 --- a/test/javascripts/mdtest/mdtest.js.erb +++ b/test/javascripts/mdtest/mdtest.js.es6.erb @@ -1,8 +1,9 @@ -module("MDTest", { - setup: function() { - Discourse.SiteSettings.traditional_markdown_linebreaks = false; - } -}); +import { sanitize } from 'pretty-text/sanitizer'; +import { default as PrettyText, buildOptions } from 'pretty-text/pretty-text'; +import { hashString } from 'discourse/lib/hash'; + +// Run the MDTest spec +module("MDTest"); // This is cheating, but the trivial differences between sanitization // do not affect formatting. @@ -15,44 +16,42 @@ function normalize(str) { // We use a custom sanitizer for MD test that hoists out comments. In Discourse // they are stripped, but to be compliant with the spec they should not be. -function hoistingSanitizer(result) { - var hoisted, - m = result.match(//g); +function sanitizer(result, whiteLister) { + let hoisted; + const m = result.match(//g); if (m && m.length) { hoisted = []; - for (var i=0; i result = result.replace(tuple[1], tuple[0])); } return result; } -var md = function(input, expected, text) { - var result = Discourse.Markdown.cook(input, { - sanitizerFunction: hoistingSanitizer, - traditional_markdown_linebreaks: true - }), - resultNorm = normalize(result), - expectedNorm = normalize(expected), - same = (result === expected) || (resultNorm === expectedNorm); +function md(input, expected, text) { + + const opts = buildOptions({ siteSettings: {} }); + opts.traditionalMarkdownLinebreaks = true; + opts.sanitizer = sanitizer; + + const cooker = new PrettyText(opts); + const result = cooker.cook(input); + const resultNorm = normalize(result); + const expectedNorm = normalize(expected); + const same = (result === expected) || (resultNorm === expectedNorm); if (same) { ok(same, text); } else { - console.log(resultNorm); - console.log(expectedNorm); equal(resultNorm, expectedNorm, text); } }; diff --git a/test/javascripts/models/badge-test.js.es6 b/test/javascripts/models/badge-test.js.es6 index 0b92f88ce4..644056d049 100644 --- a/test/javascripts/models/badge-test.js.es6 +++ b/test/javascripts/models/badge-test.js.es6 @@ -37,19 +37,15 @@ test('updateFromJson', function() { }); test('save', function() { - sandbox.stub(Discourse, 'ajax').returns(Ember.RSVP.resolve({})); + expect(0); const badge = Badge.create({name: "New Badge", description: "This is a new badge.", badge_type_id: 1}); - // TODO: clean API - badge.save(["name", "description", "badge_type_id"]); - ok(Discourse.ajax.calledOnce, "saved badge"); + return badge.save(["name", "description", "badge_type_id"]); }); test('destroy', function() { - sandbox.stub(Discourse, 'ajax'); + expect(0); const badge = Badge.create({name: "New Badge", description: "This is a new badge.", badge_type_id: 1}); badge.destroy(); - ok(!Discourse.ajax.calledOnce, "no AJAX call for a new badge"); badge.set('id', 3); - badge.destroy(); - ok(Discourse.ajax.calledOnce, "AJAX call was made"); + return badge.destroy(); }); diff --git a/test/javascripts/models/topic-test.js.es6 b/test/javascripts/models/topic-test.js.es6 index f1d2469b43..2c1ceac923 100644 --- a/test/javascripts/models/topic-test.js.es6 +++ b/test/javascripts/models/topic-test.js.es6 @@ -1,4 +1,6 @@ import { blank, present } from 'helpers/qunit-helpers'; +import { IMAGE_VERSION as v} from 'pretty-text/emoji'; + module("model:topic"); import Topic from 'discourse/models/topic'; @@ -53,29 +55,22 @@ test("destroy", function() { var user = Discourse.User.create({username: 'eviltrout'}); var topic = Topic.create({id: 1234}); - sandbox.stub(Discourse, 'ajax'); - topic.destroy(user); present(topic.get('deleted_at'), 'deleted at is set'); equal(topic.get('deleted_by'), user, 'deleted by is set'); - //ok(Discourse.ajax.calledOnce, "it called delete over the wire"); }); test("recover", function() { var user = Discourse.User.create({username: 'eviltrout'}); var topic = Topic.create({id: 1234, deleted_at: new Date(), deleted_by: user}); - sandbox.stub(Discourse, 'ajax'); - topic.recover(); blank(topic.get('deleted_at'), "it clears deleted_at"); blank(topic.get('deleted_by'), "it clears deleted_by"); - //ok(Discourse.ajax.calledOnce, "it called recover over the wire"); }); test('fancyTitle', function() { var topic = Topic.create({ fancy_title: ":smile: with all :) the emojis :pear::peach:" }); - const v = Discourse.Emoji.ImageVersion; equal(topic.get('fancyTitle'), `smile with all slight_smile the emojis pearpeach`, diff --git a/test/javascripts/models/user-badge-test.js.es6 b/test/javascripts/models/user-badge-test.js.es6 index bf2a5bf98a..5258d98b06 100644 --- a/test/javascripts/models/user-badge-test.js.es6 +++ b/test/javascripts/models/user-badge-test.js.es6 @@ -1,12 +1,10 @@ import UserBadge from 'discourse/models/user-badge'; +import badgeFixtures from 'fixtures/user-badges'; module("model:user-badge"); -const singleBadgeJson = {"badges":[{"id":874,"name":"Badge 2","description":null,"badge_type_id":7}],"badge_types":[{"id":7,"name":"Silver 2"}],"users":[{"id":13470,"username":"anne3","avatar_template":"//www.gravatar.com/avatar/a4151b1fd72089c54e2374565a87da7f.png?s={size}\u0026r=pg\u0026d=identicon"}],"user_badge":{"id":665,"granted_at":"2014-03-09T20:30:01.190-04:00","badge_id":874,"granted_by_id":13470}}, - multipleBadgesJson = {"badges":[{"id":880,"name":"Badge 8","description":null,"badge_type_id":13}],"badge_types":[{"id":13,"name":"Silver 8"}],"users":[],"user_badges":[{"id":668,"granted_at":"2014-03-09T20:30:01.420-04:00","badge_id":880,"granted_by_id":null}]}; - test('createFromJson single', function() { - const userBadge = UserBadge.createFromJson(singleBadgeJson); + const userBadge = UserBadge.createFromJson(badgeFixtures['/user_badges']); ok(!Array.isArray(userBadge), "does not return an array"); equal(userBadge.get('badge.name'), "Badge 2", "badge reference is set"); equal(userBadge.get('badge.badge_type.name'), "Silver 2", "badge.badge_type reference is set"); @@ -14,44 +12,31 @@ test('createFromJson single', function() { }); test('createFromJson array', function() { - const userBadges = UserBadge.createFromJson(multipleBadgesJson); + const userBadges = UserBadge.createFromJson(badgeFixtures['/user-badges/:username']); ok(Array.isArray(userBadges), "returns an array"); equal(userBadges[0].get('granted_by'), null, "granted_by reference is not set when null"); }); -asyncTestDiscourse('findByUsername', function() { - expect(2); - sandbox.stub(Discourse, 'ajax').returns(Ember.RSVP.resolve(multipleBadgesJson)); - UserBadge.findByUsername("anne3").then(function(badges) { +test('findByUsername', function() { + return UserBadge.findByUsername("anne3").then(function(badges) { ok(Array.isArray(badges), "returns an array"); - start(); }); - ok(Discourse.ajax.calledOnce, "makes an AJAX call"); }); -asyncTestDiscourse('findByBadgeId', function() { - expect(2); - sandbox.stub(Discourse, 'ajax').returns(Ember.RSVP.resolve(multipleBadgesJson)); - UserBadge.findByBadgeId(880).then(function(badges) { +test('findByBadgeId', function() { + return UserBadge.findByBadgeId(880).then(function(badges) { ok(Array.isArray(badges), "returns an array"); - start(); }); - ok(Discourse.ajax.calledOnce, "makes an AJAX call"); }); -asyncTestDiscourse('grant', function() { - expect(2); - sandbox.stub(Discourse, 'ajax').returns(Ember.RSVP.resolve(singleBadgeJson)); - UserBadge.grant(1, "username").then(function(userBadge) { +test('grant', function() { + return UserBadge.grant(1, "username").then(function(userBadge) { ok(!Array.isArray(userBadge), "does not return an array"); - start(); }); - ok(Discourse.ajax.calledOnce, "makes an AJAX call"); }); test('revoke', function() { - sandbox.stub(Discourse, 'ajax'); + expect(0); const userBadge = UserBadge.create({id: 1}); - userBadge.revoke(); - ok(Discourse.ajax.calledOnce, "makes an AJAX call"); + return userBadge.revoke(); }); diff --git a/test/javascripts/test_helper.js b/test/javascripts/test_helper.js index 7362298d15..2ffb2986c3 100644 --- a/test/javascripts/test_helper.js +++ b/test/javascripts/test_helper.js @@ -1,7 +1,6 @@ /*global document, sinon, QUnit, Logster */ //= require env -//= require preload_store //= require probes //= require jquery.debug //= require jquery.ui.widget @@ -13,18 +12,19 @@ //= require fake_xml_http_request //= require route-recognizer //= require pretender +//= require loader +//= require preload-store //= require locales/i18n //= require locales/en -//= require vendor - -//= require htmlparser.js - // Stuff we need to load first +//= require vendor +//= require ember-shim +//= require pretty-text-bundle //= require main_include +//= require htmlparser.js //= require admin -//= require_tree ../../app/assets/javascripts/defer //= require sinon-1.7.1 //= require sinon-qunit-1.0.0 @@ -41,14 +41,9 @@ // //= require jquery.magnific-popup-min.js +window.TestPreloadStore = require('preload-store').default; window.inTestEnv = true; -window.assetPath = function(url) { - if (url.indexOf('defer') === 0) { - return "/assets/" + url; - } -}; - // Stop the message bus so we don't get ajax calls window.MessageBus.stop(); @@ -81,6 +76,13 @@ function dup(obj) { return jQuery.extend(true, {}, obj); } +function resetSite() { + var createStore = require('helpers/create-store').default; + var siteAttrs = dup(fixtures['site.json'].site); + siteAttrs.store = createStore(); + Discourse.Site.resetCurrent(Discourse.Site.create(siteAttrs)); +} + QUnit.testStart(function(ctx) { server = createPretendServer(); @@ -90,14 +92,15 @@ QUnit.testStart(function(ctx) { Discourse.BaseUrl = "localhost"; Discourse.Session.resetCurrent(); Discourse.User.resetCurrent(); - Discourse.Site.resetCurrent(Discourse.Site.create(dup(fixtures['site.json'].site))); + resetSite(); _DiscourseURL.redirectedTo = null; _DiscourseURL.redirectTo = function(url) { _DiscourseURL.redirectedTo = url; }; - PreloadStore.reset(); + var ps = require('preload-store').default; + ps.reset(); window.sandbox = sinon.sandbox.create(); window.sandbox.stub(ScrollingDOMMethods, "screenNotFull"); @@ -137,3 +140,6 @@ Object.keys(requirejs.entries).forEach(function(entry) { require(entry, null, null, true); } }); +require('mdtest/mdtest', null, null, true); +resetSite(); + diff --git a/vendor/assets/javascripts/better_markdown.js b/vendor/assets/javascripts/better_markdown.js index a445aacec5..836b1c7e31 100644 --- a/vendor/assets/javascripts/better_markdown.js +++ b/vendor/assets/javascripts/better_markdown.js @@ -429,6 +429,7 @@ if ( attrs && attrs.references ) refs = attrs.references; + var html = convert_tree_to_html( input, refs , options ); merge_text_nodes( html ); return html; diff --git a/vendor/assets/javascripts/ember-qunit.js b/vendor/assets/javascripts/ember-qunit.js index c3717ef55b..ce4a591162 100644 --- a/vendor/assets/javascripts/ember-qunit.js +++ b/vendor/assets/javascripts/ember-qunit.js @@ -1043,4 +1043,4 @@ window.test = emberQunit.test; window.setResolver = emberQunit.setResolver; })(); -//# sourceMappingURL=ember-qunit.map \ No newline at end of file +//# sourceMappingURL=ember-qunit.map diff --git a/vendor/assets/javascripts/md5.js b/vendor/assets/javascripts/md5.js deleted file mode 100644 index bebcdacc26..0000000000 --- a/vendor/assets/javascripts/md5.js +++ /dev/null @@ -1,180 +0,0 @@ -/*! - * Joseph Myer's md5() algorithm wrapped in a self-invoked function to prevent - * global namespace polution, modified to hash unicode characters as UTF-8. - * - * Copyright 1999-2010, Joseph Myers, Paul Johnston, Greg Holt, Will Bond - * http://www.myersdaily.org/joseph/javascript/md5-text.html - * http://pajhome.org.uk/crypt/md5 - * - * Released under the BSD license - * http://www.opensource.org/licenses/bsd-license - */ -(function() { - function md5cycle(x, k) { - var a = x[0], b = x[1], c = x[2], d = x[3]; - - a = ff(a, b, c, d, k[0], 7, -680876936); - d = ff(d, a, b, c, k[1], 12, -389564586); - c = ff(c, d, a, b, k[2], 17, 606105819); - b = ff(b, c, d, a, k[3], 22, -1044525330); - a = ff(a, b, c, d, k[4], 7, -176418897); - d = ff(d, a, b, c, k[5], 12, 1200080426); - c = ff(c, d, a, b, k[6], 17, -1473231341); - b = ff(b, c, d, a, k[7], 22, -45705983); - a = ff(a, b, c, d, k[8], 7, 1770035416); - d = ff(d, a, b, c, k[9], 12, -1958414417); - c = ff(c, d, a, b, k[10], 17, -42063); - b = ff(b, c, d, a, k[11], 22, -1990404162); - a = ff(a, b, c, d, k[12], 7, 1804603682); - d = ff(d, a, b, c, k[13], 12, -40341101); - c = ff(c, d, a, b, k[14], 17, -1502002290); - b = ff(b, c, d, a, k[15], 22, 1236535329); - - a = gg(a, b, c, d, k[1], 5, -165796510); - d = gg(d, a, b, c, k[6], 9, -1069501632); - c = gg(c, d, a, b, k[11], 14, 643717713); - b = gg(b, c, d, a, k[0], 20, -373897302); - a = gg(a, b, c, d, k[5], 5, -701558691); - d = gg(d, a, b, c, k[10], 9, 38016083); - c = gg(c, d, a, b, k[15], 14, -660478335); - b = gg(b, c, d, a, k[4], 20, -405537848); - a = gg(a, b, c, d, k[9], 5, 568446438); - d = gg(d, a, b, c, k[14], 9, -1019803690); - c = gg(c, d, a, b, k[3], 14, -187363961); - b = gg(b, c, d, a, k[8], 20, 1163531501); - a = gg(a, b, c, d, k[13], 5, -1444681467); - d = gg(d, a, b, c, k[2], 9, -51403784); - c = gg(c, d, a, b, k[7], 14, 1735328473); - b = gg(b, c, d, a, k[12], 20, -1926607734); - - a = hh(a, b, c, d, k[5], 4, -378558); - d = hh(d, a, b, c, k[8], 11, -2022574463); - c = hh(c, d, a, b, k[11], 16, 1839030562); - b = hh(b, c, d, a, k[14], 23, -35309556); - a = hh(a, b, c, d, k[1], 4, -1530992060); - d = hh(d, a, b, c, k[4], 11, 1272893353); - c = hh(c, d, a, b, k[7], 16, -155497632); - b = hh(b, c, d, a, k[10], 23, -1094730640); - a = hh(a, b, c, d, k[13], 4, 681279174); - d = hh(d, a, b, c, k[0], 11, -358537222); - c = hh(c, d, a, b, k[3], 16, -722521979); - b = hh(b, c, d, a, k[6], 23, 76029189); - a = hh(a, b, c, d, k[9], 4, -640364487); - d = hh(d, a, b, c, k[12], 11, -421815835); - c = hh(c, d, a, b, k[15], 16, 530742520); - b = hh(b, c, d, a, k[2], 23, -995338651); - - a = ii(a, b, c, d, k[0], 6, -198630844); - d = ii(d, a, b, c, k[7], 10, 1126891415); - c = ii(c, d, a, b, k[14], 15, -1416354905); - b = ii(b, c, d, a, k[5], 21, -57434055); - a = ii(a, b, c, d, k[12], 6, 1700485571); - d = ii(d, a, b, c, k[3], 10, -1894986606); - c = ii(c, d, a, b, k[10], 15, -1051523); - b = ii(b, c, d, a, k[1], 21, -2054922799); - a = ii(a, b, c, d, k[8], 6, 1873313359); - d = ii(d, a, b, c, k[15], 10, -30611744); - c = ii(c, d, a, b, k[6], 15, -1560198380); - b = ii(b, c, d, a, k[13], 21, 1309151649); - a = ii(a, b, c, d, k[4], 6, -145523070); - d = ii(d, a, b, c, k[11], 10, -1120210379); - c = ii(c, d, a, b, k[2], 15, 718787259); - b = ii(b, c, d, a, k[9], 21, -343485551); - - x[0] = add32(a, x[0]); - x[1] = add32(b, x[1]); - x[2] = add32(c, x[2]); - x[3] = add32(d, x[3]); - } - - function cmn(q, a, b, x, s, t) { - a = add32(add32(a, q), add32(x, t)); - return add32((a << s) | (a >>> (32 - s)), b); - } - - function ff(a, b, c, d, x, s, t) { - return cmn((b & c) | ((~b) & d), a, b, x, s, t); - } - - function gg(a, b, c, d, x, s, t) { - return cmn((b & d) | (c & (~d)), a, b, x, s, t); - } - - function hh(a, b, c, d, x, s, t) { - return cmn(b ^ c ^ d, a, b, x, s, t); - } - - function ii(a, b, c, d, x, s, t) { - return cmn(c ^ (b | (~d)), a, b, x, s, t); - } - - function md51(s) { - // Converts the string to UTF-8 "bytes" when necessary - if (/[\x80-\xFF]/.test(s)) { - s = unescape(encodeURI(s)); - } - txt = ''; - var n = s.length, state = [1732584193, -271733879, -1732584194, 271733878], i; - for (i = 64; i <= s.length; i += 64) { - md5cycle(state, md5blk(s.substring(i - 64, i))); - } - s = s.substring(i - 64); - var tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - for (i = 0; i < s.length; i++) - tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3); - tail[i >> 2] |= 0x80 << ((i % 4) << 3); - if (i > 55) { - md5cycle(state, tail); - for (i = 0; i < 16; i++) tail[i] = 0; - } - tail[14] = n * 8; - md5cycle(state, tail); - return state; - } - - function md5blk(s) { /* I figured global was faster. */ - var md5blks = [], i; /* Andy King said do it this way. */ - for (i = 0; i < 64; i += 4) { - md5blks[i >> 2] = s.charCodeAt(i) + - (s.charCodeAt(i + 1) << 8) + - (s.charCodeAt(i + 2) << 16) + - (s.charCodeAt(i + 3) << 24); - } - return md5blks; - } - - var hex_chr = '0123456789abcdef'.split(''); - - function rhex(n) { - var s = '', j = 0; - for (; j < 4; j++) - s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + - hex_chr[(n >> (j * 8)) & 0x0F]; - return s; - } - - function hex(x) { - for (var i = 0; i < x.length; i++) - x[i] = rhex(x[i]); - return x.join(''); - } - - md5 = function (s) { - return hex(md51(s)); - } - - /* this function is much faster, so if possible we use it. Some IEs are the - only ones I know of that need the idiotic second function, generated by an - if clause. */ - function add32(a, b) { - return (a + b) & 0xFFFFFFFF; - } - - if (md5('hello') != '5d41402abc4b2a76b9719d911017c592') { - function add32(x, y) { - var lsw = (x & 0xFFFF) + (y & 0xFFFF), - msw = (x >> 16) + (y >> 16) + (lsw >> 16); - return (msw << 16) | (lsw & 0xFFFF); - } - } -})(); \ No newline at end of file diff --git a/vendor/assets/javascripts/xss.min.js b/vendor/assets/javascripts/xss.min.js new file mode 100644 index 0000000000..48d7880783 --- /dev/null +++ b/vendor/assets/javascripts/xss.min.js @@ -0,0 +1 @@ +(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);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o/g;var REGEXP_QUOTE=/"/g;var REGEXP_QUOTE_2=/"/g;var REGEXP_ATTR_VALUE_1=/&#([a-zA-Z0-9]*);?/gim;var REGEXP_ATTR_VALUE_COLON=/:?/gim;var REGEXP_ATTR_VALUE_NEWLINE=/&newline;?/gim;var REGEXP_DEFAULT_ON_TAG_ATTR_3=/\/\*|\*\//gm;var REGEXP_DEFAULT_ON_TAG_ATTR_4=/((j\s*a\s*v\s*a|v\s*b|l\s*i\s*v\s*e)\s*s\s*c\s*r\s*i\s*p\s*t\s*|m\s*o\s*c\s*h\s*a)\:/gi;var REGEXP_DEFAULT_ON_TAG_ATTR_5=/^[\s"'`]*(d\s*a\s*t\s*a\s*)\:/gi;var REGEXP_DEFAULT_ON_TAG_ATTR_6=/^[\s"'`]*(d\s*a\s*t\s*a\s*)\:\s*image\//gi;var REGEXP_DEFAULT_ON_TAG_ATTR_7=/e\s*x\s*p\s*r\s*e\s*s\s*s\s*i\s*o\s*n\s*\(.*/gi;var REGEXP_DEFAULT_ON_TAG_ATTR_8=/u\s*r\s*l\s*\(.*/gi;function escapeQuote(str){return str.replace(REGEXP_QUOTE,""")}function unescapeQuote(str){return str.replace(REGEXP_QUOTE_2,'"')}function escapeHtmlEntities(str){return str.replace(REGEXP_ATTR_VALUE_1,function replaceUnicode(str,code){return code[0]==="x"||code[0]==="X"?String.fromCharCode(parseInt(code.substr(1),16)):String.fromCharCode(parseInt(code,10))})}function escapeDangerHtml5Entities(str){return str.replace(REGEXP_ATTR_VALUE_COLON,":").replace(REGEXP_ATTR_VALUE_NEWLINE," ")}function clearNonPrintableCharacter(str){var str2="";for(var i=0,len=str.length;i/g;function stripBlankChar(html){var chars=html.split("");chars=chars.filter(function(char){var c=char.charCodeAt(0);if(c===127)return false;if(c<=31){if(c===10||c===13)return true;return false}return true});return chars.join("")}exports.whiteList=getDefaultWhiteList();exports.getDefaultWhiteList=getDefaultWhiteList;exports.onTag=onTag;exports.onIgnoreTag=onIgnoreTag;exports.onTagAttr=onTagAttr;exports.onIgnoreTagAttr=onIgnoreTagAttr;exports.safeAttrValue=safeAttrValue;exports.escapeHtml=escapeHtml;exports.escapeQuote=escapeQuote;exports.unescapeQuote=unescapeQuote;exports.escapeHtmlEntities=escapeHtmlEntities;exports.escapeDangerHtml5Entities=escapeDangerHtml5Entities;exports.clearNonPrintableCharacter=clearNonPrintableCharacter;exports.friendlyAttrValue=friendlyAttrValue;exports.escapeAttrValue=escapeAttrValue;exports.onIgnoreTagStripAll=onIgnoreTagStripAll;exports.StripTagBody=StripTagBody;exports.stripCommentTag=stripCommentTag;exports.stripBlankChar=stripBlankChar;exports.cssFilter=defaultCSSFilter},{"./util":4,cssfilter:8}],2:[function(require,module,exports){var DEFAULT=require("./default");var parser=require("./parser");var FilterXSS=require("./xss");function filterXSS(html,options){var xss=new FilterXSS(options);return xss.process(html)}exports=module.exports=filterXSS;exports.FilterXSS=FilterXSS;for(var i in DEFAULT)exports[i]=DEFAULT[i];for(var i in parser)exports[i]=parser[i];if(typeof window!=="undefined"){window.filterXSS=module.exports}},{"./default":1,"./parser":3,"./xss":5}],3:[function(require,module,exports){var _=require("./util");function getTagName(html){var i=html.indexOf(" ");if(i===-1){var tagName=html.slice(1,-1)}else{var tagName=html.slice(1,i+1)}tagName=_.trim(tagName).toLowerCase();if(tagName.slice(0,1)==="/")tagName=tagName.slice(1);if(tagName.slice(-1)==="/")tagName=tagName.slice(0,-1);return tagName}function isClosing(html){return html.slice(0,2)===""){rethtml+=escapeHtml(html.slice(lastPos,tagStart));currentHtml=html.slice(tagStart,currentPos+1);currentTagName=getTagName(currentHtml);rethtml+=onTag(tagStart,rethtml.length,currentTagName,currentHtml,isClosing(currentHtml));lastPos=currentPos+1;tagStart=false;continue}if((c==='"'||c==="'")&&html.charAt(currentPos-1)==="="){quoteStart=c;continue}}else{if(c===quoteStart){quoteStart=false;continue}}}}if(lastPos0;i--){var c=str[i];if(c===" ")continue;if(c==="=")return i;return-1}}function isQuoteWrapString(text){if(text[0]==='"'&&text[text.length-1]==='"'||text[0]==="'"&&text[text.length-1]==="'"){return true}else{return false}}function stripQuoteWrap(text){if(isQuoteWrapString(text)){return text.substr(1,text.length-2)}else{return text}}exports.parseTag=parseTag;exports.parseAttr=parseAttr},{"./util":4}],4:[function(require,module,exports){module.exports={indexOf:function(arr,item){var i,j;if(Array.prototype.indexOf){return arr.indexOf(item)}for(i=0,j=arr.length;i"}var attrs=getAttrs(html);var whiteAttrList=whiteList[tag];var attrsHtml=parseAttr(attrs.html,function(name,value){var isWhiteAttr=_.indexOf(whiteAttrList,name)!==-1;var ret=onTagAttr(tag,name,value,isWhiteAttr);if(!isNull(ret))return ret;if(isWhiteAttr){value=safeAttrValue(tag,name,value,cssFilter);if(value){return name+'="'+value+'"'}else{return name}}else{var ret=onIgnoreTagAttr(tag,name,value,isWhiteAttr);if(!isNull(ret))return ret;return}});var html="<"+tag;if(attrsHtml)html+=" "+attrsHtml;if(attrs.closing)html+=" /";html+=">";return html}else{var ret=onIgnoreTag(tag,html,info);if(!isNull(ret))return ret;return escapeHtml(html)}},escapeHtml);if(stripIgnoreTagBody){retHtml=stripIgnoreTagBody.remove(retHtml)}return retHtml};module.exports=FilterXSS},{"./default":1,"./parser":3,"./util":4,cssfilter:8}],6:[function(require,module,exports){var DEFAULT=require("./default");var parseStyle=require("./parser");var _=require("./util");function isNull(obj){return obj===undefined||obj===null}function FilterCSS(options){options=options||{};options.whiteList=options.whiteList||DEFAULT.whiteList;options.onAttr=options.onAttr||DEFAULT.onAttr;options.onIgnoreAttr=options.onIgnoreAttr||DEFAULT.onIgnoreAttr;this.options=options}FilterCSS.prototype.process=function(css){css=css||"";css=css.toString();if(!css)return"";var me=this;var options=me.options;var whiteList=options.whiteList;var onAttr=options.onAttr;var onIgnoreAttr=options.onIgnoreAttr;var retCSS=parseStyle(css,function(sourcePosition,position,name,value,source){var check=whiteList[name];var isWhite=false;if(check===true)isWhite=check;else if(typeof check==="function")isWhite=check(value);else if(check instanceof RegExp)isWhite=check.test(value);if(isWhite!==true)isWhite=false;var opts={position:position,sourcePosition:sourcePosition,source:source,isWhite:isWhite};if(isWhite){var ret=onAttr(name,value,opts);if(isNull(ret)){return name+":"+value}else{return ret}}else{var ret=onIgnoreAttr(name,value,opts);if(!isNull(ret)){return ret}}});return retCSS};module.exports=FilterCSS},{"./default":7,"./parser":9,"./util":10}],7:[function(require,module,exports){function getDefaultWhiteList(){var whiteList={};whiteList["align-content"]=false;whiteList["align-items"]=false;whiteList["align-self"]=false;whiteList["alignment-adjust"]=false;whiteList["alignment-baseline"]=false;whiteList["all"]=false;whiteList["anchor-point"]=false;whiteList["animation"]=false;whiteList["animation-delay"]=false;whiteList["animation-direction"]=false;whiteList["animation-duration"]=false;whiteList["animation-fill-mode"]=false;whiteList["animation-iteration-count"]=false;whiteList["animation-name"]=false;whiteList["animation-play-state"]=false;whiteList["animation-timing-function"]=false;whiteList["azimuth"]=false;whiteList["backface-visibility"]=false;whiteList["background"]=true;whiteList["background-attachment"]=true;whiteList["background-clip"]=true;whiteList["background-color"]=true;whiteList["background-image"]=true;whiteList["background-origin"]=true;whiteList["background-position"]=true;whiteList["background-repeat"]=true;whiteList["background-size"]=true;whiteList["baseline-shift"]=false;whiteList["binding"]=false;whiteList["bleed"]=false;whiteList["bookmark-label"]=false;whiteList["bookmark-level"]=false;whiteList["bookmark-state"]=false;whiteList["border"]=true;whiteList["border-bottom"]=true;whiteList["border-bottom-color"]=true;whiteList["border-bottom-left-radius"]=true;whiteList["border-bottom-right-radius"]=true;whiteList["border-bottom-style"]=true;whiteList["border-bottom-width"]=true;whiteList["border-collapse"]=true;whiteList["border-color"]=true;whiteList["border-image"]=true;whiteList["border-image-outset"]=true;whiteList["border-image-repeat"]=true;whiteList["border-image-slice"]=true;whiteList["border-image-source"]=true;whiteList["border-image-width"]=true;whiteList["border-left"]=true;whiteList["border-left-color"]=true;whiteList["border-left-style"]=true;whiteList["border-left-width"]=true;whiteList["border-radius"]=true;whiteList["border-right"]=true;whiteList["border-right-color"]=true;whiteList["border-right-style"]=true;whiteList["border-right-width"]=true;whiteList["border-spacing"]=true;whiteList["border-style"]=true;whiteList["border-top"]=true;whiteList["border-top-color"]=true;whiteList["border-top-left-radius"]=true;whiteList["border-top-right-radius"]=true;whiteList["border-top-style"]=true;whiteList["border-top-width"]=true;whiteList["border-width"]=true;whiteList["bottom"]=false;whiteList["box-decoration-break"]=true;whiteList["box-shadow"]=true;whiteList["box-sizing"]=true;whiteList["box-snap"]=true;whiteList["box-suppress"]=true;whiteList["break-after"]=true;whiteList["break-before"]=true;whiteList["break-inside"]=true;whiteList["caption-side"]=false;whiteList["chains"]=false;whiteList["clear"]=true;whiteList["clip"]=false;whiteList["clip-path"]=false;whiteList["clip-rule"]=false;whiteList["color"]=true;whiteList["color-interpolation-filters"]=true;whiteList["column-count"]=false;whiteList["column-fill"]=false;whiteList["column-gap"]=false;whiteList["column-rule"]=false;whiteList["column-rule-color"]=false;whiteList["column-rule-style"]=false;whiteList["column-rule-width"]=false;whiteList["column-span"]=false;whiteList["column-width"]=false;whiteList["columns"]=false;whiteList["contain"]=false;whiteList["content"]=false;whiteList["counter-increment"]=false;whiteList["counter-reset"]=false;whiteList["counter-set"]=false;whiteList["crop"]=false;whiteList["cue"]=false;whiteList["cue-after"]=false;whiteList["cue-before"]=false;whiteList["cursor"]=false;whiteList["direction"]=false;whiteList["display"]=true;whiteList["display-inside"]=true;whiteList["display-list"]=true;whiteList["display-outside"]=true;whiteList["dominant-baseline"]=false;whiteList["elevation"]=false;whiteList["empty-cells"]=false;whiteList["filter"]=false;whiteList["flex"]=false;whiteList["flex-basis"]=false;whiteList["flex-direction"]=false;whiteList["flex-flow"]=false;whiteList["flex-grow"]=false;whiteList["flex-shrink"]=false;whiteList["flex-wrap"]=false;whiteList["float"]=false;whiteList["float-offset"]=false;whiteList["flood-color"]=false;whiteList["flood-opacity"]=false;whiteList["flow-from"]=false;whiteList["flow-into"]=false;whiteList["font"]=true;whiteList["font-family"]=true;whiteList["font-feature-settings"]=true;whiteList["font-kerning"]=true;whiteList["font-language-override"]=true;whiteList["font-size"]=true;whiteList["font-size-adjust"]=true;whiteList["font-stretch"]=true;whiteList["font-style"]=true;whiteList["font-synthesis"]=true;whiteList["font-variant"]=true;whiteList["font-variant-alternates"]=true;whiteList["font-variant-caps"]=true;whiteList["font-variant-east-asian"]=true;whiteList["font-variant-ligatures"]=true;whiteList["font-variant-numeric"]=true;whiteList["font-variant-position"]=true;whiteList["font-weight"]=true;whiteList["grid"]=false;whiteList["grid-area"]=false;whiteList["grid-auto-columns"]=false;whiteList["grid-auto-flow"]=false;whiteList["grid-auto-rows"]=false;whiteList["grid-column"]=false;whiteList["grid-column-end"]=false;whiteList["grid-column-start"]=false;whiteList["grid-row"]=false;whiteList["grid-row-end"]=false;whiteList["grid-row-start"]=false;whiteList["grid-template"]=false;whiteList["grid-template-areas"]=false;whiteList["grid-template-columns"]=false;whiteList["grid-template-rows"]=false;whiteList["hanging-punctuation"]=false;whiteList["height"]=true;whiteList["hyphens"]=false;whiteList["icon"]=false;whiteList["image-orientation"]=false;whiteList["image-resolution"]=false;whiteList["ime-mode"]=false;whiteList["initial-letters"]=false;whiteList["inline-box-align"]=false;whiteList["justify-content"]=false;whiteList["justify-items"]=false;whiteList["justify-self"]=false;whiteList["left"]=false;whiteList["letter-spacing"]=true;whiteList["lighting-color"]=true;whiteList["line-box-contain"]=false;whiteList["line-break"]=false;whiteList["line-grid"]=false;whiteList["line-height"]=false;whiteList["line-snap"]=false;whiteList["line-stacking"]=false;whiteList["line-stacking-ruby"]=false;whiteList["line-stacking-shift"]=false;whiteList["line-stacking-strategy"]=false;whiteList["list-style"]=true;whiteList["list-style-image"]=true;whiteList["list-style-position"]=true;whiteList["list-style-type"]=true;whiteList["margin"]=true;whiteList["margin-bottom"]=true;whiteList["margin-left"]=true;whiteList["margin-right"]=true;whiteList["margin-top"]=true;whiteList["marker-offset"]=false;whiteList["marker-side"]=false;whiteList["marks"]=false;whiteList["mask"]=false;whiteList["mask-box"]=false;whiteList["mask-box-outset"]=false;whiteList["mask-box-repeat"]=false;whiteList["mask-box-slice"]=false;whiteList["mask-box-source"]=false;whiteList["mask-box-width"]=false;whiteList["mask-clip"]=false;whiteList["mask-image"]=false;whiteList["mask-origin"]=false;whiteList["mask-position"]=false;whiteList["mask-repeat"]=false;whiteList["mask-size"]=false;whiteList["mask-source-type"]=false;whiteList["mask-type"]=false;whiteList["max-height"]=true;whiteList["max-lines"]=false;whiteList["max-width"]=true;whiteList["min-height"]=true;whiteList["min-width"]=true;whiteList["move-to"]=false;whiteList["nav-down"]=false;whiteList["nav-index"]=false;whiteList["nav-left"]=false;whiteList["nav-right"]=false;whiteList["nav-up"]=false;whiteList["object-fit"]=false;whiteList["object-position"]=false;whiteList["opacity"]=false;whiteList["order"]=false;whiteList["orphans"]=false;whiteList["outline"]=false;whiteList["outline-color"]=false;whiteList["outline-offset"]=false;whiteList["outline-style"]=false;whiteList["outline-width"]=false;whiteList["overflow"]=false;whiteList["overflow-wrap"]=false;whiteList["overflow-x"]=false;whiteList["overflow-y"]=false;whiteList["padding"]=true;whiteList["padding-bottom"]=true;whiteList["padding-left"]=true;whiteList["padding-right"]=true;whiteList["padding-top"]=true;whiteList["page"]=false;whiteList["page-break-after"]=false;whiteList["page-break-before"]=false;whiteList["page-break-inside"]=false;whiteList["page-policy"]=false;whiteList["pause"]=false;whiteList["pause-after"]=false;whiteList["pause-before"]=false;whiteList["perspective"]=false;whiteList["perspective-origin"]=false;whiteList["pitch"]=false;whiteList["pitch-range"]=false;whiteList["play-during"]=false;whiteList["position"]=false;whiteList["presentation-level"]=false;whiteList["quotes"]=false;whiteList["region-fragment"]=false;whiteList["resize"]=false;whiteList["rest"]=false;whiteList["rest-after"]=false;whiteList["rest-before"]=false;whiteList["richness"]=false;whiteList["right"]=false;whiteList["rotation"]=false;whiteList["rotation-point"]=false;whiteList["ruby-align"]=false;whiteList["ruby-merge"]=false;whiteList["ruby-position"]=false;whiteList["shape-image-threshold"]=false;whiteList["shape-outside"]=false;whiteList["shape-margin"]=false;whiteList["size"]=false;whiteList["speak"]=false;whiteList["speak-as"]=false;whiteList["speak-header"]=false;whiteList["speak-numeral"]=false;whiteList["speak-punctuation"]=false;whiteList["speech-rate"]=false;whiteList["stress"]=false;whiteList["string-set"]=false;whiteList["tab-size"]=false;whiteList["table-layout"]=false;whiteList["text-align"]=true;whiteList["text-align-last"]=true;whiteList["text-combine-upright"]=true;whiteList["text-decoration"]=true;whiteList["text-decoration-color"]=true;whiteList["text-decoration-line"]=true;whiteList["text-decoration-skip"]=true;whiteList["text-decoration-style"]=true;whiteList["text-emphasis"]=true;whiteList["text-emphasis-color"]=true;whiteList["text-emphasis-position"]=true;whiteList["text-emphasis-style"]=true;whiteList["text-height"]=true;whiteList["text-indent"]=true;whiteList["text-justify"]=true;whiteList["text-orientation"]=true;whiteList["text-overflow"]=true;whiteList["text-shadow"]=true;whiteList["text-space-collapse"]=true;whiteList["text-transform"]=true;whiteList["text-underline-position"]=true;whiteList["text-wrap"]=true;whiteList["top"]=false;whiteList["transform"]=false;whiteList["transform-origin"]=false;whiteList["transform-style"]=false;whiteList["transition"]=false;whiteList["transition-delay"]=false;whiteList["transition-duration"]=false;whiteList["transition-property"]=false;whiteList["transition-timing-function"]=false;whiteList["unicode-bidi"]=false;whiteList["vertical-align"]=false;whiteList["visibility"]=false;whiteList["voice-balance"]=false;whiteList["voice-duration"]=false;whiteList["voice-family"]=false;whiteList["voice-pitch"]=false;whiteList["voice-range"]=false;whiteList["voice-rate"]=false;whiteList["voice-stress"]=false;whiteList["voice-volume"]=false;whiteList["volume"]=false;whiteList["white-space"]=false;whiteList["widows"]=false;whiteList["width"]=true;whiteList["will-change"]=false;whiteList["word-break"]=true;whiteList["word-spacing"]=true;whiteList["word-wrap"]=true;whiteList["wrap-flow"]=false;whiteList["wrap-through"]=false;whiteList["writing-mode"]=false;whiteList["z-index"]=false;return whiteList}function onAttr(name,value,options){}function onIgnoreAttr(name,value,options){}exports.whiteList=getDefaultWhiteList();exports.getDefaultWhiteList=getDefaultWhiteList;exports.onAttr=onAttr;exports.onIgnoreAttr=onIgnoreAttr},{}],8:[function(require,module,exports){var DEFAULT=require("./default");var FilterCSS=require("./css");function filterCSS(html,options){var xss=new FilterCSS(options);return xss.process(html)}exports=module.exports=filterCSS;exports.FilterCSS=FilterCSS;for(var i in DEFAULT)exports[i]=DEFAULT[i];if(typeof window!=="undefined"){window.filterCSS=module.exports}},{"./css":6,"./default":7}],9:[function(require,module,exports){var _=require("./util");function parseStyle(css,onAttr){css=_.trimRight(css);if(css[css.length-1]!==";")css+=";";var cssLength=css.length;var isParenthesisOpen=false;var lastPos=0;var i=0;var retCSS="";function addNewAttr(){if(!isParenthesisOpen){var source=_.trim(css.slice(lastPos,i));var j=source.indexOf(":");if(j!==-1){var name=_.trim(source.slice(0,j));var value=_.trim(source.slice(j+1));if(name){var ret=onAttr(lastPos,retCSS.length,name,value,source);if(ret)retCSS+=ret+"; "}}}lastPos=i+1}for(;i