From bba0fd0654880a5d2abc54cf2b6a7f9d6a62e9b5 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Mon, 4 Jul 2016 14:15:51 -0400 Subject: [PATCH] REFACTOR: PreloadStore to ES6 --- .eslintignore | 1 - .eslintrc | 1 - .../javascripts/admin/models/backup.js.es6 | 2 + .../admin/routes/admin-backups-logs.js.es6 | 2 + .../admin/routes/admin-backups.js.es6 | 1 + .../discourse/adapters/topic-list.js.es6 | 1 + .../discourse/helpers/custom-html.js.es6 | 1 + .../discourse/initializers/banner.js.es6 | 2 + .../initializers/enable-emoji.js.es6 | 1 + .../initializers/localization.js.es6 | 2 + .../discourse/lib/raw-handlebars.js.es6 | 41 +++++---- .../discourse/models/category-list.js.es6 | 1 + .../javascripts/discourse/models/site.js.es6 | 1 + .../models/topic-tracking-state.js.es6 | 1 + .../javascripts/discourse/models/topic.js.es6 | 1 + .../javascripts/discourse/models/user.js.es6 | 1 + .../discourse/routes/badges-index.js.es6 | 1 + .../discourse/routes/badges-show.js.es6 | 1 + .../routes/discovery-categories.js.es6 | 1 + .../discourse/routes/full-page-search.js.es6 | 1 + app/assets/javascripts/preload-store.js.es6 | 54 +++++++++++ app/assets/javascripts/preload_store.js | 90 ------------------- .../javascripts/pretty-text/sanitizer.js.es6 | 3 +- .../common/_discourse_javascript.html.erb | 36 ++++---- app/views/layouts/application.html.erb | 11 ++- config/application.rb | 2 +- lib/freedom_patches/raw_handlebars.rb | 2 +- test/javascripts/helpers/current_user.js | 1 - .../helpers/custom-html-test.js.es6 | 3 +- .../helpers/{site.js => site.js.es6} | 2 + .../javascripts/lib/preload-store-test.js.es6 | 5 +- test/javascripts/test_helper.js | 16 +++- vendor/assets/javascripts/xss.min.js | 1 + 33 files changed, 149 insertions(+), 141 deletions(-) create mode 100644 app/assets/javascripts/preload-store.js.es6 delete mode 100644 app/assets/javascripts/preload_store.js delete mode 100644 test/javascripts/helpers/current_user.js rename test/javascripts/helpers/{site.js => site.js.es6} (99%) create mode 100644 vendor/assets/javascripts/xss.min.js diff --git a/.eslintignore b/.eslintignore index 7e1a7cd5a..644318acf 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,7 +1,6 @@ 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 diff --git a/.eslintrc b/.eslintrc index 2b3ed3ba7..b529fa206 100644 --- a/.eslintrc +++ b/.eslintrc @@ -14,7 +14,6 @@ "RSVP":true, "Discourse":true, "Em":true, - "PreloadStore":true, "Handlebars":true, "I18n":true, "bootbox":true, diff --git a/app/assets/javascripts/admin/models/backup.js.es6 b/app/assets/javascripts/admin/models/backup.js.es6 index c38f74ec8..d7baceeba 100644 --- a/app/assets/javascripts/admin/models/backup.js.es6 +++ b/app/assets/javascripts/admin/models/backup.js.es6 @@ -1,4 +1,6 @@ import { ajax } from 'discourse/lib/ajax'; +import PreloadStore from 'preload-store'; + const Backup = Discourse.Model.extend({ destroy() { 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 8a6d32fdd..cd01295f5 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 1a944314e..d59001943 100644 --- a/app/assets/javascripts/admin/routes/admin-backups.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-backups.js.es6 @@ -2,6 +2,7 @@ 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"; diff --git a/app/assets/javascripts/discourse/adapters/topic-list.js.es6 b/app/assets/javascripts/discourse/adapters/topic-list.js.es6 index c0311c011..986c23e9c 100644 --- a/app/assets/javascripts/discourse/adapters/topic-list.js.es6 +++ b/app/assets/javascripts/discourse/adapters/topic-list.js.es6 @@ -1,5 +1,6 @@ import { ajax } from 'discourse/lib/ajax'; import RestAdapter from 'discourse/adapters/rest'; +import PreloadStore from 'preload-store'; export function finderFor(filter, params) { return function() { diff --git a/app/assets/javascripts/discourse/helpers/custom-html.js.es6 b/app/assets/javascripts/discourse/helpers/custom-html.js.es6 index ef11f294a..8dc3be9c3 100644 --- a/app/assets/javascripts/discourse/helpers/custom-html.js.es6 +++ b/app/assets/javascripts/discourse/helpers/custom-html.js.es6 @@ -1,4 +1,5 @@ import { registerHelper } from 'discourse/lib/helpers'; +import PreloadStore from 'preload-store'; const _customizations = {}; diff --git a/app/assets/javascripts/discourse/initializers/banner.js.es6 b/app/assets/javascripts/discourse/initializers/banner.js.es6 index f8bf99e55..9c9ff47d9 100644 --- a/app/assets/javascripts/discourse/initializers/banner.js.es6 +++ b/app/assets/javascripts/discourse/initializers/banner.js.es6 @@ -1,3 +1,5 @@ +import PreloadStore from 'preload-store'; + export default { name: "banner", after: "message-bus", diff --git a/app/assets/javascripts/discourse/initializers/enable-emoji.js.es6 b/app/assets/javascripts/discourse/initializers/enable-emoji.js.es6 index a3210677b..f66c16cff 100644 --- a/app/assets/javascripts/discourse/initializers/enable-emoji.js.es6 +++ b/app/assets/javascripts/discourse/initializers/enable-emoji.js.es6 @@ -1,5 +1,6 @@ import { withPluginApi } from 'discourse/lib/plugin-api'; import { registerEmoji } from 'pretty-text/emoji'; +import PreloadStore from 'preload-store'; export default { name: 'enable-emoji', diff --git a/app/assets/javascripts/discourse/initializers/localization.js.es6 b/app/assets/javascripts/discourse/initializers/localization.js.es6 index 2945b5b17..6477c5e4f 100644 --- a/app/assets/javascripts/discourse/initializers/localization.js.es6 +++ b/app/assets/javascripts/discourse/initializers/localization.js.es6 @@ -1,3 +1,5 @@ +import PreloadStore from 'preload-store'; + export default { name: 'localization', after: 'inject-objects', diff --git a/app/assets/javascripts/discourse/lib/raw-handlebars.js.es6 b/app/assets/javascripts/discourse/lib/raw-handlebars.js.es6 index 3e17eafc5..e33c91200 100644 --- a/app/assets/javascripts/discourse/lib/raw-handlebars.js.es6 +++ b/app/assets/javascripts/discourse/lib/raw-handlebars.js.es6 @@ -133,32 +133,37 @@ if (Handlebars.Compiler) { var environment = new RawHandlebars.Compiler().compile(ast, options); var templateSpec = new RawHandlebars.JavaScriptCompiler().compile(environment, options, undefined, true); - var template = RawHandlebars.template(templateSpec); - template.isMethod = false; + var t = RawHandlebars.template(templateSpec); + t.isMethod = false; - return template; - }; - - RawHandlebars.get = function(ctx, property, options) { - if (options.types && options.data.view) { - var view = options.data.view; - return view.getStream ? view.getStream(property).value() : view.getAttr(property); - } else { - return Ember.get(ctx, property); - } + return t; }; } -export function precompile(value, asObject) { - return RawHandlebars.precompile(value, asObject); + +RawHandlebars.get = function(ctx, property, options) { + if (options.types && options.data.view) { + var view = options.data.view; + return view.getStream ? view.getStream(property).value() : view.getAttr(property); + } else { + return Ember.get(ctx, property); + } +}; + +export function template() { + return RawHandlebars.template.apply(this, arguments); } -export function compile(string) { - return RawHandlebars.compile(string); +export function precompile() { + return RawHandlebars.precompile.apply(this, arguments); } -export function get(ctx, property, options) { - return RawHandlebars.get(ctx, property, options); +export function compile() { + return RawHandlebars.compile.apply(this, arguments); +} + +export function get() { + return RawHandlebars.get.apply(this, arguments); } export default RawHandlebars; diff --git a/app/assets/javascripts/discourse/models/category-list.js.es6 b/app/assets/javascripts/discourse/models/category-list.js.es6 index b0d6c2c45..c1469c314 100644 --- a/app/assets/javascripts/discourse/models/category-list.js.es6 +++ b/app/assets/javascripts/discourse/models/category-list.js.es6 @@ -1,3 +1,4 @@ +import PreloadStore from 'preload-store'; import { ajax } from 'discourse/lib/ajax'; const CategoryList = Ember.ArrayProxy.extend({ diff --git a/app/assets/javascripts/discourse/models/site.js.es6 b/app/assets/javascripts/discourse/models/site.js.es6 index 37c129904..eb417c742 100644 --- a/app/assets/javascripts/discourse/models/site.js.es6 +++ b/app/assets/javascripts/discourse/models/site.js.es6 @@ -3,6 +3,7 @@ import Archetype from 'discourse/models/archetype'; import PostActionType from 'discourse/models/post-action-type'; import Singleton from 'discourse/mixins/singleton'; import RestModel from 'discourse/models/rest'; +import PreloadStore from 'preload-store'; const Site = RestModel.extend({ diff --git a/app/assets/javascripts/discourse/models/topic-tracking-state.js.es6 b/app/assets/javascripts/discourse/models/topic-tracking-state.js.es6 index dfdfaf12a..d07825a6a 100644 --- a/app/assets/javascripts/discourse/models/topic-tracking-state.js.es6 +++ b/app/assets/javascripts/discourse/models/topic-tracking-state.js.es6 @@ -2,6 +2,7 @@ import { NotificationLevels } from 'discourse/lib/notification-levels'; import computed from "ember-addons/ember-computed-decorators"; import { on } from "ember-addons/ember-computed-decorators"; import { defaultHomepage } from 'discourse/lib/utilities'; +import PreloadStore from 'preload-store'; function isNew(topic) { return topic.last_read_post_number === null && diff --git a/app/assets/javascripts/discourse/models/topic.js.es6 b/app/assets/javascripts/discourse/models/topic.js.es6 index 5054634ca..712a1b369 100644 --- a/app/assets/javascripts/discourse/models/topic.js.es6 +++ b/app/assets/javascripts/discourse/models/topic.js.es6 @@ -8,6 +8,7 @@ import ActionSummary from 'discourse/models/action-summary'; import { popupAjaxError } from 'discourse/lib/ajax-error'; import { censor } from 'pretty-text/censored-words'; import { emojiUnescape } from 'discourse/lib/text'; +import PreloadStore from 'preload-store'; export function loadTopicView(topic, args) { const topicId = topic.get('id'); diff --git a/app/assets/javascripts/discourse/models/user.js.es6 b/app/assets/javascripts/discourse/models/user.js.es6 index f35d4b11f..a333ded29 100644 --- a/app/assets/javascripts/discourse/models/user.js.es6 +++ b/app/assets/javascripts/discourse/models/user.js.es6 @@ -13,6 +13,7 @@ import UserAction from 'discourse/models/user-action'; import Group from 'discourse/models/group'; import Topic from 'discourse/models/topic'; import { emojiUnescape } from 'discourse/lib/text'; +import PreloadStore from 'preload-store'; const User = RestModel.extend({ diff --git a/app/assets/javascripts/discourse/routes/badges-index.js.es6 b/app/assets/javascripts/discourse/routes/badges-index.js.es6 index 683033faa..eaaa5f879 100644 --- a/app/assets/javascripts/discourse/routes/badges-index.js.es6 +++ b/app/assets/javascripts/discourse/routes/badges-index.js.es6 @@ -1,4 +1,5 @@ import Badge from 'discourse/models/badge'; +import PreloadStore from 'preload-store'; export default Discourse.Route.extend({ model() { diff --git a/app/assets/javascripts/discourse/routes/badges-show.js.es6 b/app/assets/javascripts/discourse/routes/badges-show.js.es6 index 0592d54c6..354559852 100644 --- a/app/assets/javascripts/discourse/routes/badges-show.js.es6 +++ b/app/assets/javascripts/discourse/routes/badges-show.js.es6 @@ -1,5 +1,6 @@ import UserBadge from 'discourse/models/user-badge'; import Badge from 'discourse/models/badge'; +import PreloadStore from 'preload-store'; export default Discourse.Route.extend({ queryParams: { diff --git a/app/assets/javascripts/discourse/routes/discovery-categories.js.es6 b/app/assets/javascripts/discourse/routes/discovery-categories.js.es6 index d8769c29d..4aa3e17a5 100644 --- a/app/assets/javascripts/discourse/routes/discovery-categories.js.es6 +++ b/app/assets/javascripts/discourse/routes/discovery-categories.js.es6 @@ -2,6 +2,7 @@ import showModal from "discourse/lib/show-modal"; import OpenComposer from "discourse/mixins/open-composer"; import CategoryList from "discourse/models/category-list"; import { defaultHomepage } from 'discourse/lib/utilities'; +import PreloadStore from 'preload-store'; const DiscoveryCategoriesRoute = Discourse.Route.extend(OpenComposer, { renderTemplate() { diff --git a/app/assets/javascripts/discourse/routes/full-page-search.js.es6 b/app/assets/javascripts/discourse/routes/full-page-search.js.es6 index b59580b8b..8507c0dce 100644 --- a/app/assets/javascripts/discourse/routes/full-page-search.js.es6 +++ b/app/assets/javascripts/discourse/routes/full-page-search.js.es6 @@ -1,6 +1,7 @@ import { ajax } from 'discourse/lib/ajax'; import { translateResults, getSearchKey, isValidSearchTerm } from "discourse/lib/search"; import Composer from 'discourse/models/composer'; +import PreloadStore from 'preload-store'; export default Discourse.Route.extend({ queryParams: { q: {}, context_id: {}, context: {}, skip_context: {} }, diff --git a/app/assets/javascripts/preload-store.js.es6 b/app/assets/javascripts/preload-store.js.es6 new file mode 100644 index 000000000..1a7b96d6f --- /dev/null +++ b/app/assets/javascripts/preload-store.js.es6 @@ -0,0 +1,54 @@ +/** + We can insert data into the PreloadStore when the document is loaded. + The data can be accessed once by a key, after which it is removed + + @class PreloadStore +**/ +export default { + data: {}, + + store(key, value) { + this.data[key] = value; + }, + + /** + To retrieve a key, you provide the key you want, plus a finder to load + it if the key cannot be found. Once the key is used once, it is removed + from the store. + So, for example, you can't load a preloaded topic more than once. + **/ + getAndRemove(key, finder) { + if (this.data[key]) { + var promise = Em.RSVP.resolve(this.data[key]); + delete this.data[key]; + return promise; + } + + if (finder) { + return new Ember.RSVP.Promise(function(resolve, reject) { + var result = finder(); + + // If the finder returns a promise, we support that too + if (result && result.then) { + result.then(toResolve => resolve(toResolve)).catch(toReject => reject(toReject)); + } else { + resolve(result); + } + }); + } + + return Ember.RSVP.resolve(null); + }, + + get(key) { + return this.data[key]; + }, + + remove(key) { + if (this.data[key]) delete this.data[key]; + }, + + reset() { + this.data = {}; + } +}; diff --git a/app/assets/javascripts/preload_store.js b/app/assets/javascripts/preload_store.js deleted file mode 100644 index 4911d4e54..000000000 --- a/app/assets/javascripts/preload_store.js +++ /dev/null @@ -1,90 +0,0 @@ - -/** - We can insert data into the PreloadStore when the document is loaded. - The data can be accessed once by a key, after which it is removed - - @class PreloadStore -**/ -window.PreloadStore = { - data: {}, - - /** - Store an object in the store - - @method store - @param {String} key the key to store the object with - @param {String} value the object we're inserting into the store - **/ - store: function(key, value) { - this.data[key] = value; - }, - - /** - To retrieve a key, you provide the key you want, plus a finder to load - it if the key cannot be found. Once the key is used once, it is removed - from the store. - So, for example, you can't load a preloaded topic more than once. - - @method getAndRemove - @param {String} key the key to look up the object with - @param {function} finder a function to find the object with - @returns {Promise} a promise that will eventually be the object we want. - **/ - getAndRemove: function(key, finder) { - if (this.data[key]) { - var promise = Em.RSVP.resolve(this.data[key]); - delete this.data[key]; - return promise; - } - - if (finder) { - return new Ember.RSVP.Promise(function(resolve, reject) { - var result = finder(); - - // If the finder returns a promise, we support that too - if (result && result.then) { - result.then(function(result) { - return resolve(result); - }, function(result) { - return reject(result); - }); - } else { - resolve(result); - } - }); - } - - return Ember.RSVP.resolve(null); - }, - - /** - If we are sure it's preloaded, we don't have to supply a finder. - Just returns undefined if it's not in the store. - - @method get - @param {String} key the key to look up the object with - @returns {Object} the object from the store - **/ - "get": function(key) { - return this.data[key]; - }, - - /** - Removes the stored value if the key exists - - @method remove - @param {String} key the key to remove - **/ - remove: function(key) { - if (this.data[key]) delete this.data[key]; - }, - - /** - Resets the contents of the store. Used in testing. - - **/ - reset: function() { - this.data = {}; - } - -}; diff --git a/app/assets/javascripts/pretty-text/sanitizer.js.es6 b/app/assets/javascripts/pretty-text/sanitizer.js.es6 index 487793197..9881eceef 100644 --- a/app/assets/javascripts/pretty-text/sanitizer.js.es6 +++ b/app/assets/javascripts/pretty-text/sanitizer.js.es6 @@ -60,13 +60,12 @@ export function sanitize(text, whiteLister) { const whiteList = whiteLister.getWhiteList(); - let hadIframe = false; let result = xss(text, { whiteList: whiteList.tagList, stripIgnoreTag: true, stripIgnoreTagBody: ['script', 'table'], + onIgnoreTagAttr(tag, name, value) { - hadIframe = hadIframe || tag === 'iframe'; const forTag = whiteList.attrList[tag]; if (forTag) { const forAttr = forTag[name]; diff --git a/app/views/common/_discourse_javascript.html.erb b/app/views/common/_discourse_javascript.html.erb index 14f7a2cb3..c337c56a8 100644 --- a/app/views/common/_discourse_javascript.html.erb +++ b/app/views/common/_discourse_javascript.html.erb @@ -39,23 +39,27 @@ }); <%- end %> - Discourse.CDN = '<%= Rails.configuration.action_controller.asset_host %>'; - Discourse.BaseUrl = '<%= RailsMultisite::ConnectionManagement.current_hostname %>'.replace(/:[\d]*$/,""); - Discourse.BaseUri = '<%= Discourse::base_uri %>'; - Discourse.Environment = '<%= Rails.env %>'; - Discourse.SiteSettings = PreloadStore.get('siteSettings'); - Discourse.LetterAvatarVersion = '<%= LetterAvatar.version %>'; - I18n.defaultLocale = '<%= SiteSetting.default_locale %>'; - Discourse.start(); - Discourse.set('assetVersion','<%= Discourse.assets_digest %>'); - Discourse.Session.currentProp("disableCustomCSS", <%= loading_admin? %>); - Discourse.HighlightJSPath = <%= HighlightJs.path.inspect.html_safe %>; - <%- if SiteSetting.enable_s3_uploads %> - <%- if SiteSetting.s3_cdn_url.present? %> - Discourse.S3CDN = '<%= SiteSetting.s3_cdn_url %>'; + (function() { + var ps = require('preload-store').default; + + Discourse.CDN = '<%= Rails.configuration.action_controller.asset_host %>'; + Discourse.BaseUrl = '<%= RailsMultisite::ConnectionManagement.current_hostname %>'.replace(/:[\d]*$/,""); + Discourse.BaseUri = '<%= Discourse::base_uri %>'; + Discourse.Environment = '<%= Rails.env %>'; + Discourse.SiteSettings = ps.get('siteSettings'); + Discourse.LetterAvatarVersion = '<%= LetterAvatar.version %>'; + I18n.defaultLocale = '<%= SiteSetting.default_locale %>'; + Discourse.start(); + Discourse.set('assetVersion','<%= Discourse.assets_digest %>'); + Discourse.Session.currentProp("disableCustomCSS", <%= loading_admin? %>); + Discourse.HighlightJSPath = <%= HighlightJs.path.inspect.html_safe %>; + <%- if SiteSetting.enable_s3_uploads %> + <%- if SiteSetting.s3_cdn_url.present? %> + Discourse.S3CDN = '<%= SiteSetting.s3_cdn_url %>'; + <%- end %> + Discourse.S3BaseUrl = '<%= Discourse.store.absolute_base_url %>'; <%- end %> - Discourse.S3BaseUrl = '<%= Discourse.store.absolute_base_url %>'; - <%- end %> + })(); <%= script 'browser-update' %> diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index cac200b7d..3b8b4ccc6 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -22,9 +22,9 @@ window.EmberENV['FORCE_JQUERY'] = true; - <%= script "preload_store" %> <%= script "locales/#{I18n.locale}" %> <%= script "ember_jquery" %> + <%= script "preload-store" %> <%= script "vendor" %> <%= script "pretty-text-bundle" %> <%= script "application" %> @@ -89,9 +89,12 @@ <%- if @preloaded.present? %> <%- end %> diff --git a/config/application.rb b/config/application.rb index bc909d41f..733323342 100644 --- a/config/application.rb +++ b/config/application.rb @@ -70,7 +70,7 @@ module Discourse end] config.assets.precompile += ['vendor.js', 'common.css', 'desktop.css', 'mobile.css', - 'admin.js', 'admin.css', 'shiny/shiny.css', 'preload_store.js', + 'admin.js', 'admin.css', 'shiny/shiny.css', 'preload-store.js.es6', 'browser-update.js', 'embed.css', 'break_string.js', 'ember_jquery.js', 'pretty-text-bundle.js'] diff --git a/lib/freedom_patches/raw_handlebars.rb b/lib/freedom_patches/raw_handlebars.rb index 06a709f04..28f04e314 100644 --- a/lib/freedom_patches/raw_handlebars.rb +++ b/lib/freedom_patches/raw_handlebars.rb @@ -40,7 +40,7 @@ module Discourse module Handlebars module Helper def precompile_handlebars(string) - "Discourse.EmberCompatHandlebars.template(#{Barber::Precompiler.compile(string)});" + "require('discourse/lib/raw-handlebars').template(#{Barber::Precompiler.compile(string)});" end def compile_handlebars(string) diff --git a/test/javascripts/helpers/current_user.js b/test/javascripts/helpers/current_user.js deleted file mode 100644 index 924915e54..000000000 --- a/test/javascripts/helpers/current_user.js +++ /dev/null @@ -1 +0,0 @@ -PreloadStore.store("currentUser", {"id":42,"username":"eviltrout","avatar_template":"//www.gravatar.com/avatar/c6e17f2ae2a215e87ff9e878a4e63cd9.png?s={size}&r=pg&d=identicon","name":"Evil Trout","unread_notifications":0,"unread_private_messages":0,"admin":false,"notification_channel_position":null,"site_flagged_posts_count":0,"moderator":false,"staff":false,"reply_count":0,"topic_count":0,"enable_quoting":true,"external_links_in_new_tab":false,"dynamic_favicon":false,"trust_level":0,"can_edit":true}); diff --git a/test/javascripts/helpers/custom-html-test.js.es6 b/test/javascripts/helpers/custom-html-test.js.es6 index 1f2526d54..4c7f765e6 100644 --- a/test/javascripts/helpers/custom-html-test.js.es6 +++ b/test/javascripts/helpers/custom-html-test.js.es6 @@ -1,4 +1,6 @@ import { blank } from 'helpers/qunit-helpers'; +import PreloadStore from 'preload-store'; + module("helper:custom-html"); import { getCustomHTML, setCustomHTML } from 'discourse/helpers/custom-html'; @@ -11,5 +13,4 @@ test("customHTML", function() { PreloadStore.store('customHTML', {cookie: 'monster'}); equal(getCustomHTML('cookie'), 'monster', 'it returns HTML fragments from the PreloadStore'); - }); diff --git a/test/javascripts/helpers/site.js b/test/javascripts/helpers/site.js.es6 similarity index 99% rename from test/javascripts/helpers/site.js rename to test/javascripts/helpers/site.js.es6 index c606a3b78..d8ab85a48 100644 --- a/test/javascripts/helpers/site.js +++ b/test/javascripts/helpers/site.js.es6 @@ -1,2 +1,4 @@ +import PreloadStore from 'preload-store'; + /*jshint maxlen:10000000 */ PreloadStore.store("site",{"default_archetype":"regular","notification_types":{"mentioned":1,"replied":2,"quoted":3,"edited":4,"liked":5,"private_message":6,"invited_to_private_message":7,"invitee_accepted":8,"posted":9,"moved_post":10},"post_types":{"regular":1,"moderator_action":2},"groups":[{"id":0,"name":"everyone"},{"id":1,"name":"admins"},{"id":2,"name":"moderators"},{"id":3,"name":"staff"},{"id":10,"name":"trust_level_0"},{"id":11,"name":"trust_level_1"},{"id":12,"name":"trust_level_2"},{"id":13,"name":"trust_level_3"},{"id":14,"name":"trust_level_4"},{"id":20,"name":"ubuntu"},{"id":21,"name":"test"}],"filters":["latest","unread","new","starred","read","posted"],"periods":["yearly","monthly","weekly","daily"],"top_menu_items":["latest","unread","new","starred","read","posted","category","categories","top"],"anonymous_top_menu_items":["latest","category","categories","top"],"uncategorized_category_id":17,"categories":[{"id":5,"name":"extensibility","color":"FE8432","text_color":"FFFFFF","slug":"extensibility","topic_count":102,"description":"Topics about extending the functionality of Discourse with plugins, themes, add-ons, or other mechanisms for extensibility. ","topic_url":"/t/category-definition-for-extensibility/28","hotness":5.0,"read_restricted":false,"permission":null},{"id":7,"name":"dev","color":"000","text_color":"FFFFFF","slug":"dev","topic_count":284,"description":"This category is for topics related to hacking on Discourse: submitting pull requests, configuring development environments, coding conventions, and so forth.","topic_url":"/t/category-definition-for-dev/1026","hotness":5.0,"read_restricted":false,"permission":null},{"id":1,"name":"bug","color":"e9dd00","text_color":"000000","slug":"bug","topic_count":660,"description":"Bug reports on Discourse. Do be sure to search prior to submitting bugs. Include repro steps, and only describe one bug per topic please.","topic_url":"/t/category-definition-for-bug/2","hotness":5.0,"read_restricted":false,"permission":null},{"id":8,"name":"hosting","color":"74CCED","text_color":"FFFFFF","slug":"hosting","topic_count":69,"description":"Topics about hosting Discourse, either on your own servers, in the cloud, or with specific hosting services.","topic_url":"/t/category-definition-for-hosting/2626","hotness":5.0,"read_restricted":false,"permission":null},{"id":6,"name":"support","color":"b99","text_color":"FFFFFF","slug":"support","topic_count":782,"description":"Support on configuring, using, and installing Discourse. Not for software development related topics, but for admins and end users configuring and using Discourse.","topic_url":"/t/category-definition-for-support/389","hotness":5.0,"read_restricted":false,"permission":null},{"id":2,"name":"feature","color":"0E76BD","text_color":"FFFFFF","slug":"feature","topic_count":727,"description":"Discussion about features or potential features of Discourse: how they work, why they work, etc.","topic_url":"/t/category-definition-for-feature/11","hotness":5.0,"read_restricted":false,"permission":null},{"id":13,"name":"blog","color":"ED207B","text_color":"FFFFFF","slug":"blog","topic_count":14,"description":"Discussion topics generated from the official Discourse Blog. These topics are linked from the bottom of each blog entry where the blog comments would normally be.","topic_url":"/t/category-definition-for-blog/5250","hotness":5.0,"read_restricted":false,"permission":null},{"id":12,"name":"discourse hub","color":"b2c79f","text_color":"FFFFFF","slug":"discourse-hub","topic_count":4,"description":"Topics about current or future Discourse Hub functionality at discourse.org including nickname registration, global user pages, and the site directory.","topic_url":"/t/category-definition-for-discourse-hub/3038","hotness":5.0,"read_restricted":false,"permission":null},{"id":11,"name":"login","color":"edb400","text_color":"FFFFFF","slug":"login","topic_count":27,"description":"Topics about logging in to Discourse, using any standard third party provider (Twitter, Facebook, Google), traditional username and password, or with a custom plugin.","topic_url":"/t/category-definition-for-login/2828","hotness":5.0,"read_restricted":false,"permission":null},{"id":3,"name":"meta","color":"aaa","text_color":"FFFFFF","slug":"meta","topic_count":79,"description":"Discussion about meta.discourse.org itself, the organization of this forum about Discourse, how it works, and how we can improve this site.","topic_url":"/t/category-definition-for-meta/24","hotness":5.0,"read_restricted":false,"permission":null},{"id":10,"name":"howto","color":"76923C","text_color":"FFFFFF","slug":"howto","topic_count":58,"description":"Tutorial topics that describe how to set up, configure, or install Discourse using a specific platform or environment. Topics in this category may only be created by trust level 2 and up. ","topic_url":"/t/category-definition-for-howto/2629","hotness":5.0,"read_restricted":false,"permission":null},{"id":14,"name":"marketplace","color":"8C6238","text_color":"FFFFFF","slug":"marketplace","topic_count":24,"description":"About commercial Discourse related stuff: jobs or paid gigs, plugins, themes, hosting, etc.","topic_url":"/t/category-definition-for-marketplace/5425","hotness":5.0,"read_restricted":false,"permission":null},{"id":17,"name":"uncategorized","color":"AB9364","text_color":"FFFFFF","slug":"uncategorized","topic_count":229,"description":"","topic_url":null,"hotness":5.0,"read_restricted":false,"permission":null},{"id":9,"name":"ux","color":"5F497A","text_color":"FFFFFF","slug":"ux","topic_count":184,"description":"Discussion about the user interface of Discourse, how features are presented to the user in the client, including language and UI elements.","topic_url":"/t/category-definition-for-ux/2628","hotness":5.0,"read_restricted":false,"permission":null},{"id":4,"name":"faq","color":"33b","text_color":"FFFFFF","slug":"faq","topic_count":49,"description":"Topics that come up very often when discussing Discourse will eventually be classified into this Frequently Asked Questions category. Should only be added to popular topics.","topic_url":"/t/category-definition-for-faq/25","hotness":5.0,"read_restricted":false,"permission":null}],"post_action_types":[{"name_key":"bookmark","name":"Bookmark","description":"Bookmark this post","long_form":"bookmarked this post","is_flag":false,"icon":null,"id":1,"is_custom_flag":false},{"name_key":"like","name":"Like","description":"Like this post","long_form":"liked this","is_flag":false,"icon":"heart","id":2,"is_custom_flag":false},{"name_key":"off_topic","name":"Off-Topic","description":"This post is radically off-topic in the current conversation, and should probably be moved to a different topic. If this is a topic, perhaps it does not belong here.","long_form":"flagged this as off-topic","is_flag":true,"icon":null,"id":3,"is_custom_flag":false},{"name_key":"inappropriate","name":"Inappropriate","description":"This post contains content that a reasonable person would consider offensive, abusive, or a violation of our community guidelines<\/a>.","long_form":"flagged this as inappropriate","is_flag":true,"icon":null,"id":4,"is_custom_flag":false},{"name_key":"vote","name":"Vote","description":"Vote for this post","long_form":"voted for this post","is_flag":false,"icon":null,"id":5,"is_custom_flag":false},{"name_key":"spam","name":"Spam","description":"This post is an advertisement. It is not useful or relevant to the current conversation, but promotional in nature.","long_form":"flagged this as spam","is_flag":true,"icon":null,"id":8,"is_custom_flag":false},{"name_key":"notify_user","name":"Notify {{username}}","description":"This post contains something I want to talk to this person directly and privately about.","long_form":"notified user","is_flag":true,"icon":null,"id":6,"is_custom_flag":true},{"name_key":"notify_moderators","name":"Notify moderators","description":"This post requires general moderator attention based on the FAQ<\/a>, TOS<\/a>, or for another reason not listed above.","long_form":"notified moderators","is_flag":true,"icon":null,"id":7,"is_custom_flag":true}],"trust_levels":[{"id":0,"name":"new user"},{"id":1,"name":"basic user"},{"id":2,"name":"member"},{"id":3,"name":"regular"},{"id":4,"name":"leader"}],"archetypes":[{"id":"regular","name":"Regular Topic","options":[]}]}); diff --git a/test/javascripts/lib/preload-store-test.js.es6 b/test/javascripts/lib/preload-store-test.js.es6 index 0b296618f..391ac3d2f 100644 --- a/test/javascripts/lib/preload-store-test.js.es6 +++ b/test/javascripts/lib/preload-store-test.js.es6 @@ -1,7 +1,8 @@ import { blank } from 'helpers/qunit-helpers'; +import PreloadStore from 'preload-store'; -module("Discourse.PreloadStore", { - setup: function() { +module("preload-store", { + setup() { PreloadStore.store('bane', 'evil'); } }); diff --git a/test/javascripts/test_helper.js b/test/javascripts/test_helper.js index 82254f17b..2ffb2986c 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 @@ -14,6 +13,7 @@ //= require route-recognizer //= require pretender //= require loader +//= require preload-store //= require locales/i18n //= require locales/en @@ -41,6 +41,7 @@ // //= require jquery.magnific-popup-min.js +window.TestPreloadStore = require('preload-store').default; window.inTestEnv = true; // Stop the message bus so we don't get ajax calls @@ -75,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(); @@ -84,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"); @@ -132,4 +141,5 @@ Object.keys(requirejs.entries).forEach(function(entry) { } }); require('mdtest/mdtest', null, null, true); +resetSite(); diff --git a/vendor/assets/javascripts/xss.min.js b/vendor/assets/javascripts/xss.min.js new file mode 100644 index 000000000..48d788078 --- /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